Merge pull request #17539 from eileenmcnaughton/fatal2
authorSeamus Lee <seamuslee001@gmail.com>
Mon, 8 Jun 2020 00:53:13 +0000 (10:53 +1000)
committerGitHub <noreply@github.com>
Mon, 8 Jun 2020 00:53:13 +0000 (10:53 +1000)
Replace calls to fatal with statusBounces

17 files changed:
CRM/Contact/BAO/SearchCustom.php
CRM/Contact/Form/Search/Custom/PriceSet.php
CRM/Contact/Import/ImportJob.php
CRM/Contribute/BAO/Contribution.php
CRM/Contribute/Form/AbstractEditPayment.php
CRM/Contribute/Form/Contribution/Main.php
CRM/Contribute/Form/Task/Invoice.php
CRM/Contribute/Form/Task/Status.php
CRM/Contribute/Import/Parser.php
CRM/Event/Form/Registration.php
CRM/Event/Form/Registration/Register.php
CRM/Financial/Form/FrontEndPaymentFormTrait.php
CRM/Import/DataSource/CSV.php
CRM/Report/Form.php
CRM/Report/Form/Activity.php
CRM/Report/Page/Options.php
tools/extensions/org.civicrm.angularex/angularex.civix.php

index 548ec894aa5357ddd22f9d5fe7155fc54427a19b..fd18da44305633bc7fc07a224bf767cad02085f9 100644 (file)
@@ -76,7 +76,7 @@ class CRM_Contact_BAO_SearchCustom {
 
     $error = include_once $customSearchFile;
     if ($error == FALSE) {
-      CRM_Core_Error::fatal('Custom search file: ' . $customSearchFile . ' does not exist. Please verify your custom search settings in CiviCRM administrative panel.');
+      throw new CRM_Core_Exception('Custom search file: ' . $customSearchFile . ' does not exist. Please verify your custom search settings in CiviCRM administrative panel.');
     }
 
     return [$customSearchID, $customSearchClass, $formValues];
@@ -93,7 +93,7 @@ class CRM_Contact_BAO_SearchCustom {
     list($customSearchID, $customSearchClass, $formValues) = self::details($csID, $ssID);
 
     if (!$customSearchID) {
-      CRM_Core_Error::fatal('Could not resolve custom search ID');
+      throw new CRM_Core_Exception('Could not resolve custom search ID');
     }
 
     // instantiate the new class
index 327bc9004502be2787411fa46fa4ad8af9dc7b5b..74e5aa006ebb9d4f39f12cf71a98c3bf3593161e 100644 (file)
@@ -225,7 +225,7 @@ AND    p.entity_id    = e.id
     if ($dao->fetch() &&
       !$dao->price_set_id
     ) {
-      CRM_Core_Error::fatal(ts('There are no events with Price Sets'));
+      throw new CRM_Core_Exception(ts('There are no events with Price Sets'));
     }
 
     // get all the fields and all the option values associated with it
index 7940288294753483f09debf74bc25282075c5796..b598bd3cd3ca539ab50dbc48c682a42ccd26c031 100644 (file)
@@ -93,7 +93,7 @@ class CRM_Contact_Import_ImportJob {
    */
   public function isComplete($dropIfComplete = TRUE) {
     if (!$this->_statusFieldName) {
-      CRM_Core_Error::fatal("Could not get name of the import status field");
+      throw new CRM_Core_Exception("Could not get name of the import status field");
     }
     $query = "SELECT * FROM $this->_tableName
                   WHERE  $this->_statusFieldName = 'NEW' LIMIT 1";
index 655a9c63ac4d3df7b3a7dd69fbb4a1db0352ec5a..6ad09931c8fed3afef8aa4b8c1774b5677e03e60 100644 (file)
@@ -1944,7 +1944,7 @@ LEFT JOIN civicrm_option_value contribution_status ON (civicrm_contribution.cont
     }
 
     if (empty($clauses)) {
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception('No Where clauses defined when deleting address');
     }
 
     $condition = implode(' OR ', $clauses);
index 6a328ca1e9e6b43b28e79aa0a8c44f2c50e03a65..953c0f31cd9a98d4aadda0bf20195e930b250bd7 100644 (file)
@@ -414,7 +414,7 @@ WHERE  contribution_id = {$id}
       //get all status
       $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
       if (!($paymentStatusID == array_search('Pending', $allStatus) || $paymentStatusID == array_search('Overdue', $allStatus))) {
-        CRM_Core_Error::fatal(ts("Pledge payment status should be 'Pending' or  'Overdue'."));
+        CRM_Core_Error::statusBounce(ts("Pledge payment status should be 'Pending' or  'Overdue'."));
       }
 
       //get the pledge values associated with given pledge payment.
index b55883cbee07507fc61baea02d5c5dff05052d35..e894fa1495fc21e0db99f72547621cb55cf03511 100644 (file)
@@ -454,7 +454,7 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
       }
     }
     if (empty($this->_values['fee']) && empty($this->_ccid)) {
-      CRM_Core_Error::fatal(ts('This page does not have any price fields configured or you may not have permission for them. Please contact the site administrator for more details.'));
+      throw new CRM_Core_Exception(ts('This page does not have any price fields configured or you may not have permission for them. Please contact the site administrator for more details.'));
     }
 
     //we have to load confirm contribution button in template
index 5e9bedcf0794f78cedc441b4014992a92cba3495..d189158cd8a0402c128c2309da5751b33bc0e47c 100644 (file)
@@ -229,7 +229,7 @@ class CRM_Contribute_Form_Task_Invoice extends CRM_Contribute_Form_Task {
       $ids['event'] = $detail['event'] ?? NULL;
 
       if (!$invoiceElements['baseIPN']->validateData($input, $ids, $objects, FALSE)) {
-        CRM_Core_Error::fatal();
+        CRM_Core_Error::statusBounce('Supplied data was not able to be validated');
       }
 
       $contribution = &$objects['contribution'];
index c8acc1e8ff88e65a559b25f33d5f0a29e5a1e31c..e3c2a6e7daa1b769f2e689ddc4efc3b55755c9dc 100644 (file)
@@ -234,7 +234,7 @@ AND    co.id IN ( $contribIDs )";
       $ids['event'] = $details[$row['contribution_id']]['event'] ?? NULL;
 
       if (!$baseIPN->validateData($input, $ids, $objects, FALSE)) {
-        CRM_Core_Error::fatal();
+        CRM_Core_Error::statusBounce('Supplied data was not able to be validated');
       }
 
       $contribution = &$objects['contribution'];
index e36f742fc1b1eb3a0932951a28ce63efd1e3e59c..87637bb6294d58a8bbcf63b63e1b1777741d6622 100644 (file)
@@ -128,7 +128,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser {
     $totalRowCount = NULL
   ) {
     if (!is_array($fileName)) {
-      CRM_Core_Error::fatal();
+      throw new CRM_Core_Exception('Unable to determine import file');
     }
     $fileName = $fileName['name'];
 
index 8e69eb560e4f8a535aae9225061464d44bc9c021..638f2b8f2dc5317afb543f84fbb8fcb3f3a70dd2 100644 (file)
@@ -78,14 +78,6 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
    */
   public $_additionalParticipantIds;
 
-  /**
-   * The mode that we are in.
-   *
-   * @var string
-   * @protect
-   */
-  public $_mode;
-
   /**
    * The values for the contribution db object.
    *
@@ -182,12 +174,9 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
   public function preProcess() {
     $this->_eventId = CRM_Utils_Request::retrieve('id', 'Positive', $this, TRUE);
     $this->_action = CRM_Utils_Request::retrieve('action', 'Alphanumeric', $this, FALSE, CRM_Core_Action::ADD);
-
     //CRM-4320
     $this->_participantId = CRM_Utils_Request::retrieve('participantId', 'Positive', $this);
-
-    // current mode
-    $this->_mode = ($this->_action == 1024) ? 'test' : 'live';
+    $this->setPaymentMode();
 
     $this->_values = $this->get('values');
     $this->_fields = $this->get('fields');
index a2bc57ef5f7218ca9e58135637a9cca5c4942af6..80e3f7c72f951260a7c18c77f41c358d62bf9383 100644 (file)
@@ -1157,7 +1157,7 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
     // CRM-3907, skip check for preview registrations
     // CRM-4320 participant need to walk wizard
     if (
-      ($form->_mode == 'test' || $form->_allowConfirmation)
+      ($form->getPaymentMode() === 'test' || $form->_allowConfirmation)
     ) {
       return FALSE;
     }
index 1f296801125c9383c01aa4af6952522b8f9fb852..dbf787e27d616a03c1cab006b796764d363c2436 100644 (file)
  */
 trait CRM_Financial_Form_FrontEndPaymentFormTrait {
 
+  /**
+   * Is pay later enabled on this form?
+   *
+   * @var bool
+   */
+  protected $isPayLater = FALSE;
+
   /**
    * The label for the pay later pseudoprocessor option.
    *
@@ -27,6 +34,58 @@ trait CRM_Financial_Form_FrontEndPaymentFormTrait {
    */
   protected $payLaterLabel;
 
+  /**
+   * Is this a back office form
+   *
+   * @var bool
+   */
+  public $isBackOffice = FALSE;
+
+  /**
+   * The payment mode that we are in ("live" or "test")
+   * This should be protected and retrieved via getPaymentMode() but it's accessed all over the place so we have to leave it public for now.
+   *
+   * @var string
+   */
+  public $_mode;
+
+  /**
+   * @return bool
+   */
+  public function isPayLater() {
+    return $this->isPayLater;
+  }
+
+  /**
+   * @param bool $isPayLater
+   */
+  public function setIsPayLater($isPayLater) {
+    $this->isPayLater = $isPayLater;
+  }
+
+  /**
+   * @return bool
+   */
+  public function getIsBackOffice() {
+    return $this->isBackOffice;
+  }
+
+  /**
+   * Get the payment mode ('live' or 'test')
+   *
+   * @return string
+   */
+  public function getPaymentMode() {
+    return $this->_mode;
+  }
+
+  /**
+   * Set the payment mode ('live' or 'test')
+   */
+  public function setPaymentMode() {
+    $this->_mode = ($this->_action === CRM_Core_Action::PREVIEW) ? 'test' : 'live';
+  }
+
   /**
    * @return string
    */
index 7f066497edff88132087f9be7668bcb45d72d9fc..e03b5972f7e01c833e6bd07625778e5747c73ec7 100644 (file)
@@ -118,10 +118,10 @@ class CRM_Import_DataSource_CSV extends CRM_Import_DataSource {
     $result = [];
     $fd = fopen($file, 'r');
     if (!$fd) {
-      CRM_Core_Error::fatal("Could not read $file");
+      throw new CRM_Core_Exception("Could not read $file");
     }
     if (filesize($file) == 0) {
-      CRM_Core_Error::fatal("$file is empty. Please upload a valid file.");
+      throw new CRM_Core_Exception("$file is empty. Please upload a valid file.");
     }
 
     $config = CRM_Core_Config::singleton();
index f09de2def6efe856dd694eee5e371e78d75d5690..7ea3a221796ce7781bf8637a0818e403e817a962 100644 (file)
@@ -612,7 +612,7 @@ class CRM_Report_Form extends CRM_Core_Form {
         $this->_instanceValues
       );
       if (empty($this->_instanceValues)) {
-        CRM_Core_Error::fatal("Report could not be loaded.");
+        CRM_Core_Error::statusBounce("Report could not be loaded.");
       }
       $this->_title = $this->_instanceValues['title'];
       if (!empty($this->_instanceValues['permission']) &&
index 19c6a9fc3e8fbdd8b14e14860f3dec74f35eea07..e0c5b6408eeba56a61a08b78eada9c026ad5d991 100644 (file)
@@ -643,7 +643,7 @@ class CRM_Report_Form_Activity extends CRM_Report_Form {
    */
   public function add2group($groupID) {
     if (CRM_Utils_Array::value("contact_target_op", $this->_params) == 'nll') {
-      CRM_Core_Error::fatal(ts('Current filter criteria didn\'t have any target contact to add to group'));
+      CRM_Core_Error::statusBounce(ts('Current filter criteria didn\'t have any target contact to add to group'));
     }
 
     $new_select = 'AS addtogroup_contact_id';
index 71c005d679eb1797d4e79139c5d6460bb24bde79..a5852f190283a9213b08682ab14c12b07df8ffc8 100644 (file)
@@ -63,7 +63,7 @@ class CRM_Report_Page_Options extends CRM_Core_Page_Basic {
       self::$_gId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', self::$_gName, 'id', 'name');
     }
     else {
-      CRM_Core_Error::fatal();
+      CRM_Core_Error::statusBounce('Unable to determine the Option Group');
     }
 
     self::$_GName = ucwords(str_replace('_', ' ', self::$_gName));
index fcb5d572b075a98fdcd74357c9744d91c061923a..308a7699e560a8edeb090f44583bf378d0042852 100644 (file)
@@ -3,10 +3,86 @@
 // AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
 
 /**
- * (Delegated) Implementation of hook_civicrm_config
+ * The ExtensionUtil class provides small stubs for accessing resources of this
+ * extension.
+ */
+class CRM_Angularex_ExtensionUtil {
+  const SHORT_NAME = "angularex";
+  const LONG_NAME = "org.civicrm.angularex";
+  const CLASS_PREFIX = "CRM_Angularex";
+
+  /**
+   * Translate a string using the extension's domain.
+   *
+   * If the extension doesn't have a specific translation
+   * for the string, fallback to the default translations.
+   *
+   * @param string $text
+   *   Canonical message text (generally en_US).
+   * @param array $params
+   * @return string
+   *   Translated text.
+   * @see ts
+   */
+  public static function ts($text, $params = []) {
+    if (!array_key_exists('domain', $params)) {
+      $params['domain'] = [self::LONG_NAME, NULL];
+    }
+    return ts($text, $params);
+  }
+
+  /**
+   * Get the URL of a resource file (in this extension).
+   *
+   * @param string|NULL $file
+   *   Ex: NULL.
+   *   Ex: 'css/foo.css'.
+   * @return string
+   *   Ex: 'http://example.org/sites/default/ext/org.example.foo'.
+   *   Ex: 'http://example.org/sites/default/ext/org.example.foo/css/foo.css'.
+   */
+  public static function url($file = NULL) {
+    if ($file === NULL) {
+      return rtrim(CRM_Core_Resources::singleton()->getUrl(self::LONG_NAME), '/');
+    }
+    return CRM_Core_Resources::singleton()->getUrl(self::LONG_NAME, $file);
+  }
+
+  /**
+   * Get the path of a resource file (in this extension).
+   *
+   * @param string|NULL $file
+   *   Ex: NULL.
+   *   Ex: 'css/foo.css'.
+   * @return string
+   *   Ex: '/var/www/example.org/sites/default/ext/org.example.foo'.
+   *   Ex: '/var/www/example.org/sites/default/ext/org.example.foo/css/foo.css'.
+   */
+  public static function path($file = NULL) {
+    // return CRM_Core_Resources::singleton()->getPath(self::LONG_NAME, $file);
+    return __DIR__ . ($file === NULL ? '' : (DIRECTORY_SEPARATOR . $file));
+  }
+
+  /**
+   * Get the name of a class within this extension.
+   *
+   * @param string $suffix
+   *   Ex: 'Page_HelloWorld' or 'Page\\HelloWorld'.
+   * @return string
+   *   Ex: 'CRM_Foo_Page_HelloWorld'.
+   */
+  public static function findClass($suffix) {
+    return self::CLASS_PREFIX . '_' . str_replace('\\', '_', $suffix);
+  }
+
+}
+
+use CRM_Angularex_ExtensionUtil as E;
+
+/**
+ * (Delegated) Implements hook_civicrm_config().
  *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_config
- * @param null $config
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
 function _angularex_civix_civicrm_config(&$config = NULL) {
   static $configured = FALSE;
@@ -32,10 +108,11 @@ function _angularex_civix_civicrm_config(&$config = NULL) {
 }
 
 /**
- * (Delegated) Implementation of hook_civicrm_xmlMenu
+ * (Delegated) Implements hook_civicrm_xmlMenu().
  *
  * @param $files array(string)
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_xmlMenu
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu
  */
 function _angularex_civix_civicrm_xmlMenu(&$files) {
   foreach (_angularex_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) {
@@ -44,59 +121,74 @@ function _angularex_civix_civicrm_xmlMenu(&$files) {
 }
 
 /**
- * Implementation of hook_civicrm_install
+ * Implements hook_civicrm_install().
  *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install
  */
 function _angularex_civix_civicrm_install() {
   _angularex_civix_civicrm_config();
   if ($upgrader = _angularex_civix_upgrader()) {
-    return $upgrader->onInstall();
+    $upgrader->onInstall();
   }
 }
 
 /**
- * Implementation of hook_civicrm_uninstall
+ * Implements hook_civicrm_postInstall().
  *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
+ */
+function _angularex_civix_civicrm_postInstall() {
+  _angularex_civix_civicrm_config();
+  if ($upgrader = _angularex_civix_upgrader()) {
+    if (is_callable([$upgrader, 'onPostInstall'])) {
+      $upgrader->onPostInstall();
+    }
+  }
+}
+
+/**
+ * Implements hook_civicrm_uninstall().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
  */
 function _angularex_civix_civicrm_uninstall() {
   _angularex_civix_civicrm_config();
   if ($upgrader = _angularex_civix_upgrader()) {
-    return $upgrader->onUninstall();
+    $upgrader->onUninstall();
   }
 }
 
 /**
- * (Delegated) Implementation of hook_civicrm_enable
+ * (Delegated) Implements hook_civicrm_enable().
  *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable
  */
 function _angularex_civix_civicrm_enable() {
   _angularex_civix_civicrm_config();
   if ($upgrader = _angularex_civix_upgrader()) {
     if (is_callable([$upgrader, 'onEnable'])) {
-      return $upgrader->onEnable();
+      $upgrader->onEnable();
     }
   }
 }
 
 /**
- * (Delegated) Implementation of hook_civicrm_disable
+ * (Delegated) Implements hook_civicrm_disable().
  *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
+ * @return mixed
  */
 function _angularex_civix_civicrm_disable() {
   _angularex_civix_civicrm_config();
   if ($upgrader = _angularex_civix_upgrader()) {
     if (is_callable([$upgrader, 'onDisable'])) {
-      return $upgrader->onDisable();
+      $upgrader->onDisable();
     }
   }
 }
 
 /**
- * (Delegated) Implementation of hook_civicrm_upgrade
+ * (Delegated) Implements hook_civicrm_upgrade().
  *
  * @param $op string, the type of operation being performed; 'check' or 'enqueue'
  * @param $queue CRM_Queue_Queue, (for 'enqueue') the modifiable list of pending up upgrade tasks
@@ -104,7 +196,7 @@ function _angularex_civix_civicrm_disable() {
  * @return mixed  based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending)
  *                for 'enqueue', returns void
  *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
  */
 function _angularex_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
   if ($upgrader = _angularex_civix_upgrader()) {
@@ -125,13 +217,14 @@ function _angularex_civix_upgrader() {
 }
 
 /**
- * Search directory tree for files which match a glob pattern
+ * Search directory tree for files which match a glob pattern.
  *
  * Note: Dot-directories (like "..", ".git", or ".svn") will be ignored.
  * Note: In Civi 4.3+, delegate to CRM_Utils_File::findFiles()
  *
- * @param $dir string, base dir
- * @param $pattern string, glob pattern, eg "*.txt"
+ * @param string $dir base dir
+ * @param string $pattern , glob pattern, eg "*.txt"
+ *
  * @return array(string)
  */
 function _angularex_civix_find_files($dir, $pattern) {
@@ -162,22 +255,24 @@ function _angularex_civix_find_files($dir, $pattern) {
   }
   return $result;
 }
-
 /**
- * (Delegated) Implementation of hook_civicrm_managed
+ * (Delegated) Implements hook_civicrm_managed().
  *
  * Find any *.mgd.php files, merge their content, and return.
  *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_managed
- * @param $entities
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed
  */
 function _angularex_civix_civicrm_managed(&$entities) {
   $mgdFiles = _angularex_civix_find_files(__DIR__, '*.mgd.php');
+  sort($mgdFiles);
   foreach ($mgdFiles as $file) {
     $es = include $file;
     foreach ($es as $e) {
       if (empty($e['module'])) {
-        $e['module'] = 'org.civicrm.angularex';
+        $e['module'] = E::LONG_NAME;
+      }
+      if (empty($e['params']['version'])) {
+        $e['params']['version'] = '3';
       }
       $entities[] = $e;
     }
@@ -185,15 +280,13 @@ function _angularex_civix_civicrm_managed(&$entities) {
 }
 
 /**
- * (Delegated) Implementation of hook_civicrm_caseTypes
+ * (Delegated) Implements hook_civicrm_caseTypes().
  *
  * Find any and return any files matching "xml/case/*.xml"
  *
  * Note: This hook only runs in CiviCRM 4.4+.
  *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_caseTypes
- * @param $caseTypes
- * @throws \Exception
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_caseTypes
  */
 function _angularex_civix_civicrm_caseTypes(&$caseTypes) {
   if (!is_dir(__DIR__ . '/xml/case')) {
@@ -204,17 +297,60 @@ function _angularex_civix_civicrm_caseTypes(&$caseTypes) {
     $name = preg_replace('/\.xml$/', '', basename($file));
     if ($name != CRM_Case_XMLProcessor::mungeCaseType($name)) {
       $errorMessage = sprintf("Case-type file name is malformed (%s vs %s)", $name, CRM_Case_XMLProcessor::mungeCaseType($name));
-      CRM_Core_Error::fatal($errorMessage);
-      // throw new CRM_Core_Exception($errorMessage);
+      throw new CRM_Core_Exception($errorMessage);
     }
     $caseTypes[$name] = [
-      'module' => 'org.civicrm.angularex',
+      'module' => E::LONG_NAME,
       'name' => $name,
       'file' => $file,
     ];
   }
 }
 
+/**
+ * (Delegated) Implements hook_civicrm_angularModules().
+ *
+ * Find any and return any files matching "ang/*.ang.php"
+ *
+ * Note: This hook only runs in CiviCRM 4.5+.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules
+ */
+function _angularex_civix_civicrm_angularModules(&$angularModules) {
+  if (!is_dir(__DIR__ . '/ang')) {
+    return;
+  }
+
+  $files = _angularex_civix_glob(__DIR__ . '/ang/*.ang.php');
+  foreach ($files as $file) {
+    $name = preg_replace(':\.ang\.php$:', '', basename($file));
+    $module = include $file;
+    if (empty($module['ext'])) {
+      $module['ext'] = E::LONG_NAME;
+    }
+    $angularModules[$name] = $module;
+  }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_themes().
+ *
+ * Find any and return any files matching "*.theme.php"
+ */
+function _angularex_civix_civicrm_themes(&$themes) {
+  $files = _angularex_civix_glob(__DIR__ . '/*.theme.php');
+  foreach ($files as $file) {
+    $themeMeta = include $file;
+    if (empty($themeMeta['name'])) {
+      $themeMeta['name'] = preg_replace(':\.theme\.php$:', '', basename($file));
+    }
+    if (empty($themeMeta['ext'])) {
+      $themeMeta['ext'] = E::LONG_NAME;
+    }
+    $themes[$themeMeta['name']] = $themeMeta;
+  }
+}
+
 /**
  * Glob wrapper which is guaranteed to return an array.
  *
@@ -225,6 +361,7 @@ function _angularex_civix_civicrm_caseTypes(&$caseTypes) {
  *
  * @link http://php.net/glob
  * @param string $pattern
+ *
  * @return array, possibly empty
  */
 function _angularex_civix_glob($pattern) {
@@ -233,34 +370,24 @@ function _angularex_civix_glob($pattern) {
 }
 
 /**
- * Inserts a navigation menu item at a given place in the hierarchy
+ * Inserts a navigation menu item at a given place in the hierarchy.
+ *
+ * @param array $menu - menu hierarchy
+ * @param string $path - path to parent of this item, e.g. 'my_extension/submenu'
+ *    'Mailing', or 'Administer/System Settings'
+ * @param array $item - the item to insert (parent/child attributes will be
+ *    filled for you)
  *
- * $menu - menu hierarchy
- * $path - path where insertion should happen (ie. Administer/System Settings)
- * $item - menu you need to insert (parent/child attributes will be filled for you)
- * $parentId - used internally to recurse in the menu structure
- * @param $menu
- * @param $path
- * @param $item
- * @param null $parentId
  * @return bool
  */
-function _angularex_civix_insert_navigation_menu(&$menu, $path, $item, $parentId = NULL) {
-  static $navId;
-
+function _angularex_civix_insert_navigation_menu(&$menu, $path, $item) {
   // If we are done going down the path, insert menu
   if (empty($path)) {
-    if (!$navId) {
-      $navId = CRM_Core_DAO::singleValueQuery("SELECT max(id) FROM civicrm_navigation");
-    }
-    $navId++;
-    $menu[$navId] = [
-      'attributes' => array_merge($item, [
-        'label' => $item['name'] ?? NULL,
-        'active' => 1,
-        'parentID' => $parentId,
-        'navID' => $navId,
-      ]),
+    $menu[] = [
+      'attributes' => array_merge([
+        'label'      => CRM_Utils_Array::value('name', $item),
+        'active'     => 1,
+      ], $item),
     ];
     return TRUE;
   }
@@ -271,10 +398,10 @@ function _angularex_civix_insert_navigation_menu(&$menu, $path, $item, $parentId
     $first = array_shift($path);
     foreach ($menu as $key => &$entry) {
       if ($entry['attributes']['name'] == $first) {
-        if (!$entry['child']) {
+        if (!isset($entry['child'])) {
           $entry['child'] = [];
         }
-        $found = _angularex_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item, $key);
+        $found = _angularex_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item);
       }
     }
     return $found;
@@ -282,20 +409,69 @@ function _angularex_civix_insert_navigation_menu(&$menu, $path, $item, $parentId
 }
 
 /**
- * (Delegated) Implementation of hook_civicrm_alterSettingsFolders
- *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_alterSettingsFolders
- * @param null $metaDataFolders
+ * (Delegated) Implements hook_civicrm_navigationMenu().
  */
-function _angularex_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
-  static $configured = FALSE;
-  if ($configured) {
-    return;
+function _angularex_civix_navigationMenu(&$nodes) {
+  if (!is_callable(['CRM_Core_BAO_Navigation', 'fixNavigationMenu'])) {
+    _angularex_civix_fixNavigationMenu($nodes);
   }
-  $configured = TRUE;
+}
 
+/**
+ * Given a navigation menu, generate navIDs for any items which are
+ * missing them.
+ */
+function _angularex_civix_fixNavigationMenu(&$nodes) {
+  $maxNavID = 1;
+  array_walk_recursive($nodes, function($item, $key) use (&$maxNavID) {
+    if ($key === 'navID') {
+      $maxNavID = max($maxNavID, $item);
+    }
+  });
+  _angularex_civix_fixNavigationMenuItems($nodes, $maxNavID, NULL);
+}
+
+function _angularex_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID) {
+  $origKeys = array_keys($nodes);
+  foreach ($origKeys as $origKey) {
+    if (!isset($nodes[$origKey]['attributes']['parentID']) && $parentID !== NULL) {
+      $nodes[$origKey]['attributes']['parentID'] = $parentID;
+    }
+    // If no navID, then assign navID and fix key.
+    if (!isset($nodes[$origKey]['attributes']['navID'])) {
+      $newKey = ++$maxNavID;
+      $nodes[$origKey]['attributes']['navID'] = $newKey;
+      $nodes[$newKey] = $nodes[$origKey];
+      unset($nodes[$origKey]);
+      $origKey = $newKey;
+    }
+    if (isset($nodes[$origKey]['child']) && is_array($nodes[$origKey]['child'])) {
+      _angularex_civix_fixNavigationMenuItems($nodes[$origKey]['child'], $maxNavID, $nodes[$origKey]['attributes']['navID']);
+    }
+  }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_alterSettingsFolders().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterSettingsFolders
+ */
+function _angularex_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
   $settingsDir = __DIR__ . DIRECTORY_SEPARATOR . 'settings';
-  if (is_dir($settingsDir) && !in_array($settingsDir, $metaDataFolders)) {
+  if (!in_array($settingsDir, $metaDataFolders) && is_dir($settingsDir)) {
     $metaDataFolders[] = $settingsDir;
   }
 }
+
+/**
+ * (Delegated) Implements hook_civicrm_entityTypes().
+ *
+ * Find any *.entityType.php files, merge their content, and return.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
+ */
+
+function _angularex_civix_civicrm_entityTypes(&$entityTypes) {
+  $entityTypes = array_merge($entityTypes, array (
+  ));
+}