diff --git a/CRM/DataprocessorSearch/ActivitySearch.php b/CRM/DataprocessorSearch/ActivitySearch.php
new file mode 100644
index 0000000000000000000000000000000000000000..43678f4a414067a2817f73c59e7c3afd9fe91fc4
--- /dev/null
+++ b/CRM/DataprocessorSearch/ActivitySearch.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+use Civi\DataProcessor\Output\OutputInterface;
+
+class CRM_DataprocessorSearch_ActivitySearch implements OutputInterface, CRM_DataprocessorSearch_SearchInterface {
+
+  /**
+   * Return the url to a configuration page.
+   * Or return false when no configuration page exists.
+   *
+   * @return string|false
+   */
+  public function getConfigurationUrl() {
+    return 'civicrm/dataprocessor/form/output/activity_search';
+  }
+
+  /**
+   * Returns the url for the search
+   *
+   * @param \CRM_Dataprocessor_BAO_Output $output
+   * @param $dataProcessor
+   *
+   * @return string
+   */
+  public function getSearchUrl($output, $dataProcessor) {
+    return "civicrm/dataprocessor_activity_search/{$dataProcessor['name']}";
+  }
+
+  /**
+   * @return string
+   */
+  public function getSearchControllerClass() {
+    return 'CRM_DataprocessorSearch_Controller_ActivitySearch';
+  }
+
+}
\ No newline at end of file
diff --git a/CRM/DataprocessorSearch/ContactSearch.php b/CRM/DataprocessorSearch/ContactSearch.php
new file mode 100644
index 0000000000000000000000000000000000000000..f9bd918b760275df7fd0547e605fe09ab936dca6
--- /dev/null
+++ b/CRM/DataprocessorSearch/ContactSearch.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+use Civi\DataProcessor\Output\OutputInterface;
+
+class CRM_DataprocessorSearch_ContactSearch implements OutputInterface, CRM_DataprocessorSearch_SearchInterface {
+
+  /**
+   * Return the url to a configuration page.
+   * Or return false when no configuration page exists.
+   *
+   * @return string|false
+   */
+  public function getConfigurationUrl() {
+    return 'civicrm/dataprocessor/form/output/contact_search';
+  }
+
+  /**
+   * Returns the url for the search
+   *
+   * @param \CRM_Dataprocessor_BAO_Output $output
+   * @param $dataProcessor
+   *
+   * @return string
+   */
+  public function getSearchUrl($output, $dataProcessor) {
+    return "civicrm/dataprocessor_contact_search/{$dataProcessor['name']}";
+  }
+
+  /**
+   * @return string
+   */
+  public function getSearchControllerClass() {
+    return 'CRM_DataprocessorSearch_Controller_ContactSearch';
+  }
+
+}
\ No newline at end of file
diff --git a/CRM/DataprocessorSearch/Controller/ActivitySearch.php b/CRM/DataprocessorSearch/Controller/ActivitySearch.php
new file mode 100644
index 0000000000000000000000000000000000000000..bced16f74e7c679cc578a1780ccb6dd3a85de864
--- /dev/null
+++ b/CRM/DataprocessorSearch/Controller/ActivitySearch.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+/**
+ * This class is used by the Search functionality.
+ *
+ *  - the search controller is used for building/processing multiform
+ *    searches.
+ *
+ * Typically the first form will display the search criteria and it's results
+ *
+ * The second form is used to process search results with the associated actions.
+ */
+class CRM_DataprocessorSearch_Controller_ActivitySearch extends CRM_Core_Controller {
+
+  /**
+   * Class constructor.
+   *
+   * @param string $title
+   * @param bool $modal
+   * @param int|mixed|null $action
+   */
+  public function __construct($title = NULL, $modal = TRUE, $action = CRM_Core_Action::NONE) {
+    parent::__construct($title, $modal);
+
+    $this->set('component_mode', CRM_Contact_BAO_Query::MODE_ACTIVITY);
+    $this->_stateMachine = new CRM_DataprocessorSearch_StateMachine_ActivitySearch($this, $action);
+
+    // create and instantiate the pages
+    $this->addPages($this->_stateMachine, $action);
+
+    // add all the actions
+    $this->addActions();
+  }
+
+  /**
+   * Process the request, overrides the default QFC run method
+   * This routine actually checks if the QFC is modal and if it
+   * is the first invalid page, if so it call the requested action
+   * if not, it calls the display action on the first invalid page
+   * avoids the issue of users hitting the back button and getting
+   * a broken page
+   *
+   * This run is basically a composition of the original run and the
+   * jump action
+   *
+   * @return mixed
+   */
+  public function run() {
+
+    $actionName = $this->getActionName();
+    list($pageName, $action) = $actionName;
+    // Hack to replace to userContext for redirecting after a Task has been completed.
+    // We want the redirect
+    if (!$this->_pages[$pageName] instanceof CRM_DataprocessorSearch_Form_ActivitySearch) {
+      $session = CRM_Core_Session::singleton();
+      $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $this);
+      $urlPath = CRM_Utils_System::getUrlPath();
+      $urlParams = 'force=1';
+      if ($qfKey) {
+        $urlParams .= "&qfKey=$qfKey";
+      }
+      $this->setDestination(CRM_Utils_System::url($urlPath, $urlParams));
+    }
+
+    return parent::run();
+  }
+
+  /**
+   * @return mixed
+   */
+  public function selectorName() {
+    return $this->get('selectorName');
+  }
+
+}
diff --git a/CRM/DataprocessorSearch/Controller/Search.php b/CRM/DataprocessorSearch/Controller/ContactSearch.php
similarity index 93%
rename from CRM/DataprocessorSearch/Controller/Search.php
rename to CRM/DataprocessorSearch/Controller/ContactSearch.php
index 12ef143e329effe6516d6b379c1e6e6781ed36d1..362ed26a16610d577f921f9205e73519d28abea8 100644
--- a/CRM/DataprocessorSearch/Controller/Search.php
+++ b/CRM/DataprocessorSearch/Controller/ContactSearch.php
@@ -14,7 +14,7 @@
  *
  * The second form is used to process search results with the associated actions.
  */
-class CRM_DataprocessorSearch_Controller_Search extends CRM_Core_Controller {
+class CRM_DataprocessorSearch_Controller_ContactSearch extends CRM_Core_Controller {
 
   /**
    * Class constructor.
@@ -26,7 +26,7 @@ class CRM_DataprocessorSearch_Controller_Search extends CRM_Core_Controller {
   public function __construct($title = NULL, $modal = TRUE, $action = CRM_Core_Action::NONE) {
     parent::__construct($title, $modal);
 
-    $this->_stateMachine = new CRM_DataprocessorSearch_StateMachine_Search($this, $action);
+    $this->_stateMachine = new CRM_DataprocessorSearch_StateMachine_ContactSearch($this, $action);
 
     // create and instantiate the pages
     $this->addPages($this->_stateMachine, $action);
@@ -54,7 +54,7 @@ class CRM_DataprocessorSearch_Controller_Search extends CRM_Core_Controller {
     list($pageName, $action) = $actionName;
     // Hack to replace to userContext for redirecting after a Task has been completed.
     // We want the redirect
-    if (!$this->_pages[$pageName] instanceof CRM_DataprocessorSearch_Form_Search) {
+    if (!$this->_pages[$pageName] instanceof CRM_DataprocessorSearch_Form_ContactSearch) {
       $session = CRM_Core_Session::singleton();
       $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $this);
       $urlPath = CRM_Utils_System::getUrlPath();
diff --git a/CRM/DataprocessorSearch/Form/Search.php b/CRM/DataprocessorSearch/Form/AbstractSearch.php
similarity index 77%
rename from CRM/DataprocessorSearch/Form/Search.php
rename to CRM/DataprocessorSearch/Form/AbstractSearch.php
index cbdbe375f9169c53d340b021688f63dece991edc..2fa5c45b5ba853699d0f9e8667aa1261d6dd8547 100644
--- a/CRM/DataprocessorSearch/Form/Search.php
+++ b/CRM/DataprocessorSearch/Form/AbstractSearch.php
@@ -6,24 +6,41 @@
 
 use CRM_Dataprocessor_ExtensionUtil as E;
 
-class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
+abstract class CRM_DataprocessorSearch_Form_AbstractSearch extends CRM_Core_Form_Search {
 
   /**
    * @var \Civi\DataProcessor\ProcessorType\AbstractProcessorType;
    */
   protected $dataProcessor;
 
+  /**
+   * @var int
+   */
   protected $dataProcessorId;
 
+  /**
+   * @var String
+   */
+  protected $title;
+
   /**
    * @var \CRM_Dataprocessor_BAO_Output
    */
   protected $dataProcessorOutput;
 
+  /**
+   * @var int
+   */
   protected $limit;
 
+  /**
+   * @var int
+   */
   protected $pageId;
 
+  /**
+   * @var bool
+   */
   protected $_debug = FALSE;
 
   /**
@@ -31,8 +48,84 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
    */
   protected $sort;
 
+  /**
+   * @var bool
+   */
   protected $formHasRequiredFilters = FALSE;
 
+  /**
+   * Checks whether the output has a valid configuration
+   *
+   * @return bool
+   */
+  abstract protected function isConfigurationValid();
+
+  /**
+   * Return the data processor ID
+   *
+   * @return String
+   */
+  abstract protected function getDataProcessorName();
+
+  /**
+   * Returns the name of the output for this search
+   *
+   * @return string
+   */
+  abstract protected function getOutputName();
+
+  /**
+   * Returns the name of the ID field in the dataset.
+   *
+   * @return string
+   */
+  abstract protected function getIdFieldName();
+
+  /**
+   * @return string
+   */
+  abstract protected function getEntityTable();
+
+  /**
+   * Builds the list of tasks or actions that a searcher can perform on a result set.
+   *
+   * @return array
+   */
+  public function buildTaskList() {
+    return $this->_taskList;
+  }
+
+  /**
+   * Returns whether we want to use the prevnext cache.
+   * @return bool
+   */
+  protected function usePrevNextCache() {
+    return false;
+  }
+
+  /**
+   * Returns whether the ID field is Visible
+   *
+   * @return bool
+   */
+  protected function isIdFieldVisible() {
+    if (isset($this->dataProcessorOutput['configuration']['hide_id_field']) && $this->dataProcessorOutput['configuration']['hide_id_field']) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Return altered rows
+   *
+   * @param array $rows
+   * @param array $ids
+   *
+   */
+  protected function alterRows(&$rows, $ids) {
+
+  }
+
   public function preProcess() {
     parent::preProcess();
 
@@ -62,7 +155,7 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
       $pageId = CRM_Utils_Request::retrieve('crmPID', 'Positive', $this, FALSE, 1);
 
       $this->addColumnHeaders();
-      $this->findRows($pageId, $limit);
+      $this->buildRows($pageId, $limit);
     }
 
   }
@@ -73,27 +166,30 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
    * @throws \Exception
    */
   protected function findDataProcessor() {
-    $dataProcessorId = str_replace('civicrm/dataprocessor_search/', '', CRM_Utils_System::getUrlPath());
-    $sql = "
-      SELECT civicrm_data_processor.id as data_processor_id,  civicrm_data_processor_output.id AS output_id
-      FROM civicrm_data_processor 
-      INNER JOIN civicrm_data_processor_output ON civicrm_data_processor.id = civicrm_data_processor_output.data_processor_id
-      WHERE is_active = 1 AND civicrm_data_processor.id = %1 AND civicrm_data_processor_output.type = 'search'
-    ";
-    $params[1] = array($dataProcessorId, 'Integer');
-    $dao = CRM_Dataprocessor_BAO_DataProcessor::executeQuery($sql, $params, TRUE, 'CRM_Dataprocessor_BAO_DataProcessor');
-    if (!$dao->fetch()) {
-      throw new \Exception('Could not find Data Processor with id '.$dataProcessorId);
-    }
-    $this->dataProcessor = CRM_Dataprocessor_BAO_DataProcessor::getDataProcessorById($dao->data_processor_id);
+    if (!$this->dataProcessorId) {
+      $dataProcessorName = $this->getDataProcessorName();
+      $sql = "
+        SELECT civicrm_data_processor.id as data_processor_id,  civicrm_data_processor_output.id AS output_id
+        FROM civicrm_data_processor 
+        INNER JOIN civicrm_data_processor_output ON civicrm_data_processor.id = civicrm_data_processor_output.data_processor_id
+        WHERE is_active = 1 AND civicrm_data_processor.name = %1 AND civicrm_data_processor_output.type = %2
+      ";
+      $params[1] = [$dataProcessorName, 'String'];
+      $params[2] = [$this->getOutputName(), 'String'];
+      $dao = CRM_Dataprocessor_BAO_DataProcessor::executeQuery($sql, $params, TRUE, 'CRM_Dataprocessor_BAO_DataProcessor');
+      if (!$dao->fetch()) {
+        throw new \Exception('Could not find Data Processor "' . $dataProcessorName.'"');
+      }
+      $this->dataProcessor = CRM_Dataprocessor_BAO_DataProcessor::getDataProcessorById($dao->data_processor_id);
+      $this->dataProcessorId = $dao->data_processor_id;
 
-    $output = CRM_Dataprocessor_BAO_Output::getValues(array('id' => $dao->output_id));
-    $this->dataProcessorOutput = $output[$dao->output_id];
+      $output = CRM_Dataprocessor_BAO_Output::getValues(['id' => $dao->output_id]);
+      $this->dataProcessorOutput = $output[$dao->output_id];
 
-    if (!isset($this->dataProcessorOutput['configuration']['contact_id_field'])) {
-      throw new \Exception('Invalid configuration found for the Search output of the data processor (id = '.$dataProcessorId.')');
+      if (!$this->isConfigurationValid()) {
+        throw new \Exception('Invalid configuration found for the Search output of the data processor "' . $dataProcessorName . '"');
+      }
     }
-
   }
 
   /**
@@ -104,9 +200,9 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
    *
    * @throws \Civi\DataProcessor\DataFlow\InvalidFlowException
    */
-  protected function findRows($pageId, $limit) {
+  protected function buildRows($pageId, $limit) {
     $rows = [];
-    $contact_ids = array();
+    $ids = array();
     $prevnextData = array();
 
     $offset = ($pageId - 1) * $limit;
@@ -131,47 +227,39 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
     $this->pager = new CRM_Utils_Pager($pagerParams);
     $this->assign('pager', $this->pager);
 
-    $contact_id_field = $this->dataProcessorOutput['configuration']['contact_id_field'];
-    $this->assign('contact_id_field', $contact_id_field);
+    $id_field = $this->getIdFieldName();
+    $this->assign('id_field', $id_field);
 
     try {
       while($record = $this->dataProcessor->getDataFlow()->nextRecord()) {
         $row = array();
-        $row['contact_id'] = $record[$contact_id_field]->formattedValue;
-        $row['checkbox'] = CRM_Core_Form::CB_PREFIX.$row['contact_id'];
+        $row['id'] = $record[$id_field]->formattedValue;
+        $row['checkbox'] = CRM_Core_Form::CB_PREFIX.$row['id'];
         $row['record'] = $record;
         $this->addElement('checkbox', $row['checkbox'], NULL, NULL, ['class' => 'select-row']);
 
         $prevnextData[] = array(
-          'entity_id1' => $row['contact_id'],
+          'entity_id1' => $row['id'],
+          'entity_table' => $this->getEntityTable(),
           'data' => $record,
         );
-        $contact_ids[] = $row['contact_id'];
+        $ids[] = $row['id'];
 
-        $rows[$row['contact_id']] = $row;
+        $rows[$row['id']] = $row;
       }
     } catch (\Civi\DataProcessor\DataFlow\EndOfFlowException $e) {
       // Do nothing
     }
 
-    // Add the contact type image
-    if (count($contact_ids)) {
-      $contactDao = CRM_Core_DAO::executeQuery("SELECT id, contact_type, contact_sub_type FROM civicrm_contact WHERE `id` IN (".implode(",", $contact_ids).")");
-      while($contactDao->fetch()) {
-        $rows[$contactDao->id]['contact_type'] = CRM_Contact_BAO_Contact_Utils::getImage($contactDao->contact_sub_type ? $contactDao->contact_sub_type : $contactDao->contact_type,
-          FALSE,
-          $contactDao->id
-        );
-      }
-    }
+    $this->alterRows($rows, $ids);
 
     $this->addElement('checkbox', 'toggleSelect', NULL, NULL, ['class' => 'select-rows']);
-
-    $cacheKey = "civicrm search {$this->controller->_key}";
-    Civi::service('prevnext')->fillWithArray($cacheKey, $prevnextData);
     $this->assign('rows', $rows);
-
     $this->assign('debug_info', $this->dataProcessor->getDataFlow()->getDebugInformation());
+    if ($this->usePrevNextCache()) {
+      $cacheKey = "civicrm search {$this->controller->_key}";
+      CRM_DataprocessorSearch_Utils_PrevNextCache::fillWithArray($cacheKey, $prevnextData);
+    }
   }
 
   /**
@@ -317,12 +405,19 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
    */
   protected function addColumnHeaders() {
     $sortFields = array();
-    $contact_id_field = $this->dataProcessorOutput['configuration']['contact_id_field'];
+    $id_field = $this->getIdFieldName();
+    $idFieldVisible = $this->isIdFieldVisible();
     $columnHeaders = array();
     $sortColumnNr = 1;
     foreach($this->dataProcessor->getDataFlow()->getOutputFieldHandlers() as $outputFieldHandler) {
       $field = $outputFieldHandler->getOutputFieldSpecification();
-      if ($field->alias != $contact_id_field) {
+      $hiddenField = true;
+      if ($field->alias != $id_field) {
+        $hiddenField = false;
+      } elseif ($field->alias == $id_field && $idFieldVisible) {
+        $hiddenField = false;
+      }
+      if (!$hiddenField) {
         $columnHeaders[$field->alias] = $field->title;
         $sortFields[$sortColumnNr] = array(
           'name' => $field->title,
@@ -338,6 +433,9 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
     $this->assign_by_ref('sort', $this->sort);
   }
 
+  /**
+   * Build the criteria form
+   */
   protected function buildCriteriaForm() {
     $count = 1;
     $filterElements = array();
@@ -452,17 +550,17 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
 
     $this->buildCriteriaForm();
 
-    $selectedContactIds = array();
+    $selectedIds = array();
     $qfKeyParam = CRM_Utils_Array::value('qfKey', $this->_formValues);
     // We use ajax to handle selections only if the search results component_mode is set to "contacts"
-    if ($qfKeyParam) {
+    if ($qfKeyParam && $this->usePrevNextCache()) {
       $this->addClass('crm-ajax-selection-form');
-      $qfKeyParam = "civicrm search {$qfKeyParam}";
-      $selectedContactIdsArr = Civi::service('prevnext')->getSelection($qfKeyParam);
-      $selectedContactIds = array_keys($selectedContactIdsArr[$qfKeyParam]);
+        $qfKeyParam = "civicrm search {$qfKeyParam}";
+        $selectedIdsArr = CRM_DataprocessorSearch_Utils_PrevNextCache::getSelection($qfKeyParam);
+        $selectedIds = array_keys($selectedIdsArr[$qfKeyParam]);
     }
 
-    $this->assign_by_ref('selectedContactIds', $selectedContactIds);
+    $this->assign_by_ref('selectedIds', $selectedIds);
     $this->add('hidden', 'context');
     $this->add('hidden', CRM_Utils_Sort::SORT_ID);
   }
@@ -476,17 +574,6 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
     return $defaults;
   }
 
-  /**
-   * Builds the list of tasks or actions that a searcher can perform on a result set.
-   *
-   * @return array
-   */
-  public function buildTaskList() {
-    $taskParams['deletedContacts'] = FALSE;
-    $this->_taskList += CRM_Contact_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission(), $taskParams);
-    return $this->_taskList;
-  }
-
   public function postProcess() {
     $values = $this->exportValues();
     if ($this->_done) {
@@ -502,7 +589,7 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
     ) {
       //reset the cache table for new search
       $cacheKey = "civicrm search {$this->controller->_key}";
-      Civi::service('prevnext')->deleteItem(NULL, $cacheKey);
+      CRM_DataprocessorSearch_Utils_PrevNextCache::deleteItem(NULL, $cacheKey);
     }
 
     if (!empty($_POST)) {
@@ -513,10 +600,20 @@ class CRM_DataprocessorSearch_Form_Search extends CRM_Core_Form_Search {
     if ($buttonName == $this->_actionButtonName) {
       // check actionName and if next, then do not repeat a search, since we are going to the next page
       // hack, make sure we reset the task values
-      $stateMachine = $this->controller->getStateMachine();
-      $formName = $stateMachine->getTaskFormName();
+      $formName = $this->controller->getStateMachine()->getTaskFormName();
       $this->controller->resetPage($formName);
       return;
     }
   }
+
+  /**
+   * Return a descriptive name for the page, used in wizard header
+   *
+   * @return string
+   */
+  public function getTitle() {
+    $this->findDataProcessor();
+    return $this->dataProcessorOutput['configuration']['title'];
+  }
+
 }
\ No newline at end of file
diff --git a/CRM/DataprocessorSearch/Form/ActivitySearch.php b/CRM/DataprocessorSearch/Form/ActivitySearch.php
new file mode 100644
index 0000000000000000000000000000000000000000..456709d9f0cf4a2e58b1ab3c9dc5f783c3a77f3e
--- /dev/null
+++ b/CRM/DataprocessorSearch/Form/ActivitySearch.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+use CRM_Dataprocessor_ExtensionUtil as E;
+
+class CRM_DataprocessorSearch_Form_ActivitySearch extends CRM_DataprocessorSearch_Form_AbstractSearch {
+
+  /**
+   * Return the data processor name
+   *
+   * @return String
+   */
+  protected function getDataProcessorName() {
+    $dataProcessorName = str_replace('civicrm/dataprocessor_activity_search/', '', CRM_Utils_System::getUrlPath());
+    return $dataProcessorName;
+  }
+
+  /**
+   * Returns the name of the output for this search
+   *
+   * @return string
+   */
+  protected function getOutputName() {
+    return 'activity_search';
+  }
+
+  /**
+   * Checks whether the output has a valid configuration
+   *
+   * @return bool
+   */
+  protected function isConfigurationValid() {
+    if (!isset($this->dataProcessorOutput['configuration']['activity_id_field'])) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Returns the name of the ID field in the dataset.
+   *
+   * @return string
+   */
+  protected function getIdFieldName() {
+    return $this->dataProcessorOutput['configuration']['activity_id_field'];
+  }
+
+  /**
+   * @return string
+   */
+  protected function getEntityTable() {
+    return 'civicrm_activity';
+  }
+
+  /**
+   * Builds the list of tasks or actions that a searcher can perform on a result set.
+   *
+   * @return array
+   */
+  public function buildTaskList() {
+    if (!$this->_taskList) {
+      $this->_taskList = CRM_Activity_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission());
+    }
+    return $this->_taskList;
+  }
+
+}
\ No newline at end of file
diff --git a/CRM/DataprocessorSearch/Form/ContactSearch.php b/CRM/DataprocessorSearch/Form/ContactSearch.php
new file mode 100644
index 0000000000000000000000000000000000000000..0a12d2795094a80eeb2e79031a1cac1be51e6f7e
--- /dev/null
+++ b/CRM/DataprocessorSearch/Form/ContactSearch.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+use CRM_Dataprocessor_ExtensionUtil as E;
+
+class CRM_DataprocessorSearch_Form_ContactSearch extends CRM_DataprocessorSearch_Form_AbstractSearch {
+
+  /**
+   * Checks whether the output has a valid configuration
+   *
+   * @return bool
+   */
+  protected function isConfigurationValid() {
+    if (!isset($this->dataProcessorOutput['configuration']['contact_id_field'])) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Return the data processor ID
+   *
+   * @return String
+   */
+  protected function getDataProcessorName() {
+    $dataProcessorName = str_replace('civicrm/dataprocessor_contact_search/', '', CRM_Utils_System::getUrlPath());
+    return $dataProcessorName;
+  }
+
+  /**
+   * Returns the name of the output for this search
+   *
+   * @return string
+   */
+  protected function getOutputName() {
+    return 'contact_search';
+  }
+
+  /**
+   * Returns the name of the ID field in the dataset.
+   *
+   * @return string
+   */
+  protected function getIdFieldName() {
+    return $this->dataProcessorOutput['configuration']['contact_id_field'];
+  }
+
+  /**
+   * @return string
+   */
+  protected function getEntityTable() {
+    return 'civicrm_contact';
+  }
+
+  /**
+   * Returns whether we want to use the prevnext cache.
+   * @return bool
+   */
+  protected function usePrevNextCache() {
+    return true;
+  }
+
+  /**
+   * Return altered rows
+   *
+   * @param array $rows
+   * @param array $ids
+   *
+   */
+  protected function alterRows(&$rows, $ids) {
+    // Add the contact type image
+    if (count($ids)) {
+      $contactDao = CRM_Core_DAO::executeQuery("SELECT id, contact_type, contact_sub_type FROM civicrm_contact WHERE `id` IN (".implode(",", $ids).")");
+      while($contactDao->fetch()) {
+        $rows[$contactDao->id]['contact_type'] = CRM_Contact_BAO_Contact_Utils::getImage($contactDao->contact_sub_type ? $contactDao->contact_sub_type : $contactDao->contact_type,
+          FALSE,
+          $contactDao->id
+        );
+      }
+    }
+  }
+
+  /**
+   * Builds the list of tasks or actions that a searcher can perform on a result set.
+   *
+   * @return array
+   */
+  public function buildTaskList() {
+    if (!$this->_taskList) {
+      $taskParams['deletedContacts'] = FALSE;
+      $this->_taskList = CRM_Contact_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission(), $taskParams);
+    }
+    return $this->_taskList;
+  }
+
+}
\ No newline at end of file
diff --git a/CRM/DataprocessorSearch/Form/OutputConfiguration/ActivitySearch.php b/CRM/DataprocessorSearch/Form/OutputConfiguration/ActivitySearch.php
new file mode 100644
index 0000000000000000000000000000000000000000..ad27c8421b79702859ec6c30368d45878f7bebaa
--- /dev/null
+++ b/CRM/DataprocessorSearch/Form/OutputConfiguration/ActivitySearch.php
@@ -0,0 +1,108 @@
+<?php
+
+use CRM_Dataprocessor_ExtensionUtil as E;
+
+/**
+ * Form controller class
+ *
+ * @see https://wiki.civicrm.org/confluence/display/CRMDOC/QuickForm+Reference
+ */
+class CRM_DataprocessorSearch_Form_OutputConfiguration_ActivitySearch extends CRM_Dataprocessor_Form_Output_AbstractOutputForm {
+
+  /**
+   * @var CRM_DataprocessorSearch_Utils_Navigation
+   */
+  protected $navigation;
+
+  public function preProcess() {
+    parent::preProcess();
+    $this->navigation = CRM_DataprocessorSearch_Utils_Navigation::singleton();
+  }
+
+  public function buildQuickForm() {
+    parent::buildQuickForm();
+
+    $dataProcessor = CRM_Dataprocessor_BAO_DataProcessor::getDataProcessorById($this->dataProcessorId);
+    $fields = array();
+    foreach($dataProcessor->getDataFlow()->getOutputFieldHandlers() as $outputFieldHandler) {
+      $field = $outputFieldHandler->getOutputFieldSpecification();
+      $fields[$field->alias] = $field->title;
+    }
+
+    $this->add('text', 'title', E::ts('Title'), true);
+
+    $this->add('select','permission', E::ts('Permission'), CRM_Core_Permission::basicPermissions(), true, array(
+      'style' => 'min-width:250px',
+      'class' => 'crm-select2 huge',
+      'placeholder' => E::ts('- select -'),
+    ));
+    $this->add('select', 'activity_id_field', E::ts('Activity ID field'), $fields, true, array(
+      'style' => 'min-width:250px',
+      'class' => 'crm-select2 huge',
+      'placeholder' => E::ts('- select -'),
+    ));
+    $this->add('select', 'hide_id_field', E::ts('Show Activity ID field'), array(0=>'Activity ID is Visible', 1=> 'Activity ID is hidden'));
+
+    // navigation field
+    $navigationOptions = $this->navigation->getNavigationOptions();
+    if (isset($this->output['configuration']['navigation_id'])) {
+      $navigationPath = $this->navigation->getNavigationPathById($this->output['configuration']['navigation_id']);
+      unset($navigationOptions[$navigationPath]);
+    }
+    $this->add('select', 'navigation_parent_path', ts('Parent Menu'), array('' => ts('- select -')) + $navigationOptions, true);
+  }
+
+  function setDefaultValues() {
+    $dataProcessors = CRM_Dataprocessor_BAO_DataProcessor::getValues(array('id' => $this->dataProcessorId));
+    $dataProcessor = $dataProcessors[$this->dataProcessorId];
+
+    $defaults = parent::setDefaultValues();
+    if ($this->output) {
+      if (isset($this->output['permission'])) {
+        $defaults['permission'] = $this->output['permission'];
+      }
+      if (isset($this->output['configuration']) && is_array($this->output['configuration'])) {
+        if (isset($this->output['configuration']['activity_id_field'])) {
+          $defaults['activity_id_field'] = $this->output['configuration']['activity_id_field'];
+        }
+        if (isset($this->output['configuration']['navigation_id'])) {
+          $defaults['navigation_parent_path'] = $this->navigation->getNavigationParentPathById($this->output['configuration']['navigation_id']);
+        }
+        if (isset($this->output['configuration']['title'])) {
+          $defaults['title'] = $this->output['configuration']['title'];
+        }
+        if (isset($this->output['configuration']['hide_id_field'])) {
+          $defaults['hide_id_field'] = $this->output['configuration']['hide_id_field'];
+        }
+      }
+    }
+    if (!isset($defaults['permission'])) {
+      $defaults['permission'] = 'access CiviCRM';
+    }
+    if (empty($defaults['title'])) {
+      $defaults['title'] = $dataProcessor['title'];
+    }
+
+    return $defaults;
+  }
+
+  public function postProcess() {
+    $values = $this->exportValues();
+
+    $session = CRM_Core_Session::singleton();
+    $redirectUrl = $session->readUserContext();
+
+    $params['id'] = $this->id;
+    $params['permission'] = $values['permission'];
+    $params['configuration']['title'] = $values['title'];
+    $params['configuration']['activity_id_field'] = $values['activity_id_field'];
+    $params['configuration']['navigation_parent_path'] = $values['navigation_parent_path'];
+    $params['configuration']['hide_id_field'] = $values['hide_id_field'];
+
+    CRM_Dataprocessor_BAO_Output::add($params);
+
+    CRM_Utils_System::redirect($redirectUrl);
+    parent::postProcess();
+  }
+
+}
\ No newline at end of file
diff --git a/CRM/DataprocessorSearch/Form/OutputConfiguration.php b/CRM/DataprocessorSearch/Form/OutputConfiguration/ContactSearch.php
similarity index 87%
rename from CRM/DataprocessorSearch/Form/OutputConfiguration.php
rename to CRM/DataprocessorSearch/Form/OutputConfiguration/ContactSearch.php
index a60686c96bc363477516f1eccad921bdb46d88c9..8b7377d13d012dcaaee13acf527c46feb07ab3da 100644
--- a/CRM/DataprocessorSearch/Form/OutputConfiguration.php
+++ b/CRM/DataprocessorSearch/Form/OutputConfiguration/ContactSearch.php
@@ -7,7 +7,7 @@ use CRM_Dataprocessor_ExtensionUtil as E;
  *
  * @see https://wiki.civicrm.org/confluence/display/CRMDOC/QuickForm+Reference
  */
-class CRM_DataprocessorSearch_Form_OutputConfiguration extends CRM_Dataprocessor_Form_Output_AbstractOutputForm {
+class CRM_DataprocessorSearch_Form_OutputConfiguration_ContactSearch extends CRM_Dataprocessor_Form_Output_AbstractOutputForm {
 
   /**
    * @var CRM_DataprocessorSearch_Utils_Navigation
@@ -41,6 +41,7 @@ class CRM_DataprocessorSearch_Form_OutputConfiguration extends CRM_Dataprocessor
       'class' => 'crm-select2 huge',
       'placeholder' => E::ts('- select -'),
     ));
+    $this->add('select', 'hide_id_field', E::ts('Show Contact ID field'), array(0=>'Contact ID is Visible', 1=> 'Contact ID is hidden'));
 
     // navigation field
     $navigationOptions = $this->navigation->getNavigationOptions();
@@ -70,6 +71,9 @@ class CRM_DataprocessorSearch_Form_OutputConfiguration extends CRM_Dataprocessor
         if (isset($this->output['configuration']['title'])) {
           $defaults['title'] = $this->output['configuration']['title'];
         }
+        if (isset($this->output['configuration']['hide_id_field'])) {
+          $defaults['hide_id_field'] = $this->output['configuration']['hide_id_field'];
+        }
       }
     }
     if (!isset($defaults['permission'])) {
@@ -93,6 +97,7 @@ class CRM_DataprocessorSearch_Form_OutputConfiguration extends CRM_Dataprocessor
     $params['configuration']['title'] = $values['title'];
     $params['configuration']['contact_id_field'] = $values['contact_id_field'];
     $params['configuration']['navigation_parent_path'] = $values['navigation_parent_path'];
+    $params['configuration']['hide_id_field'] = $values['hide_id_field'];
 
     CRM_Dataprocessor_BAO_Output::add($params);
 
diff --git a/CRM/DataprocessorSearch/Search.php b/CRM/DataprocessorSearch/Search.php
index b79164aea1ba47486a7612b5584061e2eeb9fd84..a07353989e7ecfba995d36069d799c4053893ae9 100644
--- a/CRM/DataprocessorSearch/Search.php
+++ b/CRM/DataprocessorSearch/Search.php
@@ -6,45 +6,45 @@
 
 use Civi\DataProcessor\Output\OutputInterface;
 
-class CRM_DataprocessorSearch_Search implements OutputInterface {
+class CRM_DataprocessorSearch_Search {
 
   private static $rebuildMenu = false;
 
-  /**
-   * Return the url to a configuration page.
-   * Or return false when no configuration page exists.
-   *
-   * @return string|false
-   */
-  public function getConfigurationUrl() {
-    return 'civicrm/dataprocessor/form/output/search';
-  }
-
   /**
    * Delegation of the alter menu hook. Add the search outputs to the menu system.
    *
    * @param $items
    */
   public static function alterMenu(&$items) {
+    $factory = dataprocessor_get_factory();
+
     $sql = "
-    SELECT o.permission, p.id, p.title, o.configuration 
+    SELECT o.permission, p.id, p.title, o.configuration, o.type, o.id as output_id 
     FROM civicrm_data_processor_output o 
     INNER JOIN civicrm_data_processor p ON o.data_processor_id = p.id 
-    WHERE p.is_active = 1 AND o.type = 'search'";
+    WHERE p.is_active = 1";
     $dao = CRM_Core_DAO::executeQuery($sql);
     while ($dao->fetch()) {
-      $url = 'civicrm/dataprocessor_search/'.$dao->id;
-      $configuration = json_decode($dao->configuration, TRUE);
-      $title = $dao->title;
-      if (isset($configuration['title'])) {
-        $title = $configuration['title'];
+      $outputClass = $factory->getOutputByName($dao->type);
+      if ($outputClass instanceof CRM_DataprocessorSearch_SearchInterface) {
+        $outputs = CRM_Dataprocessor_BAO_Output::getValues(['id' => $dao->output_id]);
+        $output = $outputs[$dao->output_id];
+        $dataprocessors = CRM_Dataprocessor_BAO_DataProcessor::getValues(['id' => $dao->id]);
+        $dataprocessor = $dataprocessors[$dao->id];
+        $url = $outputClass->getSearchUrl($output, $dataprocessor);
+
+        $configuration = json_decode($dao->configuration, TRUE);
+        $title = $dao->title;
+        if (isset($configuration['title'])) {
+          $title = $configuration['title'];
+        }
+        $item = [
+          'title' => $title,
+          'page_callback' => $outputClass->getSearchControllerClass(),
+          'access_arguments' => [$dao->permission],
+        ];
+        $items[$url] = $item;
       }
-      $item = array(
-        'title' => $title,
-        'page_callback' => 'CRM_DataprocessorSearch_Controller_Search',
-        'access_arguments' => array($dao->permission),
-      );
-      $items[$url] = $item;
     }
   }
 
@@ -96,7 +96,7 @@ class CRM_DataprocessorSearch_Search implements OutputInterface {
             $navigationParams['parent_id'] = !empty($navigationDefaults['parent_id']) ? $navigationDefaults['parent_id'] : NULL;
           }
         }
-        self::$rebuildMenu = self::newNavigationItem($params, $dataProcessor, $navigationParams);
+        self::$rebuildMenu = self::newNavigationItem($params, $dataProcessor, $navigationParams, $output);
       }
     }
     elseif ($op == 'create' && isset($params['configuration']['navigation_parent_path'])) {
@@ -151,7 +151,20 @@ class CRM_DataprocessorSearch_Search implements OutputInterface {
    *
    * @return bool
    */
-  private static function newNavigationItem(&$params, $dataProcessor, $navigationParams=array()) {
+  private static function newNavigationItem(&$params, $dataProcessor, $navigationParams=array(), $output=null) {
+    $url = "";
+    $factory = dataprocessor_get_factory();
+    $outputClass = FALSE;
+    if (isset($params['type'])) {
+      $outputClass = $factory->getOutputByName($params['type']);
+    } elseif(isset($output['type'])) {
+      $outputClass = $factory->getOutputByName($output['type']);
+    }
+
+    if ($outputClass && $outputClass instanceof CRM_DataprocessorSearch_SearchInterface) {
+      $url = $outputClass->getSearchUrl($params, $dataProcessor);
+    }
+
     $navigation = CRM_DataprocessorSearch_Utils_Navigation::singleton();
     $navigationParams['domain_id'] = CRM_Core_Config::domainID();
     $navigationParams['permission'] = array();
@@ -167,7 +180,7 @@ class CRM_DataprocessorSearch_Search implements OutputInterface {
 
     unset($params['configuration']['navigation_parent_path']);
 
-    $navigationParams['url'] = "civicrm/dataprocessor_search/{$dataProcessor['id']}?reset=1";
+    $navigationParams['url'] = $url.'?reset=1';
     $navigation = CRM_Core_BAO_Navigation::add($navigationParams);
     CRM_Core_BAO_Navigation::resetNavigation();
 
diff --git a/CRM/DataprocessorSearch/SearchInterface.php b/CRM/DataprocessorSearch/SearchInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..5d35808ad44f6ef37278604f485216a2633a5dfa
--- /dev/null
+++ b/CRM/DataprocessorSearch/SearchInterface.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+interface CRM_DataprocessorSearch_SearchInterface {
+
+  /**
+   * Returns the url for the search
+   *
+   * @param \CRM_Dataprocessor_BAO_Output $output
+   * @param $dataprocessor
+   *
+   * @return string
+   */
+  public function getSearchUrl($output, $dataprocessor);
+
+  /**
+   * @return string
+   */
+  public function getSearchControllerClass();
+
+}
\ No newline at end of file
diff --git a/CRM/DataprocessorSearch/StateMachine/ActivitySearch.php b/CRM/DataprocessorSearch/StateMachine/ActivitySearch.php
new file mode 100644
index 0000000000000000000000000000000000000000..f0cfb9c4132dadc4cfb9fb493b53c32561ae2cfa
--- /dev/null
+++ b/CRM/DataprocessorSearch/StateMachine/ActivitySearch.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+class CRM_DataprocessorSearch_StateMachine_ActivitySearch extends CRM_Core_StateMachine {
+
+  /**
+   * The task that the wizard is currently processing.
+   *
+   * @var string
+   */
+  protected $_task;
+
+  /**
+   * Class constructor.
+   *
+   * @param object $controller
+   * @param \const|int $action
+   */
+  public function __construct($controller, $action = CRM_Core_Action::NONE) {
+    parent::__construct($controller, $action);
+
+    $this->_pages = array();
+    $this->_pages['Search'] = array(
+      'className' => 'CRM_DataprocessorSearch_Form_ActivitySearch',
+    );
+    list($task, $result) = $this->taskName($controller, 'Search');
+    $this->_task = $task;
+
+    if (is_array($task)) {
+      foreach ($task as $t) {
+        $this->_pages[$t] = NULL;
+      }
+    }
+    else {
+      $this->_pages[$task] = NULL;
+    }
+
+    $this->addSequentialPages($this->_pages, $action);
+  }
+
+  /**
+   * Determine the form name based on the action. This allows us
+   * to avoid using  conditional state machine, much more efficient
+   * and simpler
+   *
+   * @param CRM_Core_Controller $controller
+   *   The controller object.
+   *
+   * @param string $formName
+   *
+   * @return array
+   *   the name of the form that will handle the task
+   */
+  public function taskName($controller, $formName = 'Search') {
+    // total hack, check POST vars and then session to determine stuff
+    $value = CRM_Utils_Array::value('task', $_POST);
+    if (!isset($value)) {
+      $value = $this->_controller->get('task');
+    }
+    $this->_controller->set('task', $value);
+    return CRM_Activity_Task::getTask($value);
+  }
+
+  /**
+   * Return the form name of the task.
+   *
+   * @return string
+   */
+  public function getTaskFormName() {
+    var_dump($this->_task);
+    return CRM_Utils_String::getClassName($this->_task);
+  }
+
+  /**
+   * Should the controller reset the session.
+   * In some cases, specifically search we want to remember
+   * state across various actions and want to go back to the
+   * beginning from the final state, but retain the same session
+   * values
+   *
+   * @return bool
+   */
+  public function shouldReset() {
+    return FALSE;
+  }
+
+}
diff --git a/CRM/DataprocessorSearch/StateMachine/Search.php b/CRM/DataprocessorSearch/StateMachine/ContactSearch.php
similarity index 93%
rename from CRM/DataprocessorSearch/StateMachine/Search.php
rename to CRM/DataprocessorSearch/StateMachine/ContactSearch.php
index 657e517b54fe0334e6b0c6dcc0193de9486c8957..4b7d7127caef69cf3630a4ee17f7ded756d06592 100644
--- a/CRM/DataprocessorSearch/StateMachine/Search.php
+++ b/CRM/DataprocessorSearch/StateMachine/ContactSearch.php
@@ -4,7 +4,7 @@
  * @license AGPL-3.0
  */
 
-class CRM_DataprocessorSearch_StateMachine_Search extends CRM_Core_StateMachine {
+class CRM_DataprocessorSearch_StateMachine_ContactSearch extends CRM_Core_StateMachine {
 
   /**
    * The task that the wizard is currently processing
@@ -24,7 +24,7 @@ class CRM_DataprocessorSearch_StateMachine_Search extends CRM_Core_StateMachine
 
     $this->_pages = array();
     $this->_pages['Basic'] = array(
-      'className' => 'CRM_DataprocessorSearch_Form_Search',
+      'className' => 'CRM_DataprocessorSearch_Form_ContactSearch',
     );
     list($task, $result) = $this->taskName($controller);
     $this->_task = $task;
diff --git a/CRM/DataprocessorSearch/Utils/PrevNextCache.php b/CRM/DataprocessorSearch/Utils/PrevNextCache.php
new file mode 100644
index 0000000000000000000000000000000000000000..5cf0bee48c6122965bb57517dca896d4c8048db4
--- /dev/null
+++ b/CRM/DataprocessorSearch/Utils/PrevNextCache.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+class CRM_DataprocessorSearch_Utils_PrevNextCache {
+
+  /**
+   * Delete an item from the prevnext cache table based on the entity.
+   *
+   * @param int $id
+   * @param string $cacheKey
+   */
+  public function deleteItem($id = NULL, $cacheKey = NULL) {
+    if (Civi::container()->has('prevnext')) {
+      return Civi::service('prevnext')->deleteItem($id, $cacheKey);
+    } else {
+      // Backwards compatibility
+      $sql = "DELETE FROM civicrm_prevnext_cache WHERE (1)";
+      $params = [];
+
+      if (is_numeric($id)) {
+        $sql .= " AND ( entity_id1 = %2 OR entity_id2 = %2 )";
+        $params[2] = [$id, 'Integer'];
+      }
+
+      if (isset($cacheKey)) {
+        $sql .= " AND cacheKey = %3";
+        $params[3] = [$cacheKey, 'String'];
+      }
+      CRM_Core_DAO::executeQuery($sql, $params);
+    }
+  }
+
+  public static function fillWithArray($cacheKey, $rows) {
+    if (Civi::container()->has('prevnext')) {
+      return Civi::service('prevnext')->fillWithArray($cacheKey, $rows);
+    } else {
+      // Backwards compatibility
+      if (empty($rows)) {
+        return;
+      }
+
+      $insert = CRM_Utils_SQL_Insert::into('civicrm_prevnext_cache')
+        ->columns([
+          'entity_table',
+          'entity_id1',
+          'entity_id2',
+          'cacheKey',
+          'data'
+        ]);
+
+      foreach ($rows as &$row) {
+        $insert->row($row + ['cacheKey' => $cacheKey, 'entity_id2' => $row['entity_id1']]);
+      }
+
+      CRM_Core_DAO::executeQuery($insert->toSQL());
+      return TRUE;
+    }
+    return;
+  }
+
+  /**
+   * Get the selections.
+   *
+   * @param string $cacheKey
+   *   Cache key.
+   * @param string $action
+   *   One of the following:
+   *   - 'get' - get only selection records
+   *   - 'getall' - get all the records of the specified cache key
+   *
+   * @return array|NULL
+   */
+  public function getSelection($cacheKey, $action = 'get') {
+    if (Civi::container()->has('prevnext')) {
+      Civi::service('prevnext')->getSelection($cacheKey, $action);
+    } else {
+      // Backwards compatibility
+      if (!$cacheKey) {
+        return NULL;
+      }
+      $params = [];
+
+      if ($cacheKey && ($action == 'get' || $action == 'getall')) {
+        $actionGet = ($action == "get") ? " AND is_selected = 1 " : "";
+        $sql = "
+  SELECT entity_id1 FROM civicrm_prevnext_cache
+  WHERE cacheKey = %1
+        $actionGet
+  ORDER BY id
+  ";
+        $params[1] = [$cacheKey, 'String'];
+
+        $contactIds = [$cacheKey => []];
+        $cIdDao = CRM_Core_DAO::executeQuery($sql, $params);
+        while ($cIdDao->fetch()) {
+          $contactIds[$cacheKey][$cIdDao->entity_id1] = 1;
+        }
+        return $contactIds;
+      }
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/Civi/DataProcessor/Factory.php b/Civi/DataProcessor/Factory.php
index f7ee609085ac668fca704b5861e08179b090b428..c8667bcf2e7fc19738451c2293f1e2fc989b0873 100644
--- a/Civi/DataProcessor/Factory.php
+++ b/Civi/DataProcessor/Factory.php
@@ -118,7 +118,8 @@ class Factory {
     $this->addDataSource('membership_status', 'Civi\DataProcessor\Source\Member\MembershipStatusSource', E::ts('Membership Status'));
     $this->addDataSource('csv', 'Civi\DataProcessor\Source\CSV', E::ts('CSV File'));
     $this->addOutput('api', 'Civi\DataProcessor\Output\Api', E::ts('API'));
-    $this->addOutput('search', 'CRM_DataprocessorSearch_Search', E::ts('Search'));
+    $this->addOutput('contact_search', 'CRM_DataprocessorSearch_ContactSearch', E::ts('Contact Search'));
+    $this->addOutput('activity_search', 'CRM_DataprocessorSearch_ActivitySearch', E::ts('Activity Search'));
     $this->addFilter('simple_sql_filter', 'Civi\DataProcessor\FilterHandler\SimpleSqlFilter', E::ts('Simple Filter'));
     $this->addjoinType('simple_join', 'Civi\DataProcessor\DataFlow\MultipleDataFlows\SimpleJoin', E::ts('Simple Join'));
     $this->addjoinType('simple_non_required_join', 'Civi\DataProcessor\DataFlow\MultipleDataFlows\SimpleNonRequiredJoin', E::ts('Simple  (but not required) Join'));
diff --git a/templates/CRM/DataprocessorSearch/Form/ActivitySearch.tpl b/templates/CRM/DataprocessorSearch/Form/ActivitySearch.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..004d808ee0824ff8682c71446896d5897a7f3670
--- /dev/null
+++ b/templates/CRM/DataprocessorSearch/Form/ActivitySearch.tpl
@@ -0,0 +1 @@
+{include file="CRM/DataprocessorSearch/Form/Search.tpl"}
\ No newline at end of file
diff --git a/templates/CRM/DataprocessorSearch/Form/ContactSearch.tpl b/templates/CRM/DataprocessorSearch/Form/ContactSearch.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..bb0b8981f637a798521c4ff5e3b2c3d6dd6d899b
--- /dev/null
+++ b/templates/CRM/DataprocessorSearch/Form/ContactSearch.tpl
@@ -0,0 +1,68 @@
+{include file="CRM/DataprocessorSearch/Form/CriteriaForm.tpl"}
+
+{if $debug && isset($debug_info.query)}
+    <div class="crm-block crm-form-block">
+        <h3>Executes queries</h3>
+        {foreach from=$debug_info.query item=query}
+            <pre id="debug_info" class="linenums prettyprint prettyprinted" style="font-size: 11px; padding: 1em; border: 1px solid lightgrey; margin-top: 1em; overflow: auto;">{strip}
+                {$query|replace:"SELECT":"SELECT\r\n "|replace:"FROM":"\r\nFROM"|replace:"INNER JOIN":"\r\nINNER JOIN"|replace:"LEFT JOIN":"\r\nLEFT JOIN"|replace:"WHERE":"\r\nWHERE"|replace:"ORDER BY":"\r\nORDER BY"|replace:"LIMIT":"\r\nLIMIT"|replace:"AND":"\r\n  AND"|replace:"`, `":"`,\r\n  `"}
+            {/strip}</pre>
+        {/foreach}
+    </div>
+{/if}
+
+{if (isset($rows) && !empty($rows))}
+    <div class="crm-content-block">
+        <div class="crm-results-block">
+            {* This section handles form elements for action task select and submit *}
+            <div class="crm-search-tasks">
+                {include file="CRM/common/searchResultTasks.tpl"}
+            </div>
+
+            {include file="CRM/common/pager.tpl" location="top"}
+
+            <div class="crm-search-results">
+                <a href="#" class="crm-selection-reset crm-hover-button"><i class="crm-i fa-times-circle-o"></i> {ts}Reset all selections{/ts}</a>
+                <table class="selector row-highlight">
+                    <thead class="sticky">
+                    <tr>
+                        <th scope="col" title="Select Rows">{$form.toggleSelect.html}</th>
+                        <th scope="col"></th>
+                        {foreach from=$columnHeaders key=headerName item=headerTitle}
+                            <th scope="col">
+                                {$sort->_response.$headerName.link}
+                            </th>
+                        {/foreach}
+                        <th scope="col"></th>
+                    </tr></thead>
+
+
+                    {foreach from=$rows item=row}
+                        <tr id='rowid{$row.id}' class="{cycle values="odd-row,even-row"}">
+                            {assign var=cbName value=$row.checkbox}
+                            {assign var=id value=$row.id}
+                            {assign var=record value=$row.record}
+                            <td>{$form.$cbName.html}</td>
+                            <td>{$row.contact_type}</td>
+                            {foreach from=$columnHeaders key=headerName item=headerTitle}
+                                {assign var=columnValue value=$record.$headerName}
+                                <td>{$columnValue->formattedValue}</td>
+                            {/foreach}
+
+                            <td>
+                                <a href="{crmURL p='civicrm/contact/view' q="reset=1&cid=`$contact_id`"}">
+                                    {ts}View contact{/ts}
+                                </a>
+                            </td>
+                        </tr>
+                    {/foreach}
+
+                </table>
+            </div>
+
+            {include file="CRM/common/pager.tpl" location="bottom"}
+        </div>
+    </div>
+
+    {include file="CRM/DataprocessorSearch/Form/ResultsJavascript.tpl"}
+{/if}
\ No newline at end of file
diff --git a/templates/CRM/DataprocessorSearch/Form/CriteriaForm.tpl b/templates/CRM/DataprocessorSearch/Form/CriteriaForm.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..4747db016182a35e51f6482a6e378c445498e697
--- /dev/null
+++ b/templates/CRM/DataprocessorSearch/Form/CriteriaForm.tpl
@@ -0,0 +1,73 @@
+<div class="crm-form-block crm-search-form-block">
+    <div class="crm-accordion-wrapper crm-advanced_search_form-accordion {if (!empty($rows))}collapsed{/if}">
+        <div class="crm-accordion-header crm-master-accordion-header">
+            {ts}Edit Search Criteria{/ts}
+        </div>
+        <!-- /.crm-accordion-header -->
+        <div class="crm-accordion-body">
+            <div id="searchForm" class="form-item">
+                <table>
+                    <tr>
+                        <th>{ts}Name{/ts}</th>
+                        <th>{ts}Operator{/ts}</th>
+                        <th>{ts}Value{/ts}</th>
+                    </tr>
+                    {foreach from=$filters key=filterName item=filter}
+                        {assign var=fieldOp     value=$filterName|cat:"_op"}
+                        {assign var=filterVal   value=$filterName|cat:"_value"}
+                        {assign var=filterMin   value=$filterName|cat:"_min"}
+                        {assign var=filterMax   value=$filterName|cat:"_max"}
+                        {if $filter.type == 'Date'}
+                            <tr>
+                                <td>{$filter.title}</td>
+                                {include file="CRM/DataprocessorSearch/Form/DateRange.tpl" fieldName=$filterName from='_from' to='_to'}
+                            </tr>
+                        {elseif $form.$fieldOp.html}
+                            <tr>
+                                <td class="label">{$filter.title}</td>
+                                <td>{$form.$fieldOp.html}</td>
+                                <td>
+                                    <span id="{$filterVal}_cell">{$form.$filterVal.label}&nbsp;{$form.$filterVal.html}</span>
+                                    <span id="{$filterMin}_max_cell">{$form.$filterMin.label}&nbsp;{$form.$filterMin.html}&nbsp;&nbsp;{$form.$filterMax.label}&nbsp;{$form.$filterMax.html}</span>
+                                </td>
+                            </tr>
+                        {/if}
+                    {/foreach}
+                </table>
+                <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="botton"}</div>
+            </div>
+        </div>
+    </div>
+</div>
+
+{literal}
+<script type="text/javascript">
+    {/literal}
+    {foreach from=$filters item=filter key=filterName}
+            {literal}var val = "dnc";{/literal}
+    {assign var=fieldOp     value=$filterName|cat:"_op"}
+    {if !($field.operatorType & 4) && !$field.no_display && $form.$fieldOp.html}
+            {literal}var val = document.getElementById("{/literal}{$fieldOp}{literal}").value;{/literal}
+    {/if}
+    {literal}showHideMaxMinVal( "{/literal}{$filterName}{literal}", val );{/literal}
+    {/foreach}
+
+    {literal}
+    function showHideMaxMinVal( field, val ) {
+      var fldVal    = field + "_value_cell";
+      var fldMinMax = field + "_min_max_cell";
+      if ( val == "bw" || val == "nbw" ) {
+        cj('#' + fldVal ).hide();
+        cj('#' + fldMinMax ).show();
+      } else if (val =="nll" || val == "nnll") {
+        cj('#' + fldVal).hide() ;
+        cj('#' + field + '_value').val('');
+        cj('#' + fldMinMax ).hide();
+      } else {
+        cj('#' + fldVal ).show();
+        cj('#' + fldMinMax ).hide();
+      }
+    }
+
+</script>
+{/literal}
diff --git a/templates/CRM/DataprocessorSearch/Form/OutputConfiguration/ActivitySearch.tpl b/templates/CRM/DataprocessorSearch/Form/OutputConfiguration/ActivitySearch.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..435f8e0b5c497924ad69bd70231f9950eeae2013
--- /dev/null
+++ b/templates/CRM/DataprocessorSearch/Form/OutputConfiguration/ActivitySearch.tpl
@@ -0,0 +1,38 @@
+{crmScope extensionKey='dataprocessor'}
+    <div class="crm-submit-buttons">
+        {include file="CRM/common/formButtons.tpl" location="top"}
+    </div>
+
+    <h3>{ts}Output configuration{/ts}</h3>
+    <div class="crm-block crm-form-block crm-data-processor_output-block">
+        <div class="crm-section">
+            <div class="label">{$form.title.label}</div>
+            <div class="content">{$form.title.html}</div>
+            <div class="clear"></div>
+        </div>
+        <div class="crm-section">
+            <div class="label">{$form.navigation_parent_path.label}</div>
+            <div class="content">{$form.navigation_parent_path.html}</div>
+            <div class="clear"></div>
+        </div>
+        <div class="crm-section">
+            <div class="label">{$form.permission.label}</div>
+            <div class="content">{$form.permission.html}</div>
+            <div class="clear"></div>
+        </div>
+        <div class="crm-section">
+            <div class="label">{$form.activity_id_field.label}</div>
+            <div class="content">{$form.activity_id_field.html}</div>
+            <div class="clear"></div>
+        </div>
+        <div class="crm-section">
+            <div class="label">{$form.hide_id_field.label}</div>
+            <div class="content">{$form.hide_id_field.html}</div>
+            <div class="clear"></div>
+        </div>
+    </div>
+
+    <div class="crm-submit-buttons">
+        {include file="CRM/common/formButtons.tpl" location="bottom"}
+    </div>
+{/crmScope}
\ No newline at end of file
diff --git a/templates/CRM/DataprocessorSearch/Form/OutputConfiguration.tpl b/templates/CRM/DataprocessorSearch/Form/OutputConfiguration/ContactSearch.tpl
similarity index 85%
rename from templates/CRM/DataprocessorSearch/Form/OutputConfiguration.tpl
rename to templates/CRM/DataprocessorSearch/Form/OutputConfiguration/ContactSearch.tpl
index 99a80c600764715efae7a8761db3671a100b4594..855b29286d4d79a6fb32b4dbeeedd6f1e2ea9eb0 100644
--- a/templates/CRM/DataprocessorSearch/Form/OutputConfiguration.tpl
+++ b/templates/CRM/DataprocessorSearch/Form/OutputConfiguration/ContactSearch.tpl
@@ -25,6 +25,11 @@
             <div class="content">{$form.contact_id_field.html}</div>
             <div class="clear"></div>
         </div>
+        <div class="crm-section">
+            <div class="label">{$form.hide_id_field.label}</div>
+            <div class="content">{$form.hide_id_field.html}</div>
+            <div class="clear"></div>
+        </div>
     </div>
 
     <div class="crm-submit-buttons">
diff --git a/templates/CRM/DataprocessorSearch/Form/ResultsJavascript.tpl b/templates/CRM/DataprocessorSearch/Form/ResultsJavascript.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..91ae61a5b285d16c4fbfbd1752e2bcdd11b6b5b2
--- /dev/null
+++ b/templates/CRM/DataprocessorSearch/Form/ResultsJavascript.tpl
@@ -0,0 +1,13 @@
+<script type="text/javascript">
+    {literal}
+    CRM.$(function($) {
+      // Clear any old selection that may be lingering in quickform
+      $("input.select-row, input.select-rows", 'form.crm-search-form').prop('checked', false).closest('tr').removeClass('crm-row-selected');
+      // Retrieve stored checkboxes
+      var selectedIds = {/literal}{$selectedIds|@json_encode}{literal};
+      if (selectedIds.length > 0) {
+        $('#mark_x_' + selectedIds.join(',#mark_x_') + ',input[name=radio_ts][value=ts_sel]').trigger('click');
+      }
+    });
+    {/literal}
+</script>
\ No newline at end of file
diff --git a/templates/CRM/DataprocessorSearch/Form/Search.tpl b/templates/CRM/DataprocessorSearch/Form/Search.tpl
index 3c6d8d1885984849a4b95b1e975baf144422a016..407cfdf8858c489a9d238f9a9d7d9a2e47ad41c5 100644
--- a/templates/CRM/DataprocessorSearch/Form/Search.tpl
+++ b/templates/CRM/DataprocessorSearch/Form/Search.tpl
@@ -1,44 +1,4 @@
-<div class="crm-form-block crm-search-form-block">
-    <div class="crm-accordion-wrapper crm-advanced_search_form-accordion {if (!empty($rows))}collapsed{/if}">
-        <div class="crm-accordion-header crm-master-accordion-header">
-            {ts}Edit Search Criteria{/ts}
-        </div>
-        <!-- /.crm-accordion-header -->
-        <div class="crm-accordion-body">
-            <div id="searchForm" class="form-item">
-                <table>
-                    <tr>
-                        <th>{ts}Name{/ts}</th>
-                        <th>{ts}Operator{/ts}</th>
-                        <th>{ts}Value{/ts}</th>
-                    </tr>
-                {foreach from=$filters key=filterName item=filter}
-                    {assign var=fieldOp     value=$filterName|cat:"_op"}
-                    {assign var=filterVal   value=$filterName|cat:"_value"}
-                    {assign var=filterMin   value=$filterName|cat:"_min"}
-                    {assign var=filterMax   value=$filterName|cat:"_max"}
-                    {if $filter.type == 'Date'}
-                        <tr>
-                            <td>{$filter.title}</td>
-                            {include file="CRM/DataprocessorSearch/Form/DateRange.tpl" fieldName=$filterName from='_from' to='_to'}
-                        </tr>
-                    {elseif $form.$fieldOp.html}
-                        <tr>
-                            <td class="label">{$filter.title}</td>
-                            <td>{$form.$fieldOp.html}</td>
-                            <td>
-                                <span id="{$filterVal}_cell">{$form.$filterVal.label}&nbsp;{$form.$filterVal.html}</span>
-                                <span id="{$filterMin}_max_cell">{$form.$filterMin.label}&nbsp;{$form.$filterMin.html}&nbsp;&nbsp;{$form.$filterMax.label}&nbsp;{$form.$filterMax.html}</span>
-                            </td>
-                        </tr>
-                    {/if}
-                {/foreach}
-                </table>
-                <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="botton"}</div>
-            </div>
-        </div>
-    </div>
-</div>
+{include file="CRM/DataprocessorSearch/Form/CriteriaForm.tpl"}
 
 {if $debug && isset($debug_info.query)}
     <div class="crm-block crm-form-block">
@@ -67,7 +27,6 @@
                     <thead class="sticky">
                     <tr>
                         <th scope="col" title="Select Rows">{$form.toggleSelect.html}</th>
-                        <th scope="col"></th>
                         {foreach from=$columnHeaders key=headerName item=headerTitle}
                             <th scope="col">
                                 {$sort->_response.$headerName.link}
@@ -78,12 +37,11 @@
 
 
                     {foreach from=$rows item=row}
-                        <tr id='rowid{$row.contact_id}' class="{cycle values="odd-row,even-row"}">
+                        <tr id='rowid{$row.id}' class="{cycle values="odd-row,even-row"}">
                             {assign var=cbName value=$row.checkbox}
-                            {assign var=contact_id value=$row.contact_id}
+                            {assign var=id value=$row.id}
                             {assign var=record value=$row.record}
                             <td>{$form.$cbName.html}</td>
-                            <td>{$row.contact_type}</td>
                             {foreach from=$columnHeaders key=headerName item=headerTitle}
                                 {assign var=columnValue value=$record.$headerName}
                                 <td>{$columnValue->formattedValue}</td>
@@ -104,51 +62,5 @@
         </div>
     </div>
 
-    <script type="text/javascript">
-        {literal}
-        CRM.$(function($) {
-          // Clear any old selection that may be lingering in quickform
-          $("input.select-row, input.select-rows", 'form.crm-search-form').prop('checked', false).closest('tr').removeClass('crm-row-selected');
-          // Retrieve stored checkboxes
-          var cids = {/literal}{$selectedContactIds|@json_encode}{literal};
-          if (cids.length > 0) {
-            //$('#mark_x_' + cids.join(',#mark_x_') + ',input[name=radio_ts][value=ts_sel]').prop('checked', true);
-            $('#mark_x_' + cids.join(',#mark_x_') + ',input[name=radio_ts][value=ts_sel]').trigger('click');
-          }
-        });
-        {/literal}
-    </script>
-{/if}
-
-
-{literal}
-<script type="text/javascript">
-    {/literal}
-    {foreach from=$filters item=filter key=filterName}
-        {literal}var val = "dnc";{/literal}
-        {assign var=fieldOp     value=$filterName|cat:"_op"}
-        {if !($field.operatorType & 4) && !$field.no_display && $form.$fieldOp.html}
-            {literal}var val = document.getElementById("{/literal}{$fieldOp}{literal}").value;{/literal}
-        {/if}
-        {literal}showHideMaxMinVal( "{/literal}{$filterName}{literal}", val );{/literal}
-    {/foreach}
-
-    {literal}
-    function showHideMaxMinVal( field, val ) {
-      var fldVal    = field + "_value_cell";
-      var fldMinMax = field + "_min_max_cell";
-      if ( val == "bw" || val == "nbw" ) {
-        cj('#' + fldVal ).hide();
-        cj('#' + fldMinMax ).show();
-      } else if (val =="nll" || val == "nnll") {
-        cj('#' + fldVal).hide() ;
-        cj('#' + field + '_value').val('');
-        cj('#' + fldMinMax ).hide();
-      } else {
-        cj('#' + fldVal ).show();
-        cj('#' + fldMinMax ).hide();
-      }
-    }
-
-</script>
-{/literal}
+    {include file="CRM/DataprocessorSearch/Form/ResultsJavascript.tpl"}
+{/if}
\ No newline at end of file
diff --git a/xml/Menu/dataprocessor.xml b/xml/Menu/dataprocessor.xml
index e3e78a0649158d35a7f79f960050e02a20a8bd17..8fc489337d194f7df67c3c71d7b46ffa15e549b3 100644
--- a/xml/Menu/dataprocessor.xml
+++ b/xml/Menu/dataprocessor.xml
@@ -78,8 +78,15 @@
     <access_arguments>administer CiviCRM</access_arguments>
   </item>
   <item>
-    <path>civicrm/dataprocessor/form/output/search</path>
-    <page_callback>CRM_DataprocessorSearch_Form_OutputConfiguration</page_callback>
+    <path>civicrm/dataprocessor/form/output/contact_search</path>
+    <page_callback>CRM_DataprocessorSearch_Form_OutputConfiguration_ContactSearch</page_callback>
+    <title>DataProcessor</title>
+    <access_arguments>access CiviCRM</access_arguments>
+    <access_arguments>administer CiviCRM</access_arguments>
+  </item>
+  <item>
+    <path>civicrm/dataprocessor/form/output/activity_search</path>
+    <page_callback>CRM_DataprocessorSearch_Form_OutputConfiguration_ActivitySearch</page_callback>
     <title>DataProcessor</title>
     <access_arguments>access CiviCRM</access_arguments>
     <access_arguments>administer CiviCRM</access_arguments>