From 5dcbc3757aae6c89ba966f93e796125537c091ae Mon Sep 17 00:00:00 2001
From: Jaap Jansma <jaap.jansma@civicoop.org>
Date: Sun, 6 Dec 2020 15:35:03 +0100
Subject: [PATCH] Refactor of how the navigation menu is build/stored.

---
 CHANGELOG.md                                  |   1 +
 CRM/Contact/DataProcessorContactSearch.php    |   9 +-
 CRM/Dataprocessor/Utils/Importer.php          |   2 +
 CRM/DataprocessorSearch/ActivitySearch.php    |   9 +-
 CRM/DataprocessorSearch/CaseSearch.php        |   9 +-
 .../ContributionSearch.php                    |  10 +-
 CRM/DataprocessorSearch/MembershipSearch.php  |  10 +-
 CRM/DataprocessorSearch/ParticipantSearch.php |  10 +-
 CRM/DataprocessorSearch/Search.php            |   9 +-
 Civi/DataProcessor/Output/UIOutputHelper.php  | 203 +++++-------------
 dataprocessor.php                             |   3 +-
 11 files changed, 84 insertions(+), 191 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a466069c..5638cb4a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 # Version 1.23 (not yet released)
 
+* Refactor of how the navigation menu is build/stored. It now makes use of hook_civicrm_navigationMenu instead of storing the items in the database directly.
 * Refactor of config classes.
 
 # Version 1.22.1
diff --git a/CRM/Contact/DataProcessorContactSearch.php b/CRM/Contact/DataProcessorContactSearch.php
index 398f7f8a..1a94e1dd 100644
--- a/CRM/Contact/DataProcessorContactSearch.php
+++ b/CRM/Contact/DataProcessorContactSearch.php
@@ -26,6 +26,7 @@ class CRM_Contact_DataProcessorContactSearch implements UIFormOutputInterface {
    * @param array $filter
    */
   public function buildConfigurationForm(\CRM_Core_Form $form, $output=array()) {
+    \Civi\DataProcessor\Output\UIOutputHelper::fixBackwardsCompatibility($output);
     $navigation = CRM_Dataprocessor_Utils_Navigation::singleton();
     $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $output['data_processor_id']));
     $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
@@ -63,10 +64,6 @@ class CRM_Contact_DataProcessorContactSearch implements UIFormOutputInterface {
 
     // navigation field
     $navigationOptions = $navigation->getNavigationOptions();
-    if (isset($output['configuration']['navigation_id'])) {
-      $navigationPath = $navigation->getNavigationPathById($output['configuration']['navigation_id']);
-      unset($navigationOptions[$navigationPath]);
-    }
     $form->add('select', 'navigation_parent_path', ts('Parent Menu'), array('' => ts('- select -')) + $navigationOptions, true);
 
     $defaults = array();
@@ -84,8 +81,8 @@ class CRM_Contact_DataProcessorContactSearch implements UIFormOutputInterface {
         } else {
           $defaults['no_result_text'] = E::ts('No results');
         }
-        if (isset($output['configuration']['navigation_id'])) {
-          $defaults['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
+        if (isset($output['configuration']['navigation_parent_path'])) {
+          $defaults['navigation_parent_path'] = $output['configuration']['navigation_parent_path'];
         }
         if (isset($output['configuration']['hide_id_field'])) {
           $defaults['hide_id_field'] = $output['configuration']['hide_id_field'];
diff --git a/CRM/Dataprocessor/Utils/Importer.php b/CRM/Dataprocessor/Utils/Importer.php
index 0c52afbb..40f3555b 100644
--- a/CRM/Dataprocessor/Utils/Importer.php
+++ b/CRM/Dataprocessor/Utils/Importer.php
@@ -248,6 +248,7 @@ class CRM_Dataprocessor_Utils_Importer {
     $return = array();
     $importedIds = array();
     $extensions = self::getExtensionFileListWithDataProcessors($extension);
+    \Civi\DataProcessor\Output\UIOutputHelper::disableRebuildingOfMenuAndNavigation();
     foreach($extensions as $ext_file) {
       $data = json_decode($ext_file['data'], true);
       $return[$ext_file['file']] = self::import($data, $ext_file['file']);
@@ -282,6 +283,7 @@ class CRM_Dataprocessor_Utils_Importer {
         $return['deleted data processors'][] = 'Error: '. $dao->id.": ".$dao->name;
       }
     }
+    \Civi\DataProcessor\Output\UIOutputHelper::rebuildMenuAndNavigation();
     return $return;
   }
 
diff --git a/CRM/DataprocessorSearch/ActivitySearch.php b/CRM/DataprocessorSearch/ActivitySearch.php
index f6039d11..d0d5cb95 100644
--- a/CRM/DataprocessorSearch/ActivitySearch.php
+++ b/CRM/DataprocessorSearch/ActivitySearch.php
@@ -27,6 +27,7 @@ class CRM_DataprocessorSearch_ActivitySearch implements UIFormOutputInterface {
    * @param array $filter
    */
   public function buildConfigurationForm(\CRM_Core_Form $form, $output=array()) {
+    \Civi\DataProcessor\Output\UIOutputHelper::fixBackwardsCompatibility($output);
     $navigation = CRM_Dataprocessor_Utils_Navigation::singleton();
     $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $output['data_processor_id']));
     $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
@@ -61,10 +62,6 @@ class CRM_DataprocessorSearch_ActivitySearch implements UIFormOutputInterface {
 
     // navigation field
     $navigationOptions = $navigation->getNavigationOptions();
-    if (isset($output['configuration']['navigation_id'])) {
-      $navigationPath = $navigation->getNavigationPathById($output['configuration']['navigation_id']);
-      unset($navigationOptions[$navigationPath]);
-    }
     $form->add('select', 'navigation_parent_path', ts('Parent Menu'), array('' => ts('- select -')) + $navigationOptions, true);
 
     $defaults = array();
@@ -76,8 +73,8 @@ class CRM_DataprocessorSearch_ActivitySearch implements UIFormOutputInterface {
         if (isset($output['configuration']['activity_id_field'])) {
           $defaults['activity_id_field'] = $output['configuration']['activity_id_field'];
         }
-        if (isset($output['configuration']['navigation_id'])) {
-          $defaults['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
+        if (isset($output['configuration']['navigation_parent_path'])) {
+          $defaults['navigation_parent_path'] = $output['configuration']['navigation_parent_path'];
         }
         if (isset($output['configuration']['hide_id_field'])) {
           $defaults['hide_id_field'] = $output['configuration']['hide_id_field'];
diff --git a/CRM/DataprocessorSearch/CaseSearch.php b/CRM/DataprocessorSearch/CaseSearch.php
index 1e4c5f19..82ae1d16 100644
--- a/CRM/DataprocessorSearch/CaseSearch.php
+++ b/CRM/DataprocessorSearch/CaseSearch.php
@@ -27,6 +27,7 @@ class CRM_DataprocessorSearch_CaseSearch implements UIFormOutputInterface {
    * @param array $filter
    */
   public function buildConfigurationForm(\CRM_Core_Form $form, $output=array()) {
+    \Civi\DataProcessor\Output\UIOutputHelper::fixBackwardsCompatibility($output);
     $navigation = CRM_Dataprocessor_Utils_Navigation::singleton();
     $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $output['data_processor_id']));
     $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
@@ -67,10 +68,6 @@ class CRM_DataprocessorSearch_CaseSearch implements UIFormOutputInterface {
 
     // navigation field
     $navigationOptions = $navigation->getNavigationOptions();
-    if (isset($output['configuration']['navigation_id'])) {
-      $navigationPath = $navigation->getNavigationPathById($output['configuration']['navigation_id']);
-      unset($navigationOptions[$navigationPath]);
-    }
     $form->add('select', 'navigation_parent_path', ts('Parent Menu'), array('' => ts('- select -')) + $navigationOptions, true);
 
     $defaults = array();
@@ -85,8 +82,8 @@ class CRM_DataprocessorSearch_CaseSearch implements UIFormOutputInterface {
         if (isset($output['configuration']['case_id_field'])) {
           $defaults['case_id_field'] = $output['configuration']['case_id_field'];
         }
-        if (isset($output['configuration']['navigation_id'])) {
-          $defaults['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
+        if (isset($output['configuration']['navigation_parent_path'])) {
+          $defaults['navigation_parent_path'] = $output['configuration']['navigation_parent_path'];
         }
         if (isset($output['configuration']['hide_id_fields'])) {
           $defaults['hide_id_fields'] = $output['configuration']['hide_id_fields'];
diff --git a/CRM/DataprocessorSearch/ContributionSearch.php b/CRM/DataprocessorSearch/ContributionSearch.php
index 7884f205..ade051bd 100644
--- a/CRM/DataprocessorSearch/ContributionSearch.php
+++ b/CRM/DataprocessorSearch/ContributionSearch.php
@@ -27,7 +27,7 @@ class CRM_DataprocessorSearch_ContributionSearch implements UIFormOutputInterfac
    * @param array $filter
    */
   public function buildConfigurationForm(\CRM_Core_Form $form, $output=array()) {
-
+    \Civi\DataProcessor\Output\UIOutputHelper::fixBackwardsCompatibility($output);
     $navigation = CRM_Dataprocessor_Utils_Navigation::singleton();
     $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $output['data_processor_id']));
     $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
@@ -62,10 +62,6 @@ class CRM_DataprocessorSearch_ContributionSearch implements UIFormOutputInterfac
 
     // navigation field
     $navigationOptions = $navigation->getNavigationOptions();
-    if (isset($output['configuration']['navigation_id'])) {
-      $navigationPath = $navigation->getNavigationPathById($output['configuration']['navigation_id']);
-      unset($navigationOptions[$navigationPath]);
-    }
     $form->add('select', 'navigation_parent_path', ts('Parent Menu'), array('' => ts('- select -')) + $navigationOptions, true);
 
     $defaults = array();
@@ -77,8 +73,8 @@ class CRM_DataprocessorSearch_ContributionSearch implements UIFormOutputInterfac
         if (isset($output['configuration']['contribution_id_field'])) {
           $defaults['contribution_id_field'] = $output['configuration']['contribution_id_field'];
         }
-        if (isset($output['configuration']['navigation_id'])) {
-          $defaults['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
+        if (isset($output['configuration']['navigation_parent_path'])) {
+          $defaults['navigation_parent_path'] = $output['configuration']['navigation_parent_path'];
         }
         if (isset($output['configuration']['no_result_text'])) {
           $defaults['no_result_text'] = $output['configuration']['no_result_text'];
diff --git a/CRM/DataprocessorSearch/MembershipSearch.php b/CRM/DataprocessorSearch/MembershipSearch.php
index 193ae16c..6e29db7d 100644
--- a/CRM/DataprocessorSearch/MembershipSearch.php
+++ b/CRM/DataprocessorSearch/MembershipSearch.php
@@ -27,7 +27,7 @@ class CRM_DataprocessorSearch_MembershipSearch implements UIFormOutputInterface
    * @param array $filter
    */
   public function buildConfigurationForm(\CRM_Core_Form $form, $output=array()) {
-
+    \Civi\DataProcessor\Output\UIOutputHelper::fixBackwardsCompatibility($output);
     $navigation = CRM_Dataprocessor_Utils_Navigation::singleton();
     $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $output['data_processor_id']));
     $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
@@ -62,10 +62,6 @@ class CRM_DataprocessorSearch_MembershipSearch implements UIFormOutputInterface
 
     // navigation field
     $navigationOptions = $navigation->getNavigationOptions();
-    if (isset($output['configuration']['navigation_id'])) {
-      $navigationPath = $navigation->getNavigationPathById($output['configuration']['navigation_id']);
-      unset($navigationOptions[$navigationPath]);
-    }
     $form->add('select', 'navigation_parent_path', ts('Parent Menu'), array('' => ts('- select -')) + $navigationOptions, true);
 
     $defaults = array();
@@ -82,8 +78,8 @@ class CRM_DataprocessorSearch_MembershipSearch implements UIFormOutputInterface
         } else {
           $defaults['no_result_text'] = E::ts('No results');
         }
-        if (isset($output['configuration']['navigation_id'])) {
-          $defaults['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
+        if (isset($output['configuration']['navigation_parent_path'])) {
+          $defaults['navigation_parent_path'] = $output['configuration']['navigation_parent_path'];
         }
         if (isset($output['configuration']['hide_id_field'])) {
           $defaults['hide_id_field'] = $output['configuration']['hide_id_field'];
diff --git a/CRM/DataprocessorSearch/ParticipantSearch.php b/CRM/DataprocessorSearch/ParticipantSearch.php
index df8b85a0..a0874c7d 100644
--- a/CRM/DataprocessorSearch/ParticipantSearch.php
+++ b/CRM/DataprocessorSearch/ParticipantSearch.php
@@ -27,7 +27,7 @@ class CRM_DataprocessorSearch_ParticipantSearch implements UIFormOutputInterface
    * @param array $filter
    */
   public function buildConfigurationForm(\CRM_Core_Form $form, $output=array()) {
-
+    \Civi\DataProcessor\Output\UIOutputHelper::fixBackwardsCompatibility($output);
     $navigation = CRM_Dataprocessor_Utils_Navigation::singleton();
     $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $output['data_processor_id']));
     $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
@@ -62,10 +62,6 @@ class CRM_DataprocessorSearch_ParticipantSearch implements UIFormOutputInterface
 
     // navigation field
     $navigationOptions = $navigation->getNavigationOptions();
-    if (isset($output['configuration']['navigation_id'])) {
-      $navigationPath = $navigation->getNavigationPathById($output['configuration']['navigation_id']);
-      unset($navigationOptions[$navigationPath]);
-    }
     $form->add('select', 'navigation_parent_path', ts('Parent Menu'), array('' => ts('- select -')) + $navigationOptions, true);
 
     $defaults = array();
@@ -77,8 +73,8 @@ class CRM_DataprocessorSearch_ParticipantSearch implements UIFormOutputInterface
         if (isset($output['configuration']['participant_id_field'])) {
           $defaults['participant_id_field'] = $output['configuration']['participant_id_field'];
         }
-        if (isset($output['configuration']['navigation_id'])) {
-          $defaults['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
+        if (isset($output['configuration']['navigation_parent_path'])) {
+          $defaults['navigation_parent_path'] = $output['configuration']['navigation_parent_path'];
         }
         if (isset($output['configuration']['hide_id_field'])) {
           $defaults['hide_id_field'] = $output['configuration']['hide_id_field'];
diff --git a/CRM/DataprocessorSearch/Search.php b/CRM/DataprocessorSearch/Search.php
index 7ee7ddbd..975fa87b 100644
--- a/CRM/DataprocessorSearch/Search.php
+++ b/CRM/DataprocessorSearch/Search.php
@@ -28,6 +28,7 @@ class CRM_DataprocessorSearch_Search implements UIFormOutputInterface {
    * @param array $filter
    */
   public function buildConfigurationForm(\CRM_Core_Form $form, $output=array()) {
+    \Civi\DataProcessor\Output\UIOutputHelper::fixBackwardsCompatibility($output);
     $navigation = CRM_Dataprocessor_Utils_Navigation::singleton();
     $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $output['data_processor_id']));
     $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
@@ -64,10 +65,6 @@ class CRM_DataprocessorSearch_Search implements UIFormOutputInterface {
 
     // navigation field
     $navigationOptions = $navigation->getNavigationOptions();
-    if (isset($output['configuration']['navigation_id'])) {
-      $navigationPath = $navigation->getNavigationPathById($output['configuration']['navigation_id']);
-      unset($navigationOptions[$navigationPath]);
-    }
     $form->add('select', 'navigation_parent_path', ts('Parent Menu'), array('' => ts('- select -')) + $navigationOptions, true);
 
     $defaults = array();
@@ -84,8 +81,8 @@ class CRM_DataprocessorSearch_Search implements UIFormOutputInterface {
         } else {
           $defaults['no_result_text'] = E::ts('No results');
         }
-        if (isset($output['configuration']['navigation_id'])) {
-          $defaults['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
+        if (isset($output['configuration']['navigation_parent_path'])) {
+          $defaults['navigation_parent_path'] = $output['configuration']['navigation_parent_path'];
         }
         if (isset($output['configuration']['hide_id_field'])) {
           $defaults['hide_id_field'] = $output['configuration']['hide_id_field'];
diff --git a/Civi/DataProcessor/Output/UIOutputHelper.php b/Civi/DataProcessor/Output/UIOutputHelper.php
index cdab5ca2..c3d6b24f 100644
--- a/Civi/DataProcessor/Output/UIOutputHelper.php
+++ b/Civi/DataProcessor/Output/UIOutputHelper.php
@@ -15,7 +15,7 @@ namespace Civi\DataProcessor\Output;
  */
 class UIOutputHelper {
 
-  private static $rebuildMenu = false;
+  private static $rebuildMenu = true;
 
   /**
    * Delegation of the alter menu hook. Add the search outputs to the menu system.
@@ -56,92 +56,59 @@ class UIOutputHelper {
     }
   }
 
-  /**
-   * Update the navigation data when an output is saved/deleted from the database.
-   *
-   * @param $op
-   * @param $objectName
-   * @param $objectId
-   * @param $objectRef
-   */
-  public static function preHook($op, $objectName, $id, &$params) {
-    if ($objectName == 'DataProcessorOutput') {
-      // Disable this hook in unit tests because the menu rebuild it causes breaks transactions.
-      if (CIVICRM_UF === 'UnitTests') {
-        return;
-      }
-      $factory = dataprocessor_get_factory();
-      if ($op == 'delete') {
-        $output = civicrm_api3('DataProcessorOutput', 'getsingle', ['id' => $id]);
-        $outputClass = $factory->getOutputByName($output['type']);
-        if ($outputClass instanceof UIOutputInterface) {
-          self::$rebuildMenu = TRUE;
-        }
-        self::removeOutputFromNavigation($output['configuration']);
-      }
-      elseif ($op == 'edit') {
-        $output = civicrm_api3('DataProcessorOutput', 'getsingle', ['id' => $id]);
-        $outputClass = $factory->getOutputByName($output['type']);
-        if ($outputClass instanceof UIOutputInterface) {
-          self::$rebuildMenu = TRUE;
-        }
-        if (!isset($output['configuration']['navigation_id']) && !isset($params['configuration']['navigation_parent_path'])) {
-          return;
-        }
-        elseif (!isset($params['configuration']['navigation_parent_path'])) {
-          self::removeOutputFromNavigation($output['configuration']);
-        }
-        elseif (isset($params['configuration']['navigation_parent_path'])) {
-          if (!isset($output['configuration']['navigation_parent_path']) || $params['configuration']['navigation_parent_path'] != $output['configuration']['navigation_parent_path']) {
-            // Merge the current output from the database with the updated values
-            $configuration = array_merge($output['configuration'], $params['configuration']);
-            $configuration['navigation_parent_path'] = $params['configuration']['navigation_parent_path'];
-            $output = array_merge($output, $params);
-            $output['configuration'] = $configuration;
-            $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', ['id' => $output['data_processor_id']]);
-            $configuration = self::createOrUpdateNavigationItem($output, $dataProcessor);
-            if ($configuration) {
-              $params['configuration'] = $configuration;
-            }
-          }
-        }
-      }
-      elseif ($op == 'create') {
-        self::$rebuildMenu = true;
-        if (isset($params['configuration']['navigation_parent_path'])) {
-          $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', ['id' => $params['data_processor_id']]);
-          $configuration = self::createOrUpdateNavigationItem($params, $dataProcessor);
-          if ($configuration) {
-            $params['configuration'] = $configuration;
+  public static function navigationMenuHook(&$menu) {
+    $factory = dataprocessor_get_factory();
+    $dao = \CRM_Core_DAO::executeQuery("
+      SELECT DISTINCT o.id as output_id, o.type, p.id as dataprocessor_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 p.id = 9
+    ");
+    while ($dao->fetch()) {
+      $outputClass = $factory->getOutputByName($dao->type);
+      if ($outputClass instanceof UIOutputInterface) {
+        $output = civicrm_api3('DataProcessorOutput', 'getsingle', ['id' => $dao->output_id]);
+        self::fixBackwardsCompatibility($output);
+        if (isset($output['configuration']['navigation_parent_path'])) {
+          $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', ['id' => $dao->dataprocessor_id]);
+          $title = $dataProcessor['title'];
+          if ($outputClass && $outputClass instanceof \Civi\DataProcessor\Output\UIFormOutputInterface) {
+            $url = $outputClass->getUrlToUi($output, $dataProcessor);
+            $title = $outputClass->getTitleForUiLink($output, $dataProcessor);
           }
-        }
-      }
-    } elseif ($objectName == 'DataProcessor' && $op == 'edit') {
-      $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', ['id' => $id]);
-      if (isset($params['is_active']) && $params['is_active'] != $dataProcessor['is_active']) {
-        // Only update navigation when is active is changed
-        $dataProcessor = array_merge($dataProcessor, $params);
-        $outputs = civicrm_api3('DataProcessorOutput', 'get', ['data_processor_id' => $id, 'options' => ['limit' => 0]]);
-        foreach($outputs['values'] as $output) {
-          if (isset($output['configuration']['navigation_id'])) {
-            self::createOrUpdateNavigationItem($output, $dataProcessor);
+          $item = [
+            'label' => $title,
+            'name' => $dataProcessor['name'].'_'.$output['type'],
+            'url' => \CRM_Utils_System::url($url, 'reset=1', true),
+            'operator' => 'OR',
+            'separator' => 0,
+          ];
+          if (isset($output['permission'])) {
+            $item['permission'] = $output['permission'];
           }
+          _dataprocessor_civix_insert_navigation_menu($menu, $output['configuration']['navigation_parent_path'], $item);
         }
       }
     }
   }
 
   /**
-   * Remove an output from the navigation menu.
+   * Fix backwards compatibility. Previously we stored the navigation id in the database.
+   * Now we have the parent path.
    *
-   * @param array $configuration
+   * @param $output
    */
-  private static function removeOutputFromNavigation($configuration) {
-    if (isset($configuration['navigation_id'])) {
-      $navId = $configuration['navigation_id'];
-      \CRM_Core_BAO_Navigation::processDelete($navId);
-      \CRM_Core_BAO_Navigation::resetNavigation();
-      self::$rebuildMenu = TRUE;
+  public static function fixBackwardsCompatibility($output) {
+    $navigation = \CRM_Dataprocessor_Utils_Navigation::singleton();
+    if (isset($output['configuration']['navigation_id'])) {
+      // Backwards compatibility.
+      $output['configuration']['navigation_parent_path'] = $navigation->getNavigationParentPathById($output['configuration']['navigation_id']);
+      \CRM_Core_BAO_Navigation::processDelete($output['configuration']['navigation_id']);
+      unset($output['configuration']['navigation_id']);
+      \CRM_Core_DAO::executeQuery("UPDATE civicrm_data_processor_output SET configuration = %1 WHERE id = %2", [
+        1 => [json_encode($output['configuration']), 'String'],
+        2 => [$output['id'],'Integer'],
+      ]);
     }
   }
 
@@ -154,21 +121,13 @@ class UIOutputHelper {
    * @param $objectRef
    */
   public static function postHook($op, $objectName, $id, &$objectRef) {
-    if ($objectName != 'DataProcessorOutput') {
-      return;
-    }
     // Disable this hook in unit tests because the menu rebuild it causes breaks transactions.
     if (CIVICRM_UF === 'UnitTests') {
       return;
     }
 
-    if (self::$rebuildMenu) {
-      // Rebuild the CiviCRM Menu (which holds all the pages)
-      \CRM_Core_Menu::store();
-
-      // also reset navigation
-      \CRM_Core_BAO_Navigation::resetNavigation();
-      self::$rebuildMenu = false;
+    if (self::$rebuildMenu && ($objectName == 'DataProcessorOutput' || $objectName == 'DataProcessor')) {
+      self::rebuildMenuAndNavigation();
     }
   }
 
@@ -187,67 +146,21 @@ class UIOutputHelper {
   }
 
   /**
-   * Inserts/updates an navigation item.
-   * @param $params
-   * @param $dataProcessor
-   *
-   * @return array
+   * Disable the rebuilding of the menu and navigation.
+   * Usefull during an batch import.
    */
-  private static function createOrUpdateNavigationItem($output, $dataProcessor) {
-    $url = "";
-    $factory = dataprocessor_get_factory();
-    if (!isset($output['type'])) {
-      return false;
-    }
-    $outputClass = $factory->getOutputByName($output['type']);
-    $configuration = $output['configuration'];
-
-    $title = $dataProcessor['title'];
-    if ($outputClass && $outputClass instanceof \Civi\DataProcessor\Output\UIFormOutputInterface) {
-      $url = $outputClass->getUrlToUi($output, $dataProcessor);
-      $title = $outputClass->getTitleForUiLink($output, $dataProcessor);
-    }
-
-    $navigation = \CRM_Dataprocessor_Utils_Navigation::singleton();
-    $navigationParams = array();
-    // Retrieve the current navigation ID.
-    if (isset($configuration['navigation_id'])) {
-      // Get the default navigation parent id.
-      $navigationDefaults = [];
-      $retrieveNavParams = ['id' => $configuration['navigation_id']];
-      \CRM_Core_BAO_Navigation::retrieve($retrieveNavParams, $navigationDefaults);
-      if (!empty($navigationDefaults['id'])) {
-        $navigationParams['id'] = $navigationDefaults['id'];
-        $navigationParams['current_parent_id'] = !empty($navigationDefaults['parent_id']) ? $navigationDefaults['parent_id'] : NULL;
-        $navigationParams['parent_id'] = !empty($navigationDefaults['parent_id']) ? $navigationDefaults['parent_id'] : NULL;
-      }
-    }
-
-    $navigationParams['domain_id'] = \CRM_Core_Config::domainID();
-    $navigationParams['permission'] = array();
-    $navigationParams['label'] = $title;
-    $navigationParams['name'] = $dataProcessor['name'];
-
-    if (isset($configuration['navigation_parent_path'])) {
-      $navigationParams['parent_id'] = $navigation->getNavigationIdByPath($configuration['navigation_parent_path']);
-    }
-    $navigationParams['is_active'] = $dataProcessor['is_active'];
-
-    if (isset($output['permission'])) {
-      $navigationParams['permission'][] = $output['permission'];
-    }
-
-    unset($configuration['navigation_parent_path']);
+  public static function disableRebuildingOfMenuAndNavigation() {
+    self::$rebuildMenu = false;
+  }
 
-    $navigationParams['url'] = $url.'?reset=1';
-    $navigation = \CRM_Core_BAO_Navigation::add($navigationParams);
+  /**
+   * Rebuild the menu and navigation.
+   */
+  public static function rebuildMenuAndNavigation() {
+    // Rebuild the CiviCRM Menu (which has the url and routing information of the pages).
+    \CRM_Core_Menu::store();
+    // Also reset navigation
     \CRM_Core_BAO_Navigation::resetNavigation();
-
-    $configuration['navigation_id'] = $navigation->id;
-
-    self::$rebuildMenu = TRUE;
-
-    return $configuration;
   }
 
 }
diff --git a/dataprocessor.php b/dataprocessor.php
index f30d7904..4cb970c5 100644
--- a/dataprocessor.php
+++ b/dataprocessor.php
@@ -115,7 +115,7 @@ function dataprocessor_civicrm_alterMenu(&$items) {
  * @param $params
  */
 function dataprocessor_civicrm_pre($op, $objectName, $objectId, &$params) {
-  \Civi\DataProcessor\Output\UIOutputHelper::preHook($op, $objectName, $objectId, $params);
+
 }
 
 /**
@@ -318,5 +318,6 @@ function dataprocessor_civicrm_navigationMenu(&$menu) {
     'operator' => 'OR',
     'separator' => 0,
   ));
+  \Civi\DataProcessor\Output\UIOutputHelper::navigationMenuHook($menu);
   _dataprocessor_civix_navigationMenu($menu);
 }
-- 
GitLab