diff --git a/civicrm.php b/civicrm.php
index ec634adb085f8b7b48cae97d4809bd8ab16451e0..5463d699ebdd04ffdcc6d5478dbca46fd1f764ad 100644
--- a/civicrm.php
+++ b/civicrm.php
@@ -2,7 +2,7 @@
 /*
 Plugin Name: CiviCRM
 Description: CiviCRM - Growing and Sustaining Relationships
-Version: 5.29.1
+Version: 5.30.0
 Requires at least: 4.9
 Requires PHP:      7.1
 Author: CiviCRM LLC
@@ -56,7 +56,7 @@ if ( ! defined( 'ABSPATH' ) ) exit;
 
 
 // Set version here: when it changes, will force JS to reload
-define( 'CIVICRM_PLUGIN_VERSION', '5.29.1' );
+define( 'CIVICRM_PLUGIN_VERSION', '5.30.0' );
 
 // Store reference to this file
 if (!defined('CIVICRM_PLUGIN_FILE')) {
@@ -1304,7 +1304,7 @@ class CiviCRM_For_WordPress {
     );
     foreach ($setupPaths as $setupPath) {
       $loader = implode(DIRECTORY_SEPARATOR, [$civicrmCore, $setupPath, 'civicrm-setup-autoload.php']);
-      if (file_exists($civicrmCore . DIRECTORY_SEPARATOR . '.use-civicrm-setup') && file_exists($loader)) {
+      if (file_exists($loader)) {
         require_once $loader;
         require_once implode(DIRECTORY_SEPARATOR, [$civicrmCore, 'CRM', 'Core', 'ClassLoader.php']);
         CRM_Core_ClassLoader::singleton()->register();
@@ -1326,17 +1326,7 @@ class CiviCRM_For_WordPress {
       }
     }
 
-    // Uses CIVICRM_PLUGIN_DIR instead of WP_PLUGIN_DIR
-    $installFile =
-      CIVICRM_PLUGIN_DIR .
-      'civicrm' . DIRECTORY_SEPARATOR .
-      'install' . DIRECTORY_SEPARATOR .
-      'index.php';
-
-    // Notice: Undefined variable: siteDir in:
-    // CIVICRM_PLUGIN_DIR/civicrm/install/index.php on line 456
-    include ( $installFile );
-
+    wp_die( __( 'Installer unavailable. Failed to locate CiviCRM libraries.', 'civicrm' ) );
   }
 
 
diff --git a/civicrm/CRM/ACL/DAO/ACL.php b/civicrm/CRM/ACL/DAO/ACL.php
index df4002df2b74ea24d7a20847e3744663abc165bc..fa06cf96c5e2b4d421217ddb1cbfeab5b34ef913 100644
--- a/civicrm/CRM/ACL/DAO/ACL.php
+++ b/civicrm/CRM/ACL/DAO/ACL.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/ACL/ACL.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:d91adfb9ffb0a514d42f6828f1b95033)
+ * (GenCodeChecksum:f75eaa0ee87675c14a224ec22b2c30a7)
  */
 
 /**
  * Database access object for the ACL entity.
  */
 class CRM_ACL_DAO_ACL extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.6';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/ACL/DAO/ACLCache.php b/civicrm/CRM/ACL/DAO/ACLCache.php
index 9e7ede4882df4e32895c1cf7bfa0af98cb4db4d7..7e3ac8429f163e60b091c50dbd90983a661e5afc 100644
--- a/civicrm/CRM/ACL/DAO/ACLCache.php
+++ b/civicrm/CRM/ACL/DAO/ACLCache.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/ACL/ACLCache.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:0427e4f02009f7a66cf34ae18ef397aa)
+ * (GenCodeChecksum:cbf36d56ce734a5f7ceeb2071b68ebf8)
  */
 
 /**
  * Database access object for the ACLCache entity.
  */
 class CRM_ACL_DAO_ACLCache extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.6';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/ACL/DAO/EntityRole.php b/civicrm/CRM/ACL/DAO/EntityRole.php
index 0afa11cd52c78f5baf2351dde426af509f12e5e4..9130d307ae4c3e1ea10b6bc2a382f48eb3525386 100644
--- a/civicrm/CRM/ACL/DAO/EntityRole.php
+++ b/civicrm/CRM/ACL/DAO/EntityRole.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/ACL/EntityRole.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:2e8133b4c385feaa934b2d14ea40f971)
+ * (GenCodeChecksum:d985c951ef9a8872008576b41c1f2b9c)
  */
 
 /**
  * Database access object for the EntityRole entity.
  */
 class CRM_ACL_DAO_EntityRole extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.6';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Activity/BAO/Activity.php b/civicrm/CRM/Activity/BAO/Activity.php
index 3c8a0eb1551f2ee1c10baf75e16ccdfbd2b2d205..40f6fdc906656c530692d0d9f7449555c8a43adf 100644
--- a/civicrm/CRM/Activity/BAO/Activity.php
+++ b/civicrm/CRM/Activity/BAO/Activity.php
@@ -2164,7 +2164,6 @@ AND cl.modified_id  = c.id
       ],
     ];
     $fields = array_merge($activityFields, $exportableFields);
-    $fields['activity_type_id']['title'] = ts('Activity Type ID');
     $fields['activity_priority_id'] = $fields['priority_id'];
 
     if ($name === 'Case') {
diff --git a/civicrm/CRM/Activity/BAO/Query.php b/civicrm/CRM/Activity/BAO/Query.php
index 20dd823853c06cb64948ad0acfe03f51c391d9be..d432467e8d40c2ad7f176fff366af0aace58cb45 100644
--- a/civicrm/CRM/Activity/BAO/Query.php
+++ b/civicrm/CRM/Activity/BAO/Query.php
@@ -189,7 +189,7 @@ class CRM_Activity_BAO_Query {
         // We no longer expect "subject" as a specific criteria (as of CRM-19447),
         // but we still use activity_subject in Activity.Get API
       case 'activity_subject':
-        $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldSpec['where'], $op, $value, CRM_Utils_Type::typeToString($fieldSpec['type']));
+        $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldSpec['where'], $op, $value, $query->getDataTypeForRealField($name));
         $query->_qill[$grouping][]  = $query->getQillForField($fieldSpec['is_pseudofield_for'] ?? $fieldSpec['name'], $value, $op, $fieldSpec);
         break;
 
diff --git a/civicrm/CRM/Activity/DAO/Activity.php b/civicrm/CRM/Activity/DAO/Activity.php
index 141c390fa4e1faf3fa76879b4bfa3300ecb6cfac..03ad751cd6b738217b822db35492b5956a3b0996 100644
--- a/civicrm/CRM/Activity/DAO/Activity.php
+++ b/civicrm/CRM/Activity/DAO/Activity.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Activity/Activity.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:99193964db864f9ec91b02b2bbc2d8f4)
+ * (GenCodeChecksum:cbcbcbb6720f015deae4097b01196c9a)
  */
 
 /**
  * Database access object for the Activity entity.
  */
 class CRM_Activity_DAO_Activity extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
@@ -286,7 +288,7 @@ class CRM_Activity_DAO_Activity extends CRM_Core_DAO {
         'activity_type_id' => [
           'name' => 'activity_type_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Activity Type'),
+          'title' => ts('Activity Type ID'),
           'description' => ts('FK to civicrm_option_value.id, that has to be valid, registered activity type.'),
           'required' => TRUE,
           'import' => TRUE,
@@ -300,6 +302,7 @@ class CRM_Activity_DAO_Activity extends CRM_Core_DAO {
           'localizable' => 0,
           'html' => [
             'type' => 'Select',
+            'label' => ts("Activity Type"),
           ],
           'pseudoconstant' => [
             'optionGroupName' => 'activity_type',
@@ -389,7 +392,7 @@ class CRM_Activity_DAO_Activity extends CRM_Core_DAO {
         'phone_id' => [
           'name' => 'phone_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Phone (called)'),
+          'title' => ts('Phone (called) ID'),
           'description' => ts('Phone ID of the number called (optional - used if an existing phone number is selected).'),
           'where' => 'civicrm_activity.phone_id',
           'table_name' => 'civicrm_activity',
@@ -399,6 +402,7 @@ class CRM_Activity_DAO_Activity extends CRM_Core_DAO {
           'FKClassName' => 'CRM_Core_DAO_Phone',
           'html' => [
             'type' => 'EntityRef',
+            'label' => ts("Phone (called)"),
           ],
           'add' => '2.0',
         ],
diff --git a/civicrm/CRM/Activity/DAO/ActivityContact.php b/civicrm/CRM/Activity/DAO/ActivityContact.php
index f7bd52c8e50ee0b441d2ab060efc03b14a6e3ff2..61676f395b5f4f890305e51fdb8304fe70d48f02 100644
--- a/civicrm/CRM/Activity/DAO/ActivityContact.php
+++ b/civicrm/CRM/Activity/DAO/ActivityContact.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Activity/ActivityContact.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b458f04c2195968dbc9d49f35f7abb70)
+ * (GenCodeChecksum:777598f3625dfeaf37a81de282808c60)
  */
 
 /**
  * Database access object for the ActivityContact entity.
  */
 class CRM_Activity_DAO_ActivityContact extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Activity/Import/Form/MapField.php b/civicrm/CRM/Activity/Import/Form/MapField.php
index 51cb5aa42baf2fdded62fab58cc7ad3808d57473..a0688d9cffcc5d22456c4cf2894d53c86f625c9e 100644
--- a/civicrm/CRM/Activity/Import/Form/MapField.php
+++ b/civicrm/CRM/Activity/Import/Form/MapField.php
@@ -350,7 +350,7 @@ class CRM_Activity_Import_Form_MapField extends CRM_Import_Form_MapField {
     }
 
     $fileName = $this->controller->exportValue('DataSource', 'uploadFile');
-    $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
+    $separator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
 
     $mapperKeys = [];
@@ -431,7 +431,7 @@ class CRM_Activity_Import_Form_MapField extends CRM_Import_Form_MapField {
     }
 
     $parser = new CRM_Activity_Import_Parser_Activity($mapperKeysMain, $mapperLocType, $mapperPhoneType);
-    $parser->run($fileName, $seperator, $mapper, $skipColumnHeader,
+    $parser->run($fileName, $separator, $mapper, $skipColumnHeader,
       CRM_Import_Parser::MODE_PREVIEW
     );
 
diff --git a/civicrm/CRM/Activity/Import/Form/Preview.php b/civicrm/CRM/Activity/Import/Form/Preview.php
index 539c7162ab0a9d7c9b4454ac45db78434f821bc4..718f30626e4c092d912dacce5426321eb36d524e 100644
--- a/civicrm/CRM/Activity/Import/Form/Preview.php
+++ b/civicrm/CRM/Activity/Import/Form/Preview.php
@@ -92,7 +92,7 @@ class CRM_Activity_Import_Form_Preview extends CRM_Import_Form_Preview {
    */
   public function postProcess() {
     $fileName = $this->controller->exportValue('DataSource', 'uploadFile');
-    $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
+    $separator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
     $invalidRowCount = $this->get('invalidRowCount');
     $conflictRowCount = $this->get('conflictRowCount');
@@ -132,7 +132,7 @@ class CRM_Activity_Import_Form_Preview extends CRM_Import_Form_Preview {
       }
       $mapperFields[] = implode(' - ', $header);
     }
-    $parser->run($fileName, $seperator,
+    $parser->run($fileName, $separator,
       $mapperFields,
       $skipColumnHeader,
       CRM_Import_Parser::MODE_IMPORT,
diff --git a/civicrm/CRM/Activity/Import/Parser.php b/civicrm/CRM/Activity/Import/Parser.php
index d74f955ca08d97b75e28383cd92fc318a9e7a89c..9de96fd44d6d38dcadea86cd4b3a711b7ff61157 100644
--- a/civicrm/CRM/Activity/Import/Parser.php
+++ b/civicrm/CRM/Activity/Import/Parser.php
@@ -28,7 +28,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser {
    * Separator being used.
    * @var string
    */
-  protected $_seperator;
+  protected $_separator;
 
   /**
    * Total number of lines in file.
@@ -45,7 +45,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser {
 
   /**
    * @param string $fileName
-   * @param string $seperator
+   * @param string $separator
    * @param $mapper
    * @param bool $skipColumnHeader
    * @param int $mode
@@ -58,7 +58,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser {
    */
   public function run(
     $fileName,
-    $seperator = ',',
+    $separator = ',',
     &$mapper,
     $skipColumnHeader = FALSE,
     $mode = self::MODE_PREVIEW,
@@ -75,7 +75,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser {
 
     $this->_haveColumnHeader = $skipColumnHeader;
 
-    $this->_seperator = $seperator;
+    $this->_separator = $separator;
 
     $fd = fopen($fileName, "r");
     if (!$fd) {
@@ -106,7 +106,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser {
     while (!feof($fd)) {
       $this->_lineCount++;
 
-      $values = fgetcsv($fd, 8192, $seperator);
+      $values = fgetcsv($fd, 8192, $separator);
       if (!$values) {
         continue;
       }
@@ -327,7 +327,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser {
   public function set($store, $mode = self::MODE_SUMMARY) {
     $store->set('fileSize', $this->_fileSize);
     $store->set('lineCount', $this->_lineCount);
-    $store->set('seperator', $this->_seperator);
+    $store->set('separator', $this->_separator);
     $store->set('fields', $this->getSelectValues());
     $store->set('fieldTypes', $this->getSelectTypes());
 
diff --git a/civicrm/CRM/Admin/Form/Job.php b/civicrm/CRM/Admin/Form/Job.php
index 0f69059f7b3ec90eefc86173ac50b318b416f661..65bb09710e2ab6b2609258c8bada34cbe870435a 100644
--- a/civicrm/CRM/Admin/Form/Job.php
+++ b/civicrm/CRM/Admin/Form/Job.php
@@ -97,7 +97,7 @@ class CRM_Admin_Form_Job extends CRM_Admin_Form {
     $this->add('select', 'run_frequency', ts('Run frequency'), CRM_Core_SelectValues::getJobFrequency());
 
     // CRM-17686
-    $this->add('datepicker', 'scheduled_run_date', ts('Scheduled Run Date'), NULL, FALSE, ['minDate' => time()]);
+    $this->add('datepicker', 'scheduled_run_date', ts('Scheduled Run Date'), NULL, FALSE, ['minDate' => date('Y-m-d')]);
 
     $this->add('textarea', 'parameters', ts('Command parameters'),
       "cols=50 rows=6"
diff --git a/civicrm/CRM/Admin/Form/ScheduleReminders.php b/civicrm/CRM/Admin/Form/ScheduleReminders.php
index c881ef779387a483a93801c760b8f851937dff6b..f9ab1c98a6a2865b0b106cee88a4e598fb7f1843 100644
--- a/civicrm/CRM/Admin/Form/ScheduleReminders.php
+++ b/civicrm/CRM/Admin/Form/ScheduleReminders.php
@@ -250,7 +250,7 @@ class CRM_Admin_Form_ScheduleReminders extends CRM_Admin_Form {
     $this->addEntityRef('recipient_manual_id', ts('Manual Recipients'), ['multiple' => TRUE, 'create' => TRUE]);
 
     $this->add('select', 'group_id', ts('Group'),
-      CRM_Core_PseudoConstant::nestedGroup('Mailing'), FALSE, ['class' => 'crm-select2 huge']
+      CRM_Core_PseudoConstant::nestedGroup(), FALSE, ['class' => 'crm-select2 huge']
     );
 
     // multilingual only options
diff --git a/civicrm/CRM/Admin/Form/Setting/UF.php b/civicrm/CRM/Admin/Form/Setting/UF.php
index 62c12870f97426571511596029eb8003f01eec32..208b2f3cf16ad928300dae7789d418cf244f136a 100644
--- a/civicrm/CRM/Admin/Form/Setting/UF.php
+++ b/civicrm/CRM/Admin/Form/Setting/UF.php
@@ -61,7 +61,8 @@ class CRM_Admin_Form_Setting_UF extends CRM_Admin_Form_Setting {
         $config->dsn != $config->userFrameworkDSN || !empty($drupal_prefix)
       )
     ) {
-      $dsnArray = DB::parseDSN($config->dsn);
+      $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn);
+      $dsnArray = DB::parseDSN($dsn);
       $tableNames = CRM_Core_DAO::getTableNames();
       asort($tableNames);
       $tablePrefixes = '$databases[\'default\'][\'default\'][\'prefix\']= array(';
diff --git a/civicrm/CRM/Api4/Page/Api4Explorer.php b/civicrm/CRM/Api4/Page/Api4Explorer.php
index 3daba0733836d8b0966bde5fbf613d298bfc36f6..6de079bb8f157d879ffe80a539372fbebc5396ff 100644
--- a/civicrm/CRM/Api4/Page/Api4Explorer.php
+++ b/civicrm/CRM/Api4/Page/Api4Explorer.php
@@ -11,6 +11,7 @@
  */
 
 use Civi\Api4\Service\Schema\Joinable\Joinable;
+use Civi\Api4\Utils\CoreUtil;
 
 /**
  *
@@ -30,7 +31,7 @@ class CRM_Api4_Page_Api4Explorer extends CRM_Core_Page {
       });
     }
     $vars = [
-      'operators' => \CRM_Core_DAO::acceptedSQLOperators(),
+      'operators' => CoreUtil::getOperators(),
       'basePath' => Civi::resources()->getUrl('civicrm'),
       'schema' => (array) \Civi\Api4\Entity::get()->setChain(['fields' => ['$name', 'getFields']])->execute(),
       'links' => $entityLinks,
diff --git a/civicrm/CRM/Batch/DAO/Batch.php b/civicrm/CRM/Batch/DAO/Batch.php
index 995e7cd9617ba7b6713d1e37909673e56e7f420e..f2571b674f8361eb9a1e159e5e11bf42da969857 100644
--- a/civicrm/CRM/Batch/DAO/Batch.php
+++ b/civicrm/CRM/Batch/DAO/Batch.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Batch/Batch.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:062be2c63f724430f3a380c015fd6a02)
+ * (GenCodeChecksum:8169fc2f338afc4a163214c0018030be)
  */
 
 /**
  * Database access object for the Batch entity.
  */
 class CRM_Batch_DAO_Batch extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Batch/DAO/EntityBatch.php b/civicrm/CRM/Batch/DAO/EntityBatch.php
index 3bffdf0ada9807f935bd956f92dcf75050b3f1c6..6c697fb37073e0571bf4f1ca832bb11e512157dd 100644
--- a/civicrm/CRM/Batch/DAO/EntityBatch.php
+++ b/civicrm/CRM/Batch/DAO/EntityBatch.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Batch/EntityBatch.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:645843a03d3f8e9194ddd2bd79abf5c7)
+ * (GenCodeChecksum:6b6bd1337d9011c2a262de0e62c1e8e1)
  */
 
 /**
  * Database access object for the EntityBatch entity.
  */
 class CRM_Batch_DAO_EntityBatch extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Campaign/DAO/Campaign.php b/civicrm/CRM/Campaign/DAO/Campaign.php
index 44b832de2161b8260693a54501037aeba9f2205a..742996a6bde2c84ddbd4e249d59b731576110a99 100644
--- a/civicrm/CRM/Campaign/DAO/Campaign.php
+++ b/civicrm/CRM/Campaign/DAO/Campaign.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Campaign/Campaign.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:6b53e3fb452807fce31fc81bb2b09b38)
+ * (GenCodeChecksum:cfa77579eb9b91b31b6c5618b52c6e87)
  */
 
 /**
  * Database access object for the Campaign entity.
  */
 class CRM_Campaign_DAO_Campaign extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Campaign/DAO/CampaignGroup.php b/civicrm/CRM/Campaign/DAO/CampaignGroup.php
index 4dbb8b7f2d1dad664c1bba8836ba97c3b81fff9d..97d4abd5af1839b7b5a9c66f77eee82ca61cb31f 100644
--- a/civicrm/CRM/Campaign/DAO/CampaignGroup.php
+++ b/civicrm/CRM/Campaign/DAO/CampaignGroup.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Campaign/CampaignGroup.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a4fa6a19ef72119a316417521e209cd4)
+ * (GenCodeChecksum:74c02a4708ef706860d023c1635b98c4)
  */
 
 /**
  * Database access object for the CampaignGroup entity.
  */
 class CRM_Campaign_DAO_CampaignGroup extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Campaign/DAO/Survey.php b/civicrm/CRM/Campaign/DAO/Survey.php
index 62493531bf813079172122bc457d8bcdbf43b561..9c9626b6be8c714478feddfedc7d36e85ca458f4 100644
--- a/civicrm/CRM/Campaign/DAO/Survey.php
+++ b/civicrm/CRM/Campaign/DAO/Survey.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Campaign/Survey.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:80b92f90300f8e432ddd68e95d22b622)
+ * (GenCodeChecksum:e955546c8081852591bc08b1fdee4213)
  */
 
 /**
  * Database access object for the Survey entity.
  */
 class CRM_Campaign_DAO_Survey extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Campaign/Form/Campaign.php b/civicrm/CRM/Campaign/Form/Campaign.php
index 509b1278f45abbcbb61bcbcd487d43a398b8b364..b47cee77e72bb0c2755967c10b519a853ef44c02 100644
--- a/civicrm/CRM/Campaign/Form/Campaign.php
+++ b/civicrm/CRM/Campaign/Form/Campaign.php
@@ -222,7 +222,7 @@ class CRM_Campaign_Form_Campaign extends CRM_Core_Form {
     $this->add('wysiwyg', 'goal_general', ts('Campaign Goals'), ['rows' => 2, 'cols' => 40]);
     $this->add('text', 'goal_revenue', ts('Revenue Goal'), ['size' => 8, 'maxlength' => 12]);
     $this->addRule('goal_revenue', ts('Please enter a valid money value (e.g. %1).',
-      [1 => CRM_Utils_Money::format('99.99', ' ')]
+      [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency(99.99)]
     ), 'money');
 
     // is this Campaign active
diff --git a/civicrm/CRM/Case/DAO/Case.php b/civicrm/CRM/Case/DAO/Case.php
index 8f54a68d23d2117de2597ce1a84e59344f6f8e0e..946c992f4b6a509042b4d72c8d0387ff82a23fc4 100644
--- a/civicrm/CRM/Case/DAO/Case.php
+++ b/civicrm/CRM/Case/DAO/Case.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Case/Case.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ec6804103883c17fff63061e06d3e591)
+ * (GenCodeChecksum:8b18140da75bbf971a143c205f2af1cd)
  */
 
 /**
  * Database access object for the Case entity.
  */
 class CRM_Case_DAO_Case extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Case/DAO/CaseActivity.php b/civicrm/CRM/Case/DAO/CaseActivity.php
index a57d2a9bd1f7bb1b8022315557d2da43c7ecd26a..9efe7b4af3e553cc554516347cdc9e0899f848ad 100644
--- a/civicrm/CRM/Case/DAO/CaseActivity.php
+++ b/civicrm/CRM/Case/DAO/CaseActivity.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Case/CaseActivity.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:d2ef4c48307f130ba531ff5c4bfb38d6)
+ * (GenCodeChecksum:974d18e84d3416c98293bedd66c3384c)
  */
 
 /**
  * Database access object for the CaseActivity entity.
  */
 class CRM_Case_DAO_CaseActivity extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Case/DAO/CaseContact.php b/civicrm/CRM/Case/DAO/CaseContact.php
index df46165d1500b7733aea1b05d3d7d43a9bfaa0e5..788b1e58f94810e78b6e6978c1b21fff9b038adc 100644
--- a/civicrm/CRM/Case/DAO/CaseContact.php
+++ b/civicrm/CRM/Case/DAO/CaseContact.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Case/CaseContact.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:4a71399455a593008da4849db726f7b9)
+ * (GenCodeChecksum:b315f42d7c886c123c9e87c9713c4911)
  */
 
 /**
  * Database access object for the CaseContact entity.
  */
 class CRM_Case_DAO_CaseContact extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Case/DAO/CaseType.php b/civicrm/CRM/Case/DAO/CaseType.php
index 039a9582440f0efccb1a6268ba037e2566f77707..dbafcb30a8dbcafeaa5a72f5ee7ceddbcba93f0b 100644
--- a/civicrm/CRM/Case/DAO/CaseType.php
+++ b/civicrm/CRM/Case/DAO/CaseType.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Case/CaseType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:c65e3b1aa03fb9ad3122915d612fefb8)
+ * (GenCodeChecksum:cde81a56b0e8201eac521b92ded6fb45)
  */
 
 /**
  * Database access object for the CaseType entity.
  */
 class CRM_Case_DAO_CaseType extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Case/Form/CaseView.php b/civicrm/CRM/Case/Form/CaseView.php
index 646d48cfd054ea71f88872196b320a05d7ca3691..7c9724a53c83671ca4ee97eec12fc5168257bb3c 100644
--- a/civicrm/CRM/Case/Form/CaseView.php
+++ b/civicrm/CRM/Case/Form/CaseView.php
@@ -272,6 +272,9 @@ class CRM_Case_Form_CaseView extends CRM_Core_Form {
         ['class' => 'crm-select2 crm-action-menu fa-list-ol']
       );
     }
+    // This button is hidden but gets clicked by javascript at
+    // https://github.com/civicrm/civicrm-core/blob/bd28ecf8121a85bc069cad3ab912a0c3dff8fdc5/templates/CRM/Case/Form/CaseView.js#L194
+    // by the onChange handler for the above timeline_id select.
     $this->addElement('submit', $this->getButtonName('next'), ' ', ['class' => 'hiddenElement']);
 
     $this->buildMergeCaseForm();
@@ -517,6 +520,9 @@ class CRM_Case_Form_CaseView extends CRM_Core_Form {
         FALSE,
         ['class' => 'crm-select2 huge']
       );
+      // This button is hidden but gets clicked by javascript at
+      // https://github.com/civicrm/civicrm-core/blob/bd28ecf8121a85bc069cad3ab912a0c3dff8fdc5/templates/CRM/Case/Form/CaseView.js#L55
+      // when the mergeCasesDialog is saved.
       $this->addElement('submit',
         $this->getButtonName('next', 'merge_case'),
         ts('Merge'),
diff --git a/civicrm/CRM/Contact/BAO/Contact.php b/civicrm/CRM/Contact/BAO/Contact.php
index 6a105253d665f68107de1880df9d89d613b19ada..5f42dce4faa5665af235fc1995c7eafffdc13f94 100644
--- a/civicrm/CRM/Contact/BAO/Contact.php
+++ b/civicrm/CRM/Contact/BAO/Contact.php
@@ -1562,9 +1562,13 @@ WHERE     civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer');
           ],
         ];
 
+        $phoneFields = CRM_Core_DAO_Phone::export();
+        // This adds phone_type to the exportable fields and make it available for export.
+        // with testing the same can be done to the other entities.
+        CRM_Core_DAO::appendPseudoConstantsToFields($phoneFields);
         $locationFields = array_merge($locationType,
           CRM_Core_DAO_Address::export(),
-          CRM_Core_DAO_Phone::export(),
+          $phoneFields,
           CRM_Core_DAO_Email::export(),
           $IMProvider,
           CRM_Core_DAO_IM::export(TRUE),
@@ -1605,7 +1609,6 @@ WHERE     civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer');
             );
           }
         }
-        $fields['current_employer_id']['title'] = ts('Current Employer ID');
         //fix for CRM-791
         if ($export) {
           $fields = array_merge($fields, [
diff --git a/civicrm/CRM/Contact/BAO/ContactType.php b/civicrm/CRM/Contact/BAO/ContactType.php
index 4284efa467c118c03dc8eac2e2887a8030f6fe0e..18b62ab86e1d26007865ed65a5ce15ad17d3dbe7 100644
--- a/civicrm/CRM/Contact/BAO/ContactType.php
+++ b/civicrm/CRM/Contact/BAO/ContactType.php
@@ -45,9 +45,11 @@ class CRM_Contact_BAO_ContactType extends CRM_Contact_DAO_ContactType {
    * @param string $contactType
    *
    * @return bool
+   *
+   * @throws \API_Exception
    */
   public static function isActive($contactType) {
-    $contact = self::contactTypeInfo(FALSE);
+    $contact = self::contactTypeInfo();
     return array_key_exists($contactType, $contact);
   }
 
@@ -113,51 +115,25 @@ class CRM_Contact_BAO_ContactType extends CRM_Contact_DAO_ContactType {
   /**
    * Retrieve all subtypes Information.
    *
-   * @todo - call getAllContactTypes & return filtered results.
-   *
    * @param array $contactType
-   *   ..
    * @param bool $all
-   * @param bool $ignoreCache
    *
    * @return array
-   *   Array of sub type information
+   *   Array of sub type information, subset of getAllContactTypes.
+   *
+   * @throws \API_Exception
    */
-  public static function subTypeInfo($contactType = NULL, $all = FALSE, $ignoreCache = FALSE) {
-    $argString = $all ? 'CRM_CT_STI_1_' : 'CRM_CT_STI_0_';
-    if (!empty($contactType)) {
-      $contactType = (array) $contactType;
-      $argString .= implode('_', $contactType);
-    }
-    if (!Civi::cache('contactTypes')->has($argString) || $ignoreCache) {
-      $ctWHERE = '';
-      if (!empty($contactType)) {
-        $ctWHERE = " AND parent.name IN ('" . implode("','", $contactType) . "')";
-      }
-
-      $sql = "
-SELECT subtype.*, parent.name as parent, parent.label as parent_label
-FROM   civicrm_contact_type subtype
-INNER JOIN civicrm_contact_type parent ON subtype.parent_id = parent.id
-WHERE  subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE}
-";
-      if ($all === FALSE) {
-        $sql .= " AND subtype.is_active = 1 AND parent.is_active = 1 ORDER BY parent.id";
-      }
-      $dao = CRM_Core_DAO::executeQuery($sql, [],
-        FALSE, 'CRM_Contact_DAO_ContactType'
-      );
-      $values = [];
-      while ($dao->fetch()) {
-        $value = [];
-        CRM_Core_DAO::storeValues($dao, $value);
-        $value['parent'] = $dao->parent;
-        $value['parent_label'] = $dao->parent_label;
-        $values[$dao->name] = $value;
+  public static function subTypeInfo($contactType = NULL, $all = FALSE) {
+    $contactTypes = self::getAllContactTypes();
+    foreach ($contactTypes as $index => $type) {
+      if (empty($type['parent']) ||
+        (!$all && !$type['is_active'])
+        || ($contactType && $type['parent'] !== $contactType)
+      ) {
+        unset($contactTypes[$index]);
       }
-      Civi::cache('contactTypes')->set($argString, $values);
     }
-    return Civi::cache('contactTypes')->get($argString);
+    return $contactTypes;
   }
 
   /**
@@ -173,6 +149,7 @@ WHERE  subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE}
    * @return array
    *   all subtypes OR list of subtypes associated to
    *   a given basic contact type
+   * @throws \API_Exception
    */
   public static function subTypes($contactType = NULL, $all = FALSE, $columnName = 'name', $ignoreCache = FALSE) {
     if ($columnName === 'name') {
diff --git a/civicrm/CRM/Contact/BAO/Query.php b/civicrm/CRM/Contact/BAO/Query.php
index a6b2545d8bfc72265a4d1feaa3992befbc5771c6..3fe9a97fc22241580994ed26bd2d46541be9cbb4 100644
--- a/civicrm/CRM/Contact/BAO/Query.php
+++ b/civicrm/CRM/Contact/BAO/Query.php
@@ -1121,6 +1121,9 @@ class CRM_Contact_BAO_Query {
         }
 
         $field = $this->_fields[$elementName] ?? NULL;
+        if (isset($this->_pseudoConstantsSelect[$field['name']])) {
+          $this->_pseudoConstantsSelect[$name . '-' . $field['name']] = $this->_pseudoConstantsSelect[$field['name']];
+        }
 
         // hack for profile, add location id
         if (!$field) {
@@ -2186,7 +2189,7 @@ class CRM_Contact_BAO_Query {
         $name, $op, $value, $grouping,
         'CRM_Contact_DAO_Contact',
         $field,
-        $field['title'],
+        $field['html']['label'] ?? $field['title'],
         CRM_Utils_Type::typeToString($dataType)
       );
       if ($name === 'gender_id') {
@@ -6938,6 +6941,17 @@ AND   displayRelType.is_active = 1
     return $field;
   }
 
+  /**
+   * Get the field datatype, using the type in the database rather than the pseudofield, if a pseudofield.
+   *
+   * @param string $fieldName
+   *
+   * @return string
+   */
+  public function getDataTypeForRealField($fieldName) {
+    return CRM_Utils_Type::typeToString($this->getMetadataForRealField($fieldName)['type']);
+  }
+
   /**
    * If we have a field that is better rendered via the pseudoconstant handled them here.
    *
diff --git a/civicrm/CRM/Contact/DAO/ACLContactCache.php b/civicrm/CRM/Contact/DAO/ACLContactCache.php
index b1bf5bdd03645f913e4a6639a149df48d81464e9..6b319f9636ce77e248b306c203b4f9451a161a5a 100644
--- a/civicrm/CRM/Contact/DAO/ACLContactCache.php
+++ b/civicrm/CRM/Contact/DAO/ACLContactCache.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/ACLContactCache.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b99253b18e66856d4b6d914f2669fa30)
+ * (GenCodeChecksum:97d9be5e13ece64b6c9ad1722d9bca68)
  */
 
 /**
  * Database access object for the ACLContactCache entity.
  */
 class CRM_Contact_DAO_ACLContactCache extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/Contact.php b/civicrm/CRM/Contact/DAO/Contact.php
index 08668b27399710d9e094cf89331f237ad363639e..f91dc3264b1e5e35928815f8b2b63d80434c65c3 100644
--- a/civicrm/CRM/Contact/DAO/Contact.php
+++ b/civicrm/CRM/Contact/DAO/Contact.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/Contact.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:da59563e28c0229228f7c2baa20caabf)
+ * (GenCodeChecksum:f58884560d4f49764182cd97f1bbbcdf)
  */
 
 /**
  * Database access object for the Contact entity.
  */
 class CRM_Contact_DAO_Contact extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
@@ -1214,7 +1216,7 @@ class CRM_Contact_DAO_Contact extends CRM_Core_DAO {
         'gender_id' => [
           'name' => 'gender_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Gender'),
+          'title' => ts('Gender ID'),
           'description' => ts('FK to gender ID'),
           'import' => TRUE,
           'where' => 'civicrm_contact.gender_id',
@@ -1227,6 +1229,7 @@ class CRM_Contact_DAO_Contact extends CRM_Core_DAO {
           'localizable' => 0,
           'html' => [
             'type' => 'Select',
+            'label' => ts("Gender"),
           ],
           'pseudoconstant' => [
             'optionGroupName' => 'gender',
@@ -1399,7 +1402,7 @@ class CRM_Contact_DAO_Contact extends CRM_Core_DAO {
         'current_employer_id' => [
           'name' => 'employer_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Current Employer'),
+          'title' => ts('Current Employer ID'),
           'description' => ts('OPTIONAL FK to civicrm_contact record.'),
           'where' => 'civicrm_contact.employer_id',
           'export' => TRUE,
@@ -1411,6 +1414,7 @@ class CRM_Contact_DAO_Contact extends CRM_Core_DAO {
           'FKClassName' => 'CRM_Contact_DAO_Contact',
           'html' => [
             'type' => 'EntityRef',
+            'label' => ts("Current Employer"),
           ],
           'add' => '2.1',
         ],
diff --git a/civicrm/CRM/Contact/DAO/ContactType.php b/civicrm/CRM/Contact/DAO/ContactType.php
index 0237e0943dea7f0055c0ad7abd1f11af8b83c05d..41d8b878036906d1e0e34153eaefbde39dfc34a6 100644
--- a/civicrm/CRM/Contact/DAO/ContactType.php
+++ b/civicrm/CRM/Contact/DAO/ContactType.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/ContactType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:34f2ee23181ddce3050484b6e30b0e72)
+ * (GenCodeChecksum:0f7546e10f09f7637d50f7a34c632cb5)
  */
 
 /**
  * Database access object for the ContactType entity.
  */
 class CRM_Contact_DAO_ContactType extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/DashboardContact.php b/civicrm/CRM/Contact/DAO/DashboardContact.php
index 360c5caf71d43a8fdcc0af811ffd331a825981a6..16c0b3b593796c93f952a45700125efe91740eaa 100644
--- a/civicrm/CRM/Contact/DAO/DashboardContact.php
+++ b/civicrm/CRM/Contact/DAO/DashboardContact.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/DashboardContact.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:c6084fad33e2fed9634eee46cfcfd4ee)
+ * (GenCodeChecksum:67153c09e74eda2febf15986f9c04439)
  */
 
 /**
  * Database access object for the DashboardContact entity.
  */
 class CRM_Contact_DAO_DashboardContact extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/Group.php b/civicrm/CRM/Contact/DAO/Group.php
index 3c051ec4f49442505966446c8f6f54a0f2b66981..cdae401986356649bb6561599060cea33203d5e1 100644
--- a/civicrm/CRM/Contact/DAO/Group.php
+++ b/civicrm/CRM/Contact/DAO/Group.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/Group.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a8a3812a392b7544441cdbe1f9500f21)
+ * (GenCodeChecksum:6a2a222c5fa5b461727bb95379723b08)
  */
 
 /**
  * Database access object for the Group entity.
  */
 class CRM_Contact_DAO_Group extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/GroupContact.php b/civicrm/CRM/Contact/DAO/GroupContact.php
index 0605b3a09f0e941234ac67eebfe0fcd855a9b3a4..52c66fd638c930609b4d353de13d309c20b7612d 100644
--- a/civicrm/CRM/Contact/DAO/GroupContact.php
+++ b/civicrm/CRM/Contact/DAO/GroupContact.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/GroupContact.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:11947c2ac6a20781c2daa15b0f894983)
+ * (GenCodeChecksum:143ba4d95cae73fc81c8e932970cbc1f)
  */
 
 /**
  * Database access object for the GroupContact entity.
  */
 class CRM_Contact_DAO_GroupContact extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/GroupContactCache.php b/civicrm/CRM/Contact/DAO/GroupContactCache.php
index 19c2f738cf3c9313df5478a267ac14ab0d1295f0..3ee02aa7e2097f2ab0801e4452706d194b555b11 100644
--- a/civicrm/CRM/Contact/DAO/GroupContactCache.php
+++ b/civicrm/CRM/Contact/DAO/GroupContactCache.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/GroupContactCache.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:78446b0939b21d995dd17476d11030e7)
+ * (GenCodeChecksum:a169b776ec9bfc8864a05750d4ae6b95)
  */
 
 /**
  * Database access object for the GroupContactCache entity.
  */
 class CRM_Contact_DAO_GroupContactCache extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/GroupNesting.php b/civicrm/CRM/Contact/DAO/GroupNesting.php
index 2f6a61b8f5e129fc08e261717ab675196ea2e0ba..90624a6e98fdb749120d4626376b6d438ead8716 100644
--- a/civicrm/CRM/Contact/DAO/GroupNesting.php
+++ b/civicrm/CRM/Contact/DAO/GroupNesting.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/GroupNesting.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:7e5dfd763c2f4b39ff7f3ea962269bae)
+ * (GenCodeChecksum:0ca7da77b0229b439c9c3a4c4c2e4326)
  */
 
 /**
  * Database access object for the GroupNesting entity.
  */
 class CRM_Contact_DAO_GroupNesting extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/GroupOrganization.php b/civicrm/CRM/Contact/DAO/GroupOrganization.php
index 49f1bd8616e46608fdb84da2acda026adc917107..407ea31d83f505e3f82cbe9c0c2f7fa273f8c6bd 100644
--- a/civicrm/CRM/Contact/DAO/GroupOrganization.php
+++ b/civicrm/CRM/Contact/DAO/GroupOrganization.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/GroupOrganization.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:59e2d9b6cb5577d3f8a72422fb33f643)
+ * (GenCodeChecksum:dfe8edf8f786790af95f09f456d1cbe7)
  */
 
 /**
  * Database access object for the GroupOrganization entity.
  */
 class CRM_Contact_DAO_GroupOrganization extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/Relationship.php b/civicrm/CRM/Contact/DAO/Relationship.php
index 5110814fe3c2d0955a7aab7029a79f07662c954c..14b10a52802e77892254edd2063d4583955aaac6 100644
--- a/civicrm/CRM/Contact/DAO/Relationship.php
+++ b/civicrm/CRM/Contact/DAO/Relationship.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/Relationship.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b1e83f9c102db77881c4b9c730f38337)
+ * (GenCodeChecksum:25faea8225f483ae95cf29af08a8542d)
  */
 
 /**
  * Database access object for the Relationship entity.
  */
 class CRM_Contact_DAO_Relationship extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/RelationshipCache.php b/civicrm/CRM/Contact/DAO/RelationshipCache.php
index ddb541bb70277782d78e1d1712f5c2273111f9e8..cfa5a75bc2e0ec936dd9198202d1190c36cef8e6 100644
--- a/civicrm/CRM/Contact/DAO/RelationshipCache.php
+++ b/civicrm/CRM/Contact/DAO/RelationshipCache.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/RelationshipCache.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:d511533f30d5d2d8deac82664a6288d1)
+ * (GenCodeChecksum:b40781c15c3351a766a6083522f0e5e4)
  */
 
 /**
  * Database access object for the RelationshipCache entity.
  */
 class CRM_Contact_DAO_RelationshipCache extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '5.29';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/RelationshipType.php b/civicrm/CRM/Contact/DAO/RelationshipType.php
index 06ec7aff101aac07bcfc13feac8c14e07e5f3943..e703e2fe849b78e5065561a73fa2e65504b74f88 100644
--- a/civicrm/CRM/Contact/DAO/RelationshipType.php
+++ b/civicrm/CRM/Contact/DAO/RelationshipType.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/RelationshipType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:205fa87e1f397fbf810ffbbd74c1853a)
+ * (GenCodeChecksum:6e9767fcd0fc6eba8fcd408588fe0755)
  */
 
 /**
  * Database access object for the RelationshipType entity.
  */
 class CRM_Contact_DAO_RelationshipType extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/SavedSearch.php b/civicrm/CRM/Contact/DAO/SavedSearch.php
index 0d3dcbbedd00e0bb963abc455a522b5fab111a8a..a5fb2d460375d06c57bd1bcc742f8c05b9643cc4 100644
--- a/civicrm/CRM/Contact/DAO/SavedSearch.php
+++ b/civicrm/CRM/Contact/DAO/SavedSearch.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/SavedSearch.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:4093fd1cfa684e54d609528d8df160ca)
+ * (GenCodeChecksum:4b2f292a8196a5dc4a73afc078cd11cb)
  */
 
 /**
  * Database access object for the SavedSearch entity.
  */
 class CRM_Contact_DAO_SavedSearch extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/DAO/SubscriptionHistory.php b/civicrm/CRM/Contact/DAO/SubscriptionHistory.php
index d476dd3ebb88c6cc60775b812162d5ec15f662bd..421ea3381555f30664b05db8bf3b608b8874a1f4 100644
--- a/civicrm/CRM/Contact/DAO/SubscriptionHistory.php
+++ b/civicrm/CRM/Contact/DAO/SubscriptionHistory.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contact/SubscriptionHistory.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:4aa1411720f09f9ad8e2feb2a8d2a833)
+ * (GenCodeChecksum:7c033b0631f14da30172883b14686574)
  */
 
 /**
  * Database access object for the SubscriptionHistory entity.
  */
 class CRM_Contact_DAO_SubscriptionHistory extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contact/Form/Search/Custom/Proximity.php b/civicrm/CRM/Contact/Form/Search/Custom/Proximity.php
index 34026c06de81f75eabe65e691b85df004bbdacf1..8d211aafcf7b5087a8fdcc6eb588d86c8baa5cbc 100644
--- a/civicrm/CRM/Contact/Form/Search/Custom/Proximity.php
+++ b/civicrm/CRM/Contact/Form/Search/Custom/Proximity.php
@@ -19,9 +19,6 @@
  */
 class CRM_Contact_Form_Search_Custom_Proximity extends CRM_Contact_Form_Search_Custom_Base implements CRM_Contact_Form_Search_Interface {
 
-  protected $_latitude = NULL;
-  protected $_longitude = NULL;
-  protected $_distance = NULL;
   protected $_aclFrom = NULL;
   protected $_aclWhere = NULL;
 
@@ -39,21 +36,6 @@ class CRM_Contact_Form_Search_Custom_Proximity extends CRM_Contact_Form_Search_C
     unset($this->_formValues['uf_group_id']);
     unset($this->_formValues['component_mode']);
     unset($this->_formValues['operator']);
-
-    if (!empty($this->_formValues)) {
-      // add the country and state
-      self::addGeocodingData($this->_formValues);
-      $this->_latitude = $this->_formValues['geo_code_1'];
-      $this->_longitude = $this->_formValues['geo_code_2'];
-
-      if ($this->_formValues['prox_distance_unit'] == "miles") {
-        $conversionFactor = 1609.344;
-      }
-      else {
-        $conversionFactor = 1000;
-      }
-      $this->_distance = $this->_formValues['distance'] * $conversionFactor;
-    }
     $this->_group = $this->_formValues['group'] ?? NULL;
 
     $this->_tag = $this->_formValues['tag'] ?? NULL;
@@ -192,6 +174,10 @@ class CRM_Contact_Form_Search_Custom_Proximity extends CRM_Contact_Form_Search_C
       $isCountOnly = TRUE;
     }
 
+    if (empty($this->_formValues['geo_code_1']) ||  empty($this->_formValues['geo_code_2'])) {
+      self::addGeocodingData($this->_formValues);
+    }
+
     $searchParams = [
       ['prox_distance_unit', '=', $this->_formValues['prox_distance_unit'], 0, 0],
       ['prox_distance', '=', $this->_formValues['distance'], 0, 0],
diff --git a/civicrm/CRM/Contribute/BAO/Contribution.php b/civicrm/CRM/Contribute/BAO/Contribution.php
index e5d83dd6cfeb24d22509b38c493b2a54a3339bc8..238c4f196d3c95f0e3150a132c6c49cb19dfb48a 100644
--- a/civicrm/CRM/Contribute/BAO/Contribution.php
+++ b/civicrm/CRM/Contribute/BAO/Contribution.php
@@ -847,8 +847,6 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
         CRM_Core_BAO_CustomField::getFieldsForImport('Contribution', FALSE, FALSE, FALSE, $checkPermission)
       );
 
-      $fields['financial_type_id']['title'] = ts('Financial Type ID');
-
       self::$_exportableFields = $fields;
     }
 
@@ -2613,7 +2611,7 @@ LEFT JOIN  civicrm_contribution contribution ON ( componentPayment.contribution_
    * @return bool|array
    * @throws CiviCRM_API3_Exception
    */
-  protected static function repeatTransaction(&$contribution, &$input, $contributionParams) {
+  protected static function repeatTransaction(&$contribution, $input, $contributionParams) {
     if (!empty($contribution->id)) {
       return FALSE;
     }
@@ -4286,14 +4284,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
       $params['tax_amount'] = array_sum($taxAmountArray);
       $params['total_amount'] = $params['total_amount'] + $params['tax_amount'];
     }
-    else {
-      // update line item of contrbution
-      if (isset($params['financial_type_id']) && array_key_exists($params['financial_type_id'], $taxRates) && $isLineItem) {
-        $taxRate = $taxRates[$params['financial_type_id']];
-        $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($params['line_total'], $taxRate);
-        $params['tax_amount'] = round($taxAmount['tax_amount'], 2);
-      }
-    }
+
     return $params;
   }
 
@@ -4452,10 +4443,18 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
    * @throws \CRM_Core_Exception
    * @throws \CiviCRM_API3_Exception
    */
-  public static function completeOrder($input, &$ids, $objects, $isPostPaymentCreate = FALSE) {
+  public static function completeOrder($input, $ids, $objects, $isPostPaymentCreate = FALSE) {
     $transaction = new CRM_Core_Transaction();
     $contribution = $objects['contribution'];
-    $primaryContributionID = $contribution->id ?? $objects['first_contribution']->id;
+    // @todo see if we even need this - it's used further down to create an activity
+    // but the BAO layer should create that - we just need to add a test to cover it & can
+    // maybe remove $ids altogether.
+    $contributionContactID = $ids['related_contact'];
+    $participantID = $ids['participant'];
+    $recurringContributionID = $ids['contributionRecur'];
+
+    // Unset ids just to make it clear it's not used again.
+    unset($ids);
     // The previous details are used when calculating line items so keep it before any code that 'does something'
     if (!empty($contribution->id)) {
       $input['prevContribution'] = CRM_Contribute_BAO_Contribution::getValues(['id' => $contribution->id]);
@@ -4476,9 +4475,6 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
       'financial_type_id',
     ];
 
-    $participant = $objects['participant'] ?? NULL;
-    $recurContrib = $objects['contributionRecur'] ?? NULL;
-    $recurringContributionID = (empty($recurContrib->id)) ? NULL : $recurContrib->id;
     $event = $objects['event'] ?? NULL;
 
     $paymentProcessorId = '';
@@ -4499,7 +4495,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
     ], array_intersect_key($input, array_fill_keys($inputContributionWhiteList, 1)
     ));
 
-    $contributionParams['payment_processor'] = $input['payment_processor'] = $paymentProcessorId;
+    $contributionParams['payment_processor'] = $paymentProcessorId;
 
     // If paymentProcessor is not set then the payment_instrument_id would not be correct.
     // not clear when or if this would occur if you encounter this please fix here & add a unit test.
@@ -4524,11 +4520,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
     }
     else {
       if (empty($input['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'])) {
-        if ($event->is_email_confirm) {
-          // @todo this should be set by the function that sends the mail after sending.
-          $contributionParams['receipt_date'] = $changeDate;
-        }
-        $participantParams['id'] = $participant->id;
+        $participantParams['id'] = $participantID;
         $participantParams['status_id'] = 'Registered';
         civicrm_api3('Participant', 'create', $participantParams);
       }
@@ -4542,16 +4534,10 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
     }
 
     // Add new soft credit against current $contribution.
-    if (!empty($objects['contributionRecur']) && $objects['contributionRecur']->id) {
-      CRM_Contribute_BAO_ContributionRecur::addrecurSoftCredit($objects['contributionRecur']->id, $contribution->id);
+    if ($recurringContributionID) {
+      CRM_Contribute_BAO_ContributionRecur::addrecurSoftCredit($recurringContributionID, $contribution->id);
     }
 
-    if (empty($contribution->_relatedObjects['participant']) && !empty($contribution->_relatedObjects['membership'])) {
-      // @fixme Can we remove this if altogether? - we removed the participant if / else and left relatedObjects['participant'] to ensure behaviour didn't change but it is probably not required.
-      // @todo - use getRelatedMemberships instead
-      $contribution->trxn_id = $input['trxn_id'] ?? NULL;
-      $contribution->receive_date = CRM_Utils_Date::isoToMysql($contribution->receive_date);
-    }
     $contribution->contribution_status_id = $contributionParams['contribution_status_id'];
 
     CRM_Core_Error::debug_log_message('Contribution record updated successfully');
@@ -4566,9 +4552,9 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
     if ($input['component'] == 'contribute') {
       //CRM-4027
       $targetContactID = NULL;
-      if (!empty($ids['related_contact'])) {
+      if ($contributionContactID) {
         $targetContactID = $contribution->contact_id;
-        $contribution->contact_id = $ids['related_contact'];
+        $contribution->contact_id = $contributionContactID;
       }
       CRM_Activity_BAO_Activity::addActivity($contribution, NULL, $targetContactID);
     }
@@ -4714,40 +4700,6 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
     return $ids;
   }
 
-  /**
-   * This function is used to record partial payments for contribution
-   *
-   * @param array $contribution
-   *
-   * @param array $params
-   *
-   * @return CRM_Financial_DAO_FinancialTrxn
-   */
-  public static function recordPartialPayment($contribution, $params) {
-    CRM_Core_Error::deprecatedFunctionWarning('use payment create api');
-    $balanceTrxnParams['to_financial_account_id'] = self::getToFinancialAccount($contribution, $params);
-    $balanceTrxnParams['from_financial_account_id'] = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($contribution['financial_type_id'], 'Accounts Receivable Account is');
-    $balanceTrxnParams['total_amount'] = $params['total_amount'];
-    $balanceTrxnParams['contribution_id'] = $params['contribution_id'];
-    $balanceTrxnParams['trxn_date'] = CRM_Utils_Array::value('trxn_date', $params, CRM_Utils_Array::value('contribution_receive_date', $params, date('YmdHis')));
-    $balanceTrxnParams['fee_amount'] = $params['fee_amount'] ?? NULL;
-    $balanceTrxnParams['net_amount'] = $params['total_amount'] ?? NULL;
-    $balanceTrxnParams['currency'] = $contribution['currency'];
-    $balanceTrxnParams['trxn_id'] = $params['contribution_trxn_id'] ?? NULL;
-    $balanceTrxnParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_FinancialTrxn', 'status_id', 'Completed');
-    $balanceTrxnParams['payment_instrument_id'] = CRM_Utils_Array::value('payment_instrument_id', $params, $contribution['payment_instrument_id']);
-    $balanceTrxnParams['check_number'] = $params['check_number'] ?? NULL;
-    $balanceTrxnParams['is_payment'] = 1;
-
-    if (!empty($params['payment_processor'])) {
-      // I can't find evidence this is passed in - I was gonna just remove it but decided to deprecate  as I see self::getToFinancialAccount
-      // also anticipates it.
-      CRM_Core_Error::deprecatedFunctionWarning('passing payment_processor is deprecated - use payment_processor_id');
-      $balanceTrxnParams['payment_processor_id'] = $params['payment_processor'];
-    }
-    return CRM_Core_BAO_FinancialTrxn::create($balanceTrxnParams);
-  }
-
   /**
    * Get the description (source field) for the recurring contribution.
    *
@@ -4820,7 +4772,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
         if (empty($item['financial_type_id'])) {
           $item['financial_type_id'] = $params['financial_type_id'];
         }
-        $lineItemAmount += $item['line_total'] + CRM_Utils_Array::value('tax_amount', $item, 0.00);
+        $lineItemAmount += $item['line_total'] + ($item['tax_amount'] ?? 0.00);
       }
     }
 
@@ -4828,11 +4780,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
       $params['total_amount'] = $lineItemAmount;
     }
     else {
-      $currency = CRM_Utils_Array::value('currency', $params, '');
-
-      if (empty($currency)) {
-        $currency = CRM_Core_Config::singleton()->defaultCurrency;
-      }
+      $currency = $params['currency'] ?? CRM_Core_Config::singleton()->defaultCurrency;
 
       if (!CRM_Utils_Money::equals($totalAmount, $lineItemAmount, $currency)) {
         throw new CRM_Contribute_Exception_CheckLineItemsException();
diff --git a/civicrm/CRM/Contribute/BAO/Query.php b/civicrm/CRM/Contribute/BAO/Query.php
index 2925909affaf14afd5ef8c51f6b5735e9bda55eb..f91512a0eaa3131945c6f6ec63456238e380f4fe 100644
--- a/civicrm/CRM/Contribute/BAO/Query.php
+++ b/civicrm/CRM/Contribute/BAO/Query.php
@@ -915,10 +915,10 @@ class CRM_Contribute_BAO_Query extends CRM_Core_BAO_Query {
     $form->addFormFieldsFromMetadata();
 
     $form->add('text', 'contribution_amount_low', ts('From'), ['size' => 8, 'maxlength' => 8]);
-    $form->addRule('contribution_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('9.99', ' ')]), 'money');
+    $form->addRule('contribution_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('9.99')]), 'money');
 
     $form->add('text', 'contribution_amount_high', ts('To'), ['size' => 8, 'maxlength' => 8]);
-    $form->addRule('contribution_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money');
+    $form->addRule('contribution_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money');
 
     // Adding select option for curreny type -- CRM-4711
     $form->add('select', 'contribution_currency_type',
diff --git a/civicrm/CRM/Contribute/DAO/Contribution.php b/civicrm/CRM/Contribute/DAO/Contribution.php
index e64cf1675e0bcaa1c132bf5182abc7deae1486f9..45f51b756cad855b0a4ae6c45d1bda1a92c7925e 100644
--- a/civicrm/CRM/Contribute/DAO/Contribution.php
+++ b/civicrm/CRM/Contribute/DAO/Contribution.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contribute/Contribution.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:5e4c6a77803e361625cafbd2f6e58bbe)
+ * (GenCodeChecksum:cc3bcdbce84066823084f71e30f6990b)
  */
 
 /**
  * Database access object for the Contribution entity.
  */
 class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.3';
 
   /**
    * Static instance to hold the table name.
@@ -325,7 +327,7 @@ class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO {
         'financial_type_id' => [
           'name' => 'financial_type_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Financial Type'),
+          'title' => ts('Financial Type ID'),
           'description' => ts('FK to Financial Type for (total_amount - non_deductible_amount).'),
           'where' => 'civicrm_contribution.financial_type_id',
           'export' => TRUE,
@@ -336,6 +338,7 @@ class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO {
           'FKClassName' => 'CRM_Financial_DAO_FinancialType',
           'html' => [
             'type' => 'Select',
+            'label' => ts("Financial Type"),
           ],
           'pseudoconstant' => [
             'table' => 'civicrm_financial_type',
@@ -359,6 +362,7 @@ class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO {
           'FKClassName' => 'CRM_Contribute_DAO_ContributionPage',
           'html' => [
             'type' => 'Select',
+            'label' => ts("Contribution Page"),
           ],
           'pseudoconstant' => [
             'table' => 'civicrm_contribution_page',
diff --git a/civicrm/CRM/Contribute/DAO/ContributionPage.php b/civicrm/CRM/Contribute/DAO/ContributionPage.php
index 7970a2907f992564ca7e2f2fa35c7833461eef97..80ec08d3acf67e474c303f50f213a8665539c268 100644
--- a/civicrm/CRM/Contribute/DAO/ContributionPage.php
+++ b/civicrm/CRM/Contribute/DAO/ContributionPage.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contribute/ContributionPage.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:63bc96be1f5552249e75545940c13bed)
+ * (GenCodeChecksum:902bfa164280b9ba21a7cb5a38aceba8)
  */
 
 /**
  * Database access object for the ContributionPage entity.
  */
 class CRM_Contribute_DAO_ContributionPage extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.3';
 
   /**
    * Static instance to hold the table name.
@@ -467,6 +469,7 @@ class CRM_Contribute_DAO_ContributionPage extends CRM_Core_DAO {
           'entity' => 'ContributionPage',
           'bao' => 'CRM_Contribute_BAO_ContributionPage',
           'localizable' => 0,
+          'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED,
           'html' => [
             'type' => 'Select',
           ],
diff --git a/civicrm/CRM/Contribute/DAO/ContributionProduct.php b/civicrm/CRM/Contribute/DAO/ContributionProduct.php
index cebd77a3fbf76ca4e75f4c6ca36266d66cf4e743..e5a0f053702abd27b6be9e7f4abc20a0cd49b8e1 100644
--- a/civicrm/CRM/Contribute/DAO/ContributionProduct.php
+++ b/civicrm/CRM/Contribute/DAO/ContributionProduct.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contribute/ContributionProduct.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:dd95e7a994c6dfe314f149b5ba7d48db)
+ * (GenCodeChecksum:4e76d9dc75f5bc1b1141645c8ee5e2e4)
  */
 
 /**
  * Database access object for the ContributionProduct entity.
  */
 class CRM_Contribute_DAO_ContributionProduct extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contribute/DAO/ContributionRecur.php b/civicrm/CRM/Contribute/DAO/ContributionRecur.php
index a3254572ca08522ad7db4093c632556a91cdd683..2ccd7486d6c3dd11f3920b8dc1b7f48c6fe91d12 100644
--- a/civicrm/CRM/Contribute/DAO/ContributionRecur.php
+++ b/civicrm/CRM/Contribute/DAO/ContributionRecur.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contribute/ContributionRecur.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:62019becf87dee13b7e9e174eb3c286e)
+ * (GenCodeChecksum:decf43c002d0e4ded0fe5f2a2e2f7bd0)
  */
 
 /**
  * Database access object for the ContributionRecur entity.
  */
 class CRM_Contribute_DAO_ContributionRecur extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.6';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contribute/DAO/ContributionSoft.php b/civicrm/CRM/Contribute/DAO/ContributionSoft.php
index ba2c9e21c3a88ad175c1f23861e0b40e4c0f0ca6..3a1a9b7af7609c05af4aa5179fc6cb8151f185bd 100644
--- a/civicrm/CRM/Contribute/DAO/ContributionSoft.php
+++ b/civicrm/CRM/Contribute/DAO/ContributionSoft.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contribute/ContributionSoft.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a1c2918874b74f3f9bbbe0464aea1563)
+ * (GenCodeChecksum:caa58722ef865c7342fdff08f24d86ee)
  */
 
 /**
  * Database access object for the ContributionSoft entity.
  */
 class CRM_Contribute_DAO_ContributionSoft extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contribute/DAO/Premium.php b/civicrm/CRM/Contribute/DAO/Premium.php
index a6ef8403508b4dc9b896194012db76b26cef07d2..7e0020c134926741b9c068a8c73681af624ab722 100644
--- a/civicrm/CRM/Contribute/DAO/Premium.php
+++ b/civicrm/CRM/Contribute/DAO/Premium.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contribute/Premium.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:df0bfae4e0916c02a621471db14f3151)
+ * (GenCodeChecksum:cd1826e777cea80450636ef175aaab7f)
  */
 
 /**
  * Database access object for the Premium entity.
  */
 class CRM_Contribute_DAO_Premium extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contribute/DAO/PremiumsProduct.php b/civicrm/CRM/Contribute/DAO/PremiumsProduct.php
index 79d9071c30c171892afa85c895249a7b6c2b8133..b153adb274962e9a89de5b1789fe607092cdde9d 100644
--- a/civicrm/CRM/Contribute/DAO/PremiumsProduct.php
+++ b/civicrm/CRM/Contribute/DAO/PremiumsProduct.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contribute/PremiumsProduct.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a250114a018b2fd5e9d259f00d18a5c3)
+ * (GenCodeChecksum:4831cb4c7e0611db0f4312f6522d2c20)
  */
 
 /**
  * Database access object for the PremiumsProduct entity.
  */
 class CRM_Contribute_DAO_PremiumsProduct extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contribute/DAO/Product.php b/civicrm/CRM/Contribute/DAO/Product.php
index 5632c65d4335c7f180744a5fb87cea72869dc1b4..8433e1820ad0ee4b6982f5b79413d5725ae82ddc 100644
--- a/civicrm/CRM/Contribute/DAO/Product.php
+++ b/civicrm/CRM/Contribute/DAO/Product.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contribute/Product.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:6e0a8b319985de1fe01120f4d91003a8)
+ * (GenCodeChecksum:dea1c7db61776456a70f752fe9f93f06)
  */
 
 /**
  * Database access object for the Product entity.
  */
 class CRM_Contribute_DAO_Product extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contribute/DAO/Widget.php b/civicrm/CRM/Contribute/DAO/Widget.php
index 116d447ed53148c8dfc7e96ff2b81ff9a0ba16c6..5da47438da53eebf0e55504fd27d37a6cc5d7a05 100644
--- a/civicrm/CRM/Contribute/DAO/Widget.php
+++ b/civicrm/CRM/Contribute/DAO/Widget.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Contribute/Widget.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b0e7180701d83e1d83d53bf2e43c0c51)
+ * (GenCodeChecksum:e24eaf675b793969d408fbc0f847a9ed)
  */
 
 /**
  * Database access object for the Widget entity.
  */
 class CRM_Contribute_DAO_Widget extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Contribute/Form/Contribution/Confirm.php b/civicrm/CRM/Contribute/Form/Contribution/Confirm.php
index 228e285dfdee18ffeef735d6bf931e6d444bc852..93a210a5530ca8840b12ce71a23501120f4adcef 100644
--- a/civicrm/CRM/Contribute/Form/Contribution/Confirm.php
+++ b/civicrm/CRM/Contribute/Form/Contribution/Confirm.php
@@ -102,7 +102,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       $pledgeParams['create_date'] = $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date("Ymd");
       if (!empty($params['start_date'])) {
         $pledgeParams['frequency_day'] = intval(date("d", strtotime($params['start_date'])));
-        $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date('Ymd', strtotime(CRM_Utils_Array::value('start_date', $params)));
+        $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date('Ymd', strtotime($params['start_date']));
       }
       $pledgeParams['status_id'] = $contribution->contribution_status_id;
       $pledgeParams['max_reminders'] = $form->_values['max_reminders'];
@@ -155,19 +155,19 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $paymentProcessorOutcome, $receiptDate, $recurringContributionID) {
     $contributionParams = [
       'financial_type_id' => $financialTypeID,
-      'receive_date' => (CRM_Utils_Array::value('receive_date', $params)) ? CRM_Utils_Date::processDate($params['receive_date']) : date('YmdHis'),
+      'receive_date' => !empty($params['receive_date']) ? CRM_Utils_Date::processDate($params['receive_date']) : date('YmdHis'),
       'tax_amount' => $params['tax_amount'] ?? NULL,
       'amount_level' => $params['amount_level'] ?? NULL,
       'invoice_id' => $params['invoiceID'],
       'currency' => $params['currencyID'],
-      'is_pay_later' => CRM_Utils_Array::value('is_pay_later', $params, 0),
+      'is_pay_later' => $params['is_pay_later'] ?? 0,
       //configure cancel reason, cancel date and thankyou date
       //from 'contribution' type profile if included
-      'cancel_reason' => CRM_Utils_Array::value('cancel_reason', $params, 0),
+      'cancel_reason' => $params['cancel_reason'] ?? 0,
       'cancel_date' => isset($params['cancel_date']) ? CRM_Utils_Date::format($params['cancel_date']) : NULL,
       'thankyou_date' => isset($params['thankyou_date']) ? CRM_Utils_Date::format($params['thankyou_date']) : NULL,
       //setting to make available to hook - although seems wrong to set on form for BAO hook availability
-      'skipLineItem' => CRM_Utils_Array::value('skipLineItem', $params, 0),
+      'skipLineItem' => $params['skipLineItem'] ?? 0,
     ];
 
     if ($paymentProcessorOutcome) {
@@ -301,7 +301,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $this->_params['is_pay_later'] = $this->get('is_pay_later');
     $this->assign('is_pay_later', $this->_params['is_pay_later']);
     if ($this->_params['is_pay_later']) {
-      $this->assign('pay_later_receipt', CRM_Utils_Array::value('pay_later_receipt', $this->_values));
+      $this->assign('pay_later_receipt', $this->_values['pay_later_receipt'] ?? NULL);
     }
     // if onbehalf-of-organization
     if (!empty($this->_values['onbehalf_profile_id']) && !empty($this->_params['onbehalf']['organization_name'])) {
@@ -460,7 +460,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
 
     // assign contribution page id to the template so we can add css class for it
     $this->assign('contributionPageID', $this->_id);
-    $this->assign('is_for_organization', CRM_Utils_Array::value('is_for_organization', $this->_params));
+    $this->assign('is_for_organization', $this->_params['is_for_organization'] ?? NULL);
 
     $this->set('params', $this->_params);
   }
@@ -485,7 +485,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       $fieldTypes[] = CRM_Core_BAO_UFGroup::getContactType($this->_values['honoree_profile_id']);
       $this->buildCustom($this->_values['honoree_profile_id'], 'honoreeProfileFields', TRUE, 'honor', $fieldTypes);
     }
-    $this->assign('receiptFromEmail', CRM_Utils_Array::value('receipt_from_email', $this->_values));
+    $this->assign('receiptFromEmail', $this->_values['receipt_from_email'] ?? NULL);
     $amount_block_is_active = $this->get('amount_block_is_active');
     $this->assign('amount_block_is_active', $amount_block_is_active);
 
@@ -738,7 +738,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       $this->assign('product_name', $productDAO->name);
       $this->assign('price', $productDAO->price);
       $this->assign('sku', $productDAO->sku);
-      $this->assign('option', CRM_Utils_Array::value('options_' . $premiumParams['selectProduct'], $premiumParams));
+      $this->assign('option', $premiumParams['options_' . $premiumParams['selectProduct']] ?? NULL);
 
       $periodType = $productDAO->period_type;
 
@@ -887,7 +887,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
 
     $isEmailReceipt = !empty($form->_values['is_email_receipt']);
     $isSeparateMembershipPayment = !empty($params['separate_membership_payment']);
-    $pledgeID = !empty($params['pledge_id']) ? $params['pledge_id'] : CRM_Utils_Array::value('pledge_id', $form->_values);
+    $pledgeID = !empty($params['pledge_id']) ? $params['pledge_id'] : $form->_values['pledge_id'] ?? NULL;
     if (!$isSeparateMembershipPayment && !empty($form->_values['pledge_block_id']) &&
       (!empty($params['is_pledge']) || $pledgeID)) {
       $isPledge = TRUE;
@@ -1089,10 +1089,10 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $recurParams['is_email_receipt'] = $params['is_email_receipt'] ?? NULL;
     // we need to add a unique trxn_id to avoid a unique key error
     // in paypal IPN we reset this when paypal sends us the real trxn id, CRM-2991
-    $recurParams['trxn_id'] = CRM_Utils_Array::value('trxn_id', $params, $params['invoiceID']);
+    $recurParams['trxn_id'] = $params['trxn_id'] ?? $params['invoiceID'];
     $recurParams['financial_type_id'] = $contributionType->id;
 
-    $campaignId = CRM_Utils_Array::value('campaign_id', $params, CRM_Utils_Array::value('campaign_id', $form->_values));
+    $campaignId = $params['campaign_id'] ?? $form->_values['campaign_id'] ?? NULL;
     $recurParams['campaign_id'] = $campaignId;
     $recurring = CRM_Contribute_BAO_ContributionRecur::add($recurParams);
     if (is_a($recurring, 'CRM_Core_Error')) {
@@ -1343,7 +1343,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $membershipTypes = CRM_Member_BAO_Membership::buildMembershipTypeValues($this, $membershipTypeIDs);
     $membershipType = empty($membershipTypes) ? [] : reset($membershipTypes);
 
-    $this->assign('membership_name', CRM_Utils_Array::value('name', $membershipType));
+    $this->assign('membership_name', $membershipType['name']);
     $this->_values['membership_name'] = $membershipType['name'] ?? NULL;
 
     $isPaidMembership = FALSE;
@@ -1360,7 +1360,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       $financialTypeID = $this->_values['financial_type_id'];
     }
     else {
-      $financialTypeID = CRM_Utils_Array::value('financial_type_id', $membershipType, CRM_Utils_Array::value('financial_type_id', $membershipParams));
+      $financialTypeID = $membershipType['financial_type_id'] ?? $membershipParams['financial_type_id'] ?? NULL;
     }
 
     if (!empty($this->_params['membership_source'])) {
@@ -1412,10 +1412,10 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $isProcessSeparateMembershipTransaction, $financialTypeID, $unprocessedLineItems) {
 
     $membershipContribution = NULL;
-    $isTest = CRM_Utils_Array::value('is_test', $membershipParams, FALSE);
+    $isTest = $membershipParams['is_test'] ?? FALSE;
     $errors = $paymentResults = [];
     $form->_values['isMembership'] = TRUE;
-    $isRecurForFirstTransaction = CRM_Utils_Array::value('is_recur', $form->_params, CRM_Utils_Array::value('is_recur', $membershipParams));
+    $isRecurForFirstTransaction = $form->_params['is_recur'] ?? $membershipParams['is_recur'] ?? NULL;
 
     $totalAmount = $membershipParams['amount'];
 
@@ -1465,7 +1465,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
           unset($membershipParams['is_recur']);
         }
         list($membershipContribution, $secondPaymentResult) = $this->processSecondaryFinancialTransaction($contactID, $form, array_merge($membershipParams, ['skipLineItem' => 1]),
-          $isTest, $unprocessedLineItems, CRM_Utils_Array::value('minimum_fee', $membershipDetails, 0), CRM_Utils_Array::value('financial_type_id', $membershipDetails));
+          $isTest, $unprocessedLineItems, $membershipDetails['minimum_fee'] ?? 0, $membershipDetails['financial_type_id'] ?? NULL);
         $paymentResults[] = ['contribution_id' => $membershipContribution->id, 'result' => $secondPaymentResult];
         $totalAmount = $membershipContribution->total_amount;
       }
@@ -1486,7 +1486,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     }
     //@todo it should no longer be possible for it to get to this point & membership to not be an array
     if (is_array($membershipTypeIDs) && !empty($membershipContributionID)) {
-      $typesTerms = CRM_Utils_Array::value('types_terms', $membershipParams, []);
+      $typesTerms = $membershipParams['types_terms'] ?? [];
 
       $membershipLines = $nonMembershipLines = [];
       foreach ($unprocessedLineItems as $priceSetID => $lines) {
@@ -1509,7 +1509,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
           $membershipLineItems = $unprocessedLineItems;
         }
         $i++;
-        $numTerms = CRM_Utils_Array::value($memType, $typesTerms, 1);
+        $numTerms = $typesTerms[$memType] ?? 1;
         $contributionRecurID = $form->_params['contributionRecurID'] ?? NULL;
 
         $membershipSource = NULL;
@@ -1537,11 +1537,21 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
           $pending = $membershipContribution->contribution_status_id == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
         }
         else {
-          $pending = $this->getIsPending();
+          // The concept of contributeMode is deprecated.
+          // the is_monetary concept probably should be too as it can be calculated from
+          // the existence of 'amount' & seems fragile.
+          if (((isset($this->_contributeMode)) || !empty($this->_params['is_pay_later'])
+            ) &&
+            (($this->_values['is_monetary'] && $this->_amount > 0.0))
+          ) {
+            $pending = TRUE;
+          }
+          $pending = FALSE;
         }
+
         list($membership, $renewalMode, $dates) = CRM_Member_BAO_Membership::processMembership(
           $contactID, $memType, $isTest,
-          date('YmdHis'), CRM_Utils_Array::value('cms_contactID', $membershipParams),
+          date('YmdHis'), $membershipParams['cms_contactID'] ?? NULL,
           $customFieldsFormatted,
           $numTerms, $membershipID, $pending,
           $contributionRecurID, $membershipSource, $isPayLater, $campaignId, [], $membershipContribution,
@@ -1648,7 +1658,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       if (!empty($membershipContribution) && !is_a($membershipContribution, 'CRM_Core_Error')) {
         if (empty($form->_paymentProcessor)) {
           // @todo this can maybe go now we are setting payment_processor_id = 0 more reliably.
-          $paymentProcessorIDs = explode(CRM_Core_DAO::VALUE_SEPARATOR, CRM_Utils_Array::value('payment_processor', $this->_values));
+          $paymentProcessorIDs = explode(CRM_Core_DAO::VALUE_SEPARATOR, $this->_values['payment_processor'] ?? NULL);
           $this->_paymentProcessor['id'] = $paymentProcessorIDs[0];
         }
         $result = ['payment_status_id' => 1, 'contribution' => $membershipContribution];
@@ -1731,10 +1741,9 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       'contact_id' => $contactID,
       'line_item' => $lineItems,
       'is_test' => $isTest,
-      'campaign_id' => CRM_Utils_Array::value('campaign_id', $tempParams, CRM_Utils_Array::value('campaign_id',
-        $form->_values)),
+      'campaign_id' => $tempParams['campaign_id'] ?? $form->_values['campaign_id'] ?? NULL,
       'contribution_page_id' => $form->_id,
-      'source' => CRM_Utils_Array::value('source', $tempParams, CRM_Utils_Array::value('description', $tempParams)),
+      'source' => $tempParams['source'] ?? $tempParams['description'] ?? NULL,
     ];
     $isMonetary = !empty($form->_values['is_monetary']);
     if ($isMonetary) {
@@ -1787,27 +1796,6 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     return [$membershipContribution, $result];
   }
 
-  /**
-   * Is the payment a pending payment.
-   *
-   * We are moving towards always creating as pending and updating at the end (based on payment), so this should be
-   * an interim refactoring. It was shared with another unrelated form & some parameters may not apply to this form.
-   *
-   * @return bool
-   */
-  protected function getIsPending() {
-    // The concept of contributeMode is deprecated.
-    // the is_monetary concept probably should be too as it can be calculated from
-    // the existence of 'amount' & seems fragile.
-    if (((isset($this->_contributeMode)) || !empty($this->_params['is_pay_later'])
-      ) &&
-      (($this->_values['is_monetary'] && $this->_amount > 0.0))
-    ) {
-      return TRUE;
-    }
-    return FALSE;
-  }
-
   /**
    * Are we going to do 2 financial transactions.
    *
@@ -1875,7 +1863,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
         !empty($this->_membershipBlock['is_separate_payment'])
         && !empty($this->_values['fee'][$priceField->id])
         && ($this->_values['fee'][$priceField->id]['name'] == "other_amount")
-        && CRM_Utils_Array::value("price_{$paramWeDoNotUnderstand}", $this->_params) < 1
+        && ($this->_params["price_{$paramWeDoNotUnderstand}"] ?? NULL) < 1
         && empty($this->_params["price_{$priceField->id}"])
       ) {
         $this->_params['amount'] = NULL;
@@ -1888,8 +1876,8 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       //@todo - merge with section above
       if (!empty($this->_membershipBlock['is_separate_payment'])
         && !empty($this->_values['fee'][$priceField->id])
-        && CRM_Utils_Array::value('name', $this->_values['fee'][$priceField->id]) == 'contribution_amount'
-        && CRM_Utils_Array::value("price_{$priceField->id}", $this->_params) == '-1'
+        && ($this->_values['fee'][$priceField->id]['name'] ?? NULL) == 'contribution_amount'
+        && ($this->_params["price_{$priceField->id}"] ?? NULL) == '-1'
       ) {
         $this->_params['amount'] = NULL;
       }
@@ -1976,7 +1964,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     }
     $form->set('memberPriceFieldIDS', $membershipPriceFieldIDs);
     $form->setRecurringMembershipParams();
-    $form->processFormSubmission(CRM_Utils_Array::value('contact_id', $params));
+    $form->processFormSubmission($params['contact_id'] ?? NULL);
   }
 
   /**
@@ -2208,7 +2196,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $this->_contactID = $contactID;
 
     //get email primary first if exist
-    $subscriptionEmail = ['email' => CRM_Utils_Array::value('email-Primary', $params)];
+    $subscriptionEmail = ['email' => $params['email-Primary'] ?? NULL];
     if (!$subscriptionEmail['email']) {
       $subscriptionEmail['email'] = $params["email-{$this->_bltID}"] ?? NULL;
     }
@@ -2305,7 +2293,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
         $contactID,
         $this->wrangleFinancialTypeID($this->_values['financial_type_id']),
         ($this->_mode == 'test') ? 1 : 0,
-        CRM_Utils_Array::value('is_recur', $paymentParams)
+        $paymentParams['is_recur'] ?? NULL
       );
 
       if (empty($result['is_payment_failure'])) {
@@ -2471,12 +2459,12 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
    * @throws \Exception
    */
   protected function completeTransaction($result, $contributionID) {
-    if (CRM_Utils_Array::value('payment_status_id', $result) == 1) {
+    if (($result['payment_status_id'] ?? NULL) == 1) {
       try {
         civicrm_api3('contribution', 'completetransaction', [
           'id' => $contributionID,
           'trxn_id' => $result['trxn_id'] ?? NULL,
-          'payment_processor_id' => CRM_Utils_Array::value('payment_processor_id', $result, $this->_paymentProcessor['id']),
+          'payment_processor_id' => $result['payment_processor_id'] ?? $this->_paymentProcessor['id'],
           'is_transactional' => FALSE,
           'fee_amount' => $result['fee_amount'] ?? NULL,
           'receive_date' => $result['receive_date'] ?? NULL,
diff --git a/civicrm/CRM/Contribute/Form/Contribution/Main.php b/civicrm/CRM/Contribute/Form/Contribution/Main.php
index d215092d92b42976236475f2c438b5872f2f5a51..7753b2a135732b8644b0a83597645ed24ceec7fb 100644
--- a/civicrm/CRM/Contribute/Form/Contribution/Main.php
+++ b/civicrm/CRM/Contribute/Form/Contribution/Main.php
@@ -330,8 +330,7 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
       }
     }
 
-    $contactID = $this->getContactID();
-    if ($this->getContactID() === 0) {
+    if ($contactID === 0) {
       $this->addCidZeroOptions();
     }
 
diff --git a/civicrm/CRM/Contribute/Form/ContributionPage/Amount.php b/civicrm/CRM/Contribute/Form/ContributionPage/Amount.php
index b90b5ea054f706ae4d424ab9d4bbe40da4908467..16fdf6324518178d22b942ab83b19d7ed1efbf6a 100644
--- a/civicrm/CRM/Contribute/Form/ContributionPage/Amount.php
+++ b/civicrm/CRM/Contribute/Form/ContributionPage/Amount.php
@@ -48,10 +48,10 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co
     // do u want to allow a free form text field for amount
     $this->addElement('checkbox', 'is_allow_other_amount', ts('Allow other amounts'), NULL, ['onclick' => "minMax(this);showHideAmountBlock( this, 'is_allow_other_amount' );"]);
     $this->add('text', 'min_amount', ts('Minimum Amount'), ['size' => 8, 'maxlength' => 8]);
-    $this->addRule('min_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('9.99', ' ')]), 'money');
+    $this->addRule('min_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('9.99')]), 'money');
 
     $this->add('text', 'max_amount', ts('Maximum Amount'), ['size' => 8, 'maxlength' => 8]);
-    $this->addRule('max_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money');
+    $this->addRule('max_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money');
 
     //CRM-12055
     $this->add('text', 'amount_label', ts('Contribution Amounts Label'));
@@ -67,7 +67,7 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co
 
       // value
       $this->add('text', "value[$i]", ts('Value'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'value'));
-      $this->addRule("value[$i]", ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money');
+      $this->addRule("value[$i]", ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money');
 
       // default
       $default[] = $this->createElement('radio', NULL, NULL, NULL, $i);
@@ -262,7 +262,7 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co
 
         // CRM-4038: fix value display
         foreach ($defaults['value'] as & $amount) {
-          $amount = trim(CRM_Utils_Money::format($amount, ' '));
+          $amount = trim(CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($amount));
         }
       }
     }
@@ -366,7 +366,7 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co
       }
     }
     else {
-      if ($fields['amount_block_is_active'] && empty($fields['payment_processor'])) {
+      if (!empty($fields['amount_block_is_active']) && empty($fields['payment_processor'])) {
         $errors['payment_processor'] = ts('You have listed fixed contribution options or selected a price set, but no payment option has been selected. Please select at least one payment processor and/or enable the pay later option.');
       }
     }
@@ -533,7 +533,7 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co
     if (array_key_exists('payment_processor', $params) &&
       !CRM_Utils_System::isNull($params['payment_processor'])
     ) {
-      $params['payment_processor'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, array_keys($params['payment_processor']));
+      $params['payment_processor'] = array_keys($params['payment_processor']);
     }
     else {
       $params['payment_processor'] = 'null';
diff --git a/civicrm/CRM/Contribute/Form/ContributionPage/Settings.php b/civicrm/CRM/Contribute/Form/ContributionPage/Settings.php
index dc96867c0aee53f1a74c8d1d3bfd249f106d7770..07e498ae48041ee0a51c38a916c5ac6724540983 100644
--- a/civicrm/CRM/Contribute/Form/ContributionPage/Settings.php
+++ b/civicrm/CRM/Contribute/Form/ContributionPage/Settings.php
@@ -162,7 +162,7 @@ class CRM_Contribute_Form_ContributionPage_Settings extends CRM_Contribute_Form_
 
     // collect goal amount
     $this->add('text', 'goal_amount', ts('Goal Amount'), ['size' => 8, 'maxlength' => 12]);
-    $this->addRule('goal_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money');
+    $this->addRule('goal_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money');
 
     // is confirmation page enabled?
     $this->addElement('checkbox', 'is_confirm_enabled', ts('Use a confirmation page?'));
diff --git a/civicrm/CRM/Contribute/Form/ContributionPage/Widget.php b/civicrm/CRM/Contribute/Form/ContributionPage/Widget.php
index d59c1f6c45bc1e29c49753b7b99a4240354ea8b2..7c6ee1bb991cdef4b2a58bcf694f71b1c32760fa 100644
--- a/civicrm/CRM/Contribute/Form/ContributionPage/Widget.php
+++ b/civicrm/CRM/Contribute/Form/ContributionPage/Widget.php
@@ -40,7 +40,7 @@ class CRM_Contribute_Form_ContributionPage_Widget extends CRM_Contribute_Form_Co
 
     $this->assign('cpageId', $this->_id);
 
-    $this->assign('widgetExternUrl', CRM_Utils_System::externUrl('extern/widget', "cpageId={$this->_id}&widgetId={$this->_widget->id}&format=3"));
+    $this->assign('widgetExternUrl', CRM_Utils_System::externUrl('extern/widget', "cpageId={$this->_id}&widgetId=" . ($this->_widget->id ?? '') . "&format=3"));
 
     $config = CRM_Core_Config::singleton();
     $title = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_ContributionPage',
diff --git a/civicrm/CRM/Contribute/Import/Form/MapField.php b/civicrm/CRM/Contribute/Import/Form/MapField.php
index cb886752c7081caf26a539a3a00ca0936d3f5ddd..9a354792cb49007a702b4b1988c20d4b9d23d0dc 100644
--- a/civicrm/CRM/Contribute/Import/Form/MapField.php
+++ b/civicrm/CRM/Contribute/Import/Form/MapField.php
@@ -424,7 +424,7 @@ class CRM_Contribute_Import_Form_MapField extends CRM_Import_Form_MapField {
     }
 
     $fileName = $this->controller->exportValue('DataSource', 'uploadFile');
-    $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
+    $separator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
 
     $mapper = $mapperKeys = $mapperKeysMain = $mapperSoftCredit = $softCreditFields = $mapperPhoneType = $mapperSoftCreditType = [];
@@ -511,7 +511,7 @@ class CRM_Contribute_Import_Form_MapField extends CRM_Import_Form_MapField {
     }
 
     $parser = new CRM_Contribute_Import_Parser_Contribution($mapperKeysMain, $mapperSoftCredit, $mapperPhoneType);
-    $parser->run($fileName, $seperator, $mapper, $skipColumnHeader,
+    $parser->run($fileName, $separator, $mapper, $skipColumnHeader,
       CRM_Import_Parser::MODE_PREVIEW, $this->get('contactType')
     );
 
diff --git a/civicrm/CRM/Contribute/Import/Form/Preview.php b/civicrm/CRM/Contribute/Import/Form/Preview.php
index b14ea26c39164f444a638baf92cca2b4d1cf524b..e45de43a25b8a2d446fa429d0f64342d3c75faa6 100644
--- a/civicrm/CRM/Contribute/Import/Form/Preview.php
+++ b/civicrm/CRM/Contribute/Import/Form/Preview.php
@@ -94,7 +94,7 @@ class CRM_Contribute_Import_Form_Preview extends CRM_Import_Form_Preview {
    */
   public function postProcess() {
     $fileName = $this->controller->exportValue('DataSource', 'uploadFile');
-    $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
+    $separator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
     $invalidRowCount = $this->get('invalidRowCount');
     $conflictRowCount = $this->get('conflictRowCount');
@@ -128,7 +128,7 @@ class CRM_Contribute_Import_Form_Preview extends CRM_Import_Form_Preview {
       }
       $mapperFields[] = implode(' - ', $header);
     }
-    $parser->run($fileName, $seperator,
+    $parser->run($fileName, $separator,
       $mapperFields,
       $skipColumnHeader,
       CRM_Import_Parser::MODE_IMPORT,
diff --git a/civicrm/CRM/Contribute/Import/Parser.php b/civicrm/CRM/Contribute/Import/Parser.php
index 87637bb6294d58a8bbcf63b63e1b1777741d6622..77ec4824cb2f648ed0002bceecf4f60296801adf 100644
--- a/civicrm/CRM/Contribute/Import/Parser.php
+++ b/civicrm/CRM/Contribute/Import/Parser.php
@@ -34,10 +34,10 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser {
   protected $_fileSize;
 
   /**
-   * Seperator being used
+   * Separator being used
    * @var string
    */
-  protected $_seperator;
+  protected $_separator;
 
   /**
    * Total number of lines in file
@@ -104,7 +104,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser {
 
   /**
    * @param string $fileName
-   * @param string $seperator
+   * @param string $separator
    * @param $mapper
    * @param bool $skipColumnHeader
    * @param int $mode
@@ -118,7 +118,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser {
    */
   public function run(
     $fileName,
-    $seperator = ',',
+    $separator = ',',
     &$mapper,
     $skipColumnHeader = FALSE,
     $mode = self::MODE_PREVIEW,
@@ -149,7 +149,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser {
 
     $this->_haveColumnHeader = $skipColumnHeader;
 
-    $this->_seperator = $seperator;
+    $this->_separator = $separator;
 
     $fd = fopen($fileName, "r");
     if (!$fd) {
@@ -182,7 +182,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser {
     while (!feof($fd)) {
       $this->_lineCount++;
 
-      $values = fgetcsv($fd, 8192, $seperator);
+      $values = fgetcsv($fd, 8192, $separator);
       if (!$values) {
         continue;
       }
@@ -506,7 +506,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser {
   public function set($store, $mode = self::MODE_SUMMARY) {
     $store->set('fileSize', $this->_fileSize);
     $store->set('lineCount', $this->_lineCount);
-    $store->set('seperator', $this->_seperator);
+    $store->set('separator', $this->_separator);
     $store->set('fields', $this->getSelectValues());
     $store->set('fieldTypes', $this->getSelectTypes());
 
diff --git a/civicrm/CRM/Core/BAO/CustomValueTable.php b/civicrm/CRM/Core/BAO/CustomValueTable.php
index 8bfc4fb8add995528e032d0cadb2657caf402177..fa3396a3b3aa787d20aa1d556f3f22c5a5860a40 100644
--- a/civicrm/CRM/Core/BAO/CustomValueTable.php
+++ b/civicrm/CRM/Core/BAO/CustomValueTable.php
@@ -540,7 +540,7 @@ AND    $cond
     }
 
     if (!isset($params['entityID']) || !CRM_Utils_Type::validate($params['entityID'], 'Integer', FALSE)) {
-      return CRM_Core_Error::createAPIError(ts('entity_id needs to be set and of type Integer'));
+      throw new CRM_Core_Exception(ts('entity_id needs to be set and of type Integer'));
     }
 
     // first collect all the id/value pairs. The format is:
@@ -550,7 +550,7 @@ AND    $cond
       if ($customFieldInfo = CRM_Core_BAO_CustomField::getKeyID($n, TRUE)) {
         $fieldID = (int ) $customFieldInfo[0];
         if (CRM_Utils_Type::escape($fieldID, 'Integer', FALSE) === NULL) {
-          return CRM_Core_Error::createAPIError(ts('field ID needs to be of type Integer for index %1',
+          throw new CRM_Core_Exception(ts('field ID needs to be of type Integer for index %1',
             [1 => $fieldID]
           ));
         }
@@ -620,7 +620,7 @@ AND    cf.id IN ( $fieldIDList )
         }
         // Ensure that value is of the right data type
         elseif (CRM_Utils_Type::escape($fieldValue['value'], $dataType, FALSE) === NULL) {
-          return CRM_Core_Error::createAPIError(ts('value: %1 is not of the right field data type: %2',
+          throw new CRM_Core_Exception(ts('value: %1 is not of the right field data type: %2',
             [
               1 => $fieldValue['value'],
               2 => $dao->data_type,
@@ -668,7 +668,7 @@ AND    cf.id IN ( $fieldIDList )
       return ['is_error' => 0, 'result' => 1];
     }
 
-    return CRM_Core_Error::createAPIError(ts('Unknown error'));
+    throw new CRM_Core_Exception(ts('Unknown error'));
   }
 
   /**
diff --git a/civicrm/CRM/Core/BAO/MessageTemplate.php b/civicrm/CRM/Core/BAO/MessageTemplate.php
index fbbb96709e01a0d6eec20ae875a7bc012c35195f..4dedb8c66e7d6e1bfd86bff788ceaa2b23ec4516 100644
--- a/civicrm/CRM/Core/BAO/MessageTemplate.php
+++ b/civicrm/CRM/Core/BAO/MessageTemplate.php
@@ -402,6 +402,8 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
       'isTest' => FALSE,
       // filename of optional PDF version to add as attachment (do not include path)
       'PDFFilename' => NULL,
+      // Disable Smarty?
+      'disableSmarty' => FALSE,
     ];
     $params = array_merge($defaults, $params);
 
@@ -511,14 +513,17 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
       $contact = $contact[$contactID];
     }
 
-    $mailContent['subject'] = CRM_Utils_Token::replaceDomainTokens($mailContent['subject'], $domain, FALSE, $tokens['subject'], TRUE);
-    $mailContent['text'] = CRM_Utils_Token::replaceDomainTokens($mailContent['text'], $domain, FALSE, $tokens['text'], TRUE);
-    $mailContent['html'] = CRM_Utils_Token::replaceDomainTokens($mailContent['html'], $domain, TRUE, $tokens['html'], TRUE);
+    // When using Smarty we need to pass the $escapeSmarty parameter.
+    $escapeSmarty = !$params['disableSmarty'];
+
+    $mailContent['subject'] = CRM_Utils_Token::replaceDomainTokens($mailContent['subject'], $domain, FALSE, $tokens['subject'], $escapeSmarty);
+    $mailContent['text'] = CRM_Utils_Token::replaceDomainTokens($mailContent['text'], $domain, FALSE, $tokens['text'], $escapeSmarty);
+    $mailContent['html'] = CRM_Utils_Token::replaceDomainTokens($mailContent['html'], $domain, TRUE, $tokens['html'], $escapeSmarty);
 
     if ($contactID) {
-      $mailContent['subject'] = CRM_Utils_Token::replaceContactTokens($mailContent['subject'], $contact, FALSE, $tokens['subject'], FALSE, TRUE);
-      $mailContent['text'] = CRM_Utils_Token::replaceContactTokens($mailContent['text'], $contact, FALSE, $tokens['text'], FALSE, TRUE);
-      $mailContent['html'] = CRM_Utils_Token::replaceContactTokens($mailContent['html'], $contact, FALSE, $tokens['html'], FALSE, TRUE);
+      $mailContent['subject'] = CRM_Utils_Token::replaceContactTokens($mailContent['subject'], $contact, FALSE, $tokens['subject'], FALSE, $escapeSmarty);
+      $mailContent['text'] = CRM_Utils_Token::replaceContactTokens($mailContent['text'], $contact, FALSE, $tokens['text'], FALSE, $escapeSmarty);
+      $mailContent['html'] = CRM_Utils_Token::replaceContactTokens($mailContent['html'], $contact, FALSE, $tokens['html'], FALSE, $escapeSmarty);
 
       $contactArray = [$contactID => $contact];
       CRM_Utils_Hook::tokenValues($contactArray,
@@ -535,20 +540,30 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
       $mailContent['html'] = CRM_Utils_Token::replaceHookTokens($mailContent['html'], $contact, $categories, TRUE);
     }
 
-    // strip whitespace from ends and turn into a single line
-    $mailContent['subject'] = "{strip}{$mailContent['subject']}{/strip}";
+    // Normally Smarty is run, but it can be disabled using the disableSmarty
+    // parameter, which may be useful for non-core uses of MessageTemplate.send
+    // In particular it helps with the mosaicomsgtpl extension.
+    if (!$params['disableSmarty']) {
+      // strip whitespace from ends and turn into a single line
+      $mailContent['subject'] = "{strip}{$mailContent['subject']}{/strip}";
 
-    // parse the three elements with Smarty
-    $smarty = CRM_Core_Smarty::singleton();
-    foreach ($params['tplParams'] as $name => $value) {
-      $smarty->assign($name, $value);
+      // parse the three elements with Smarty
+      $smarty = CRM_Core_Smarty::singleton();
+      foreach ($params['tplParams'] as $name => $value) {
+        $smarty->assign($name, $value);
+      }
+      foreach ([
+        'subject',
+        'text',
+        'html',
+      ] as $elem) {
+        $mailContent[$elem] = $smarty->fetch("string:{$mailContent[$elem]}");
+      }
     }
-    foreach ([
-      'subject',
-      'text',
-      'html',
-    ] as $elem) {
-      $mailContent[$elem] = $smarty->fetch("string:{$mailContent[$elem]}");
+    else {
+      // Since we're not relying on Smarty for this function, we DIY.
+      // strip whitespace from ends and turn into a single line
+      $mailContent['subject'] = trim(preg_replace('/[\r\n]+/', ' ', $mailContent['subject']));
     }
 
     // send the template, honouring the target user’s preferences (if any)
diff --git a/civicrm/CRM/Core/BAO/PrevNextCache.php b/civicrm/CRM/Core/BAO/PrevNextCache.php
index 4eae51e91a1333a47ab2ddc1d3374b192161763f..f84454ae72ed7e43fc66aa30bb71076bebebd709 100644
--- a/civicrm/CRM/Core/BAO/PrevNextCache.php
+++ b/civicrm/CRM/Core/BAO/PrevNextCache.php
@@ -121,32 +121,6 @@ WHERE  cachekey     = %3 AND
     CRM_Core_DAO::executeQuery($sql, $params);
   }
 
-  /**
-   * Delete pair from the previous next cache table to remove it from further merge consideration.
-   *
-   * The pair may have been flipped, so make sure we delete using both orders
-   *
-   * @param int $id1
-   * @param int $id2
-   * @param string $cacheKey
-   */
-  public static function deletePair($id1, $id2, $cacheKey = NULL) {
-    $sql = "DELETE FROM civicrm_prevnext_cache WHERE  entity_table = 'civicrm_contact'";
-
-    $pair = "(entity_id1 = %2 AND entity_id2 = %3) OR (entity_id1 = %3 AND entity_id2 = %2)";
-    $sql .= " AND ( {$pair} )";
-    $params[2] = [$id1, 'Integer'];
-    $params[3] = [$id2, 'Integer'];
-
-    if (isset($cacheKey)) {
-      $sql .= " AND cachekey LIKE %4";
-      // used % to address any row with conflict-cacheKey e.g "merge Individual_8_0_conflicts"
-      $params[4] = ["{$cacheKey}%", 'String'];
-    }
-
-    CRM_Core_DAO::executeQuery($sql, $params);
-  }
-
   /**
    * Mark contacts as being in conflict.
    *
diff --git a/civicrm/CRM/Core/BAO/SchemaHandler.php b/civicrm/CRM/Core/BAO/SchemaHandler.php
index 5523858d6ac9fa69be7ed35e13f216d9b41daa68..a6c9845a224de45e2feb8e740d16d68a553232f7 100644
--- a/civicrm/CRM/Core/BAO/SchemaHandler.php
+++ b/civicrm/CRM/Core/BAO/SchemaHandler.php
@@ -592,7 +592,8 @@ MODIFY      {$columnName} varchar( $length )
    */
   public static function checkFKExists($table_name, $constraint_name) {
     $config = CRM_Core_Config::singleton();
-    $dbUf = DB::parseDSN($config->dsn);
+    $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn);
+    $dbUf = DB::parseDSN($dsn);
     $query = "
       SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
       WHERE TABLE_SCHEMA = %1
@@ -796,30 +797,51 @@ MODIFY      {$columnName} varchar( $length )
    *
    * @param bool $revert
    *   Being able to revert if primarily for unit testing.
+   * @param array $patterns
+   *   Defaults to ['civicrm\_%'] but can be overridden to specify any pattern. eg ['civicrm\_%', 'civi%\_%', 'veda%\_%'].
+   * @param array $databaseList
+   *   Allows you to specify an alternative database to the configured CiviCRM database.
    *
    * @return bool
    */
-  public static function migrateUtf8mb4($revert = FALSE) {
+  public static function migrateUtf8mb4($revert = FALSE, $patterns = ['civicrm\_%'], $databaseList = NULL) {
     $newCharSet = $revert ? 'utf8' : 'utf8mb4';
     $newCollation = $revert ? 'utf8_unicode_ci' : 'utf8mb4_unicode_ci';
     $newBinaryCollation = $revert ? 'utf8_bin' : 'utf8mb4_bin';
     $tables = [];
     $dao = new CRM_Core_DAO();
-    $database = $dao->_database;
-    CRM_Core_DAO::executeQuery("ALTER DATABASE $database CHARACTER SET = $newCharSet COLLATE = $newCollation");
-    $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS WHERE Engine = 'InnoDB' AND Name LIKE 'civicrm\_%'");
-    while ($dao->fetch()) {
-      $tables[$dao->Name] = [
-        'Engine' => $dao->Engine,
-      ];
-    }
-    $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN);
-    $logging_database = $dsn['database'];
-    $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `$logging_database` WHERE Engine <> 'MyISAM' AND Name LIKE 'log\_civicrm\_%'");
-    while ($dao->fetch()) {
-      $tables["$logging_database.{$dao->Name}"] = [
-        'Engine' => $dao->Engine,
-      ];
+    $databases = $databaseList ?? [$dao->_database];
+
+    $tableNameLikePatterns = [];
+    $logTableNameLikePatterns = [];
+
+    foreach ($patterns as $pattern) {
+      $pattern = CRM_Utils_Type::escape($pattern, 'String');
+      $tableNameLikePatterns[] = "Name LIKE '{$pattern}'";
+      $logTableNameLikePatterns[] = "Name LIKE 'log\_{$pattern}'";
+    }
+
+    foreach ($databases as $database) {
+      CRM_Core_DAO::executeQuery("ALTER DATABASE $database CHARACTER SET = $newCharSet COLLATE = $newCollation");
+      $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `{$database}` WHERE Engine = 'InnoDB' AND (" . implode(' OR ', $tableNameLikePatterns) . ")");
+      while ($dao->fetch()) {
+        $tables["{$database}.{$dao->Name}"] = [
+          'Engine' => $dao->Engine,
+        ];
+      }
+    }
+    // If we specified a list of databases assume the user knows what they are doing.
+    // If they specify the database they should also specify the pattern.
+    if (!$databaseList) {
+      $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN);
+      $dsn = DB::parseDSN($dsn);
+      $logging_database = $dsn['database'];
+      $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `{$logging_database}` WHERE Engine <> 'MyISAM' AND (" . implode(' OR ', $logTableNameLikePatterns) . ")");
+      while ($dao->fetch()) {
+        $tables["{$logging_database}.{$dao->Name}"] = [
+          'Engine' => $dao->Engine,
+        ];
+      }
     }
     foreach ($tables as $table => $param) {
       $query = "ALTER TABLE $table";
diff --git a/civicrm/CRM/Core/BAO/UFGroup.php b/civicrm/CRM/Core/BAO/UFGroup.php
index 34490de330646be9af9eb12d06d5bdf8a2aa1a3a..aca664795e7f2c5987402714aa230db5fe5519cc 100644
--- a/civicrm/CRM/Core/BAO/UFGroup.php
+++ b/civicrm/CRM/Core/BAO/UFGroup.php
@@ -1316,8 +1316,8 @@ class CRM_Core_BAO_UFGroup extends CRM_Core_DAO_UFGroup {
             'Multi-Select State/Province',
             'Multi-Select Country',
           ])) {
-            $valSeperator = CRM_Core_DAO::VALUE_SEPARATOR;
-            $selectedOptions = explode($valSeperator, $params[$index]);
+            $valSeparator = CRM_Core_DAO::VALUE_SEPARATOR;
+            $selectedOptions = explode($valSeparator, $params[$index]);
 
             foreach ($selectedOptions as $key => $multiOption) {
               if ($multiOption) {
@@ -2982,7 +2982,7 @@ AND    ( entity_id IS NULL OR entity_id <= 0 )
 
   /**
    * Update the profile type 'group_type' as per profile fields including group types and group subtype values.
-   * Build and store string like: group_type1,group_type2[VALUE_SEPERATOR]group_type1Type:1:2:3,group_type2Type:1:2
+   * Build and store string like: group_type1,group_type2[VALUE_SEPARATOR]group_type1Type:1:2:3,group_type2Type:1:2
    *
    * FIELDS                                                   GROUP_TYPE
    * BirthDate + Email                                        Individual,Contact
diff --git a/civicrm/CRM/Core/CodeGen/DAO.php b/civicrm/CRM/Core/CodeGen/DAO.php
index 69cc8d0e479ac4d59b5479f9e01fb027ca1f8111..9865d9e5f8be8dee6d1c11a6b67cfde800a8988c 100644
--- a/civicrm/CRM/Core/CodeGen/DAO.php
+++ b/civicrm/CRM/Core/CodeGen/DAO.php
@@ -26,6 +26,10 @@ class CRM_Core_CodeGen_DAO extends CRM_Core_CodeGen_BaseTask {
    */
   private $tsFunctionName;
 
+  private $useHelper = '';
+
+  private $ext = "'civicrm'";
+
   /**
    * CRM_Core_CodeGen_DAO constructor.
    *
@@ -37,6 +41,12 @@ class CRM_Core_CodeGen_DAO extends CRM_Core_CodeGen_BaseTask {
     parent::__construct($config);
     $this->name = $name;
     $this->tsFunctionName = $tsFunctionName;
+    // If this DAO belongs to an extension, add `use` statement and define EXT constant.
+    if (strpos($tsFunctionName, '::ts')) {
+      $this->tsFunctionName = 'E::ts';
+      $this->useHelper = 'use \\' . explode('::', $tsFunctionName)[0] . ' as E;';
+      $this->ext = 'E::LONG_NAME';
+    }
   }
 
   /**
@@ -68,16 +78,8 @@ class CRM_Core_CodeGen_DAO extends CRM_Core_CodeGen_BaseTask {
       return;
     }
 
-    $template = new CRM_Core_CodeGen_Util_Template('php');
-    $template->assign('table', $this->tables[$this->name]);
-    if (empty($this->tables[$this->name]['index'])) {
-      $template->assign('indicesPhp', var_export([], 1));
-    }
-    else {
-      $template->assign('indicesPhp', var_export($this->tables[$this->name]['index'], 1));
-    }
+    $template = $this->getTemplate();
     $template->assign('genCodeChecksum', $this->getTableChecksum());
-    $template->assign('tsFunctionName', $this->tsFunctionName);
     $template->run('dao.tpl', $this->getAbsFileName());
   }
 
@@ -88,21 +90,31 @@ class CRM_Core_CodeGen_DAO extends CRM_Core_CodeGen_BaseTask {
    */
   public function getRaw() {
     if (!$this->raw) {
-      $template = new CRM_Core_CodeGen_Util_Template('php');
-      $template->assign('table', $this->tables[$this->name]);
-      if (empty($this->tables[$this->name]['index'])) {
-        $template->assign('indicesPhp', var_export([], 1));
-      }
-      else {
-        $template->assign('indicesPhp', var_export($this->tables[$this->name]['index'], 1));
-      }
+      $template = $this->getTemplate();
       $template->assign('genCodeChecksum', 'NEW');
-      $template->assign('tsFunctionName', $this->tsFunctionName);
       $this->raw = $template->fetch('dao.tpl');
     }
     return $this->raw;
   }
 
+  /**
+   * @return CRM_Core_CodeGen_Util_Template
+   */
+  private function getTemplate() {
+    $template = new CRM_Core_CodeGen_Util_Template('php');
+    $template->assign('table', $this->tables[$this->name]);
+    if (empty($this->tables[$this->name]['index'])) {
+      $template->assign('indicesPhp', var_export([], 1));
+    }
+    else {
+      $template->assign('indicesPhp', var_export($this->tables[$this->name]['index'], 1));
+    }
+    $template->assign('tsFunctionName', $this->tsFunctionName);
+    $template->assign('ext', $this->ext);
+    $template->assign('useHelper', $this->useHelper);
+    return $template;
+  }
+
   /**
    * Get relative file name.
    *
diff --git a/civicrm/CRM/Core/CodeGen/Specification.php b/civicrm/CRM/Core/CodeGen/Specification.php
index 0f4678d74acde15fe778eb0305bcd17b30471d37..4d2be52ad41a80a166a0a7e479ab6de497f6e767 100644
--- a/civicrm/CRM/Core/CodeGen/Specification.php
+++ b/civicrm/CRM/Core/CodeGen/Specification.php
@@ -212,6 +212,7 @@ class CRM_Core_CodeGen_Specification {
       'objectName' => $klass,
       'title' => $tableXML->title ?? self::nameToTitle($klass),
       'icon' => $tableXML->icon ?? NULL,
+      'add' => $tableXML->add ?? NULL,
       'labelName' => substr($name, 8),
       'className' => $this->classNames[$name],
       'bao' => ($useBao ? str_replace('DAO', 'BAO', $this->classNames[$name]) : $this->classNames[$name]),
diff --git a/civicrm/CRM/Core/CodeGen/Util/Template.php b/civicrm/CRM/Core/CodeGen/Util/Template.php
index ee0e09bd4baca3eab3ba6b010b9645a9c7abfab3..5febab786d75beff796a82725c290586d3913dd4 100644
--- a/civicrm/CRM/Core/CodeGen/Util/Template.php
+++ b/civicrm/CRM/Core/CodeGen/Util/Template.php
@@ -78,6 +78,7 @@ class CRM_Core_CodeGen_Util_Template {
         '=> true,' => '=> TRUE,',
         '=> false,' => '=> FALSE,',
         'static ::' => 'static::',
+        'use\\' => 'use \\',
       ];
       $contents = str_replace(array_keys($replacements), array_values($replacements), $contents);
       $contents = preg_replace('#(\s*)\\/\\*\\*#', "\n\$1/**", $contents);
diff --git a/civicrm/CRM/Core/DAO.php b/civicrm/CRM/Core/DAO.php
index a1cd6f7498915e29b1919cb62dd88662533a633a..d62fba1401e7961d9af93590f7527de490ca577d 100644
--- a/civicrm/CRM/Core/DAO.php
+++ b/civicrm/CRM/Core/DAO.php
@@ -161,9 +161,10 @@ class CRM_Core_DAO extends DB_DataObject {
   public static function init($dsn) {
     Civi::$statics[__CLASS__]['init'] = 1;
     $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
+    $dsn = CRM_Utils_SQL::autoSwitchDSN($dsn);
     $options['database'] = $dsn;
     $options['quote_identifiers'] = TRUE;
-    if (self::isSSLDSN($dsn)) {
+    if (CRM_Utils_SQL::isSSLDSN($dsn)) {
       // There are two different options arrays.
       $other_options = &PEAR::getStaticProperty('DB', 'options');
       $other_options['ssl'] = TRUE;
@@ -552,7 +553,8 @@ class CRM_Core_DAO extends DB_DataObject {
 
     // Exclude fields yet not added by pending upgrades
     $dbVer = \CRM_Core_BAO_Domain::version();
-    if ($fields && version_compare($dbVer, \CRM_Utils_System::version()) < 0) {
+    $daoExt = defined(static::class . '::EXT') ? constant(static::class . '::EXT') : NULL;
+    if ($fields && $daoExt === 'civicrm' && version_compare($dbVer, \CRM_Utils_System::version()) < 0) {
       $fields = array_filter($fields, function($field) use ($dbVer) {
         $add = $field['add'] ?? '1.0.0';
         if (substr_count($add, '.') < 2) {
@@ -1148,6 +1150,23 @@ class CRM_Core_DAO extends DB_DataObject {
     return $result;
   }
 
+  /**
+   * Checks if this DAO's table ought to exist.
+   *
+   * If there are pending DB updates, this function compares the CiviCRM version of the table to the current schema version.
+   *
+   * @return bool
+   * @throws CRM_Core_Exception
+   */
+  public static function tableHasBeenAdded() {
+    if (CRM_Utils_System::version() === CRM_Core_BAO_Domain::version()) {
+      return TRUE;
+    }
+    $daoExt = defined(static::class . '::EXT') ? constant(static::class . '::EXT') : NULL;
+    $daoVersion = defined(static::class . '::TABLE_ADDED') ? constant(static::class . '::TABLE_ADDED') : '1.0';
+    return !($daoExt === 'civicrm' && version_compare(CRM_Core_BAO_Domain::version(), $daoVersion, '<'));
+  }
+
   /**
    * Check if there is a given table in the database.
    *
@@ -1181,15 +1200,12 @@ LIKE %1
 
   /**
    * @param $version
-   *
+   * @deprecated
    * @return bool
    */
   public function checkVersion($version) {
-    $query = "
-SELECT version
-FROM   civicrm_domain
-";
-    $dbVersion = CRM_Core_DAO::singleValueQuery($query);
+    CRM_Core_Error::deprecatedFunctionWarning('CRM_Core_BAO_Domain::version');
+    $dbVersion = CRM_Core_BAO_Domain::version();
     return trim($version) == trim($dbVersion);
   }
 
@@ -1524,6 +1540,7 @@ FROM   civicrm_domain
     }
 
     if ($trapException) {
+      CRM_Core_Error::deprecatedFunctionWarning('calling functions should handle exceptions');
       $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
     }
 
@@ -1539,6 +1556,7 @@ FROM   civicrm_domain
     }
 
     if (is_a($result, 'DB_Error')) {
+      CRM_Core_Error::deprecatedFunctionWarning('calling functions should handle exceptions');
       return $result;
     }
 
@@ -2812,7 +2830,7 @@ SELECT contact_id
   /**
    * @see http://issues.civicrm.org/jira/browse/CRM-9150
    * support for other syntaxes is discussed in ticket but being put off for now
-   * @return array
+   * @return string[]
    */
   public static function acceptedSQLOperators() {
     return [
@@ -3111,21 +3129,4 @@ SELECT contact_id
     }
   }
 
-  /**
-   * Does the DSN indicate the connection should use ssl.
-   *
-   * @param string $dsn
-   *
-   * @return bool
-   */
-  public static function isSSLDSN(string $dsn):bool {
-    // Note that ssl= below is not an official PEAR::DB option. It doesn't know
-    // what to do with it. We made it up because it's not required
-    // to have client-side certificates to use ssl, so here you can specify
-    // you want that by putting ssl=1 in the DSN string.
-    //
-    // Cast to bool in case of error which we interpret as no ssl.
-    return (bool) preg_match('/[\?&](key|cert|ca|capath|cipher|ssl)=/', $dsn);
-  }
-
 }
diff --git a/civicrm/CRM/Core/DAO/ActionLog.php b/civicrm/CRM/Core/DAO/ActionLog.php
index b4399596f4a1d998cae1d6918e92ef8d6104bb0b..f725a2558a186451449ae1ba0461a6e334d51646 100644
--- a/civicrm/CRM/Core/DAO/ActionLog.php
+++ b/civicrm/CRM/Core/DAO/ActionLog.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/ActionLog.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:1cc48318ce597a6cac7ff0e2a4518ff5)
+ * (GenCodeChecksum:2ccef6f7cc6a43d833301e93a2a0d61f)
  */
 
 /**
  * Database access object for the ActionLog entity.
  */
 class CRM_Core_DAO_ActionLog extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/ActionMapping.php b/civicrm/CRM/Core/DAO/ActionMapping.php
index 95f2c276ab78d36c4edc13e788298d552c97c0a8..8a61ce5bbc492cc9bfe0792b2e3fd10d088a0ef9 100644
--- a/civicrm/CRM/Core/DAO/ActionMapping.php
+++ b/civicrm/CRM/Core/DAO/ActionMapping.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/ActionMapping.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:be879c0936430ca0cebf0c8bf1c3dc7a)
+ * (GenCodeChecksum:037a3f26719a4774957814f28c499e60)
  */
 
 /**
  * Database access object for the ActionMapping entity.
  */
 class CRM_Core_DAO_ActionMapping extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/ActionSchedule.php b/civicrm/CRM/Core/DAO/ActionSchedule.php
index 6ac11664116c050a4a2f958214c9e5b5354e8ed5..666f773ba859b2c7226dc70327bbfe912dfb8dd1 100644
--- a/civicrm/CRM/Core/DAO/ActionSchedule.php
+++ b/civicrm/CRM/Core/DAO/ActionSchedule.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/ActionSchedule.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:4493f5d18b2b452d4a6b9ee4e15a15bf)
+ * (GenCodeChecksum:d05639de89f460efbb3474dcaf5acd27)
  */
 
 /**
  * Database access object for the ActionSchedule entity.
  */
 class CRM_Core_DAO_ActionSchedule extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.4';
 
   /**
    * Static instance to hold the table name.
@@ -407,6 +409,7 @@ class CRM_Core_DAO_ActionSchedule extends CRM_Core_DAO {
           'entity' => 'ActionSchedule',
           'bao' => 'CRM_Core_BAO_ActionSchedule',
           'localizable' => 0,
+          'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED,
           'add' => '3.4',
         ],
         'entity_status' => [
@@ -421,6 +424,7 @@ class CRM_Core_DAO_ActionSchedule extends CRM_Core_DAO {
           'entity' => 'ActionSchedule',
           'bao' => 'CRM_Core_BAO_ActionSchedule',
           'localizable' => 0,
+          'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED,
           'add' => '3.4',
         ],
         'start_action_offset' => [
@@ -615,6 +619,7 @@ class CRM_Core_DAO_ActionSchedule extends CRM_Core_DAO {
           'entity' => 'ActionSchedule',
           'bao' => 'CRM_Core_BAO_ActionSchedule',
           'localizable' => 0,
+          'serialize' => self::SERIALIZE_COMMA,
           'add' => '3.4',
         ],
         'recipient_listing' => [
diff --git a/civicrm/CRM/Core/DAO/Address.php b/civicrm/CRM/Core/DAO/Address.php
index f74252f6e24e9caebf26140916e0dc9e0ff10064..7151847db2f9c41fe4745643fc994f2bf2180648 100644
--- a/civicrm/CRM/Core/DAO/Address.php
+++ b/civicrm/CRM/Core/DAO/Address.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Address.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:8dc9868de21595676938b96deb836379)
+ * (GenCodeChecksum:a6b8f21dd3839de1ce1273e0910f0c8c)
  */
 
 /**
  * Database access object for the Address entity.
  */
 class CRM_Core_DAO_Address extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/AddressFormat.php b/civicrm/CRM/Core/DAO/AddressFormat.php
index 436733d4bcacb17f1e1b13c73b6b3f83db5298b5..762b90b6a177136e61d4353dd7e0fb57a35f0251 100644
--- a/civicrm/CRM/Core/DAO/AddressFormat.php
+++ b/civicrm/CRM/Core/DAO/AddressFormat.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/AddressFormat.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:bd005804e8f957b74c4eafacea35792d)
+ * (GenCodeChecksum:1ce11647576d05acfc364969eddfcce4)
  */
 
 /**
  * Database access object for the AddressFormat entity.
  */
 class CRM_Core_DAO_AddressFormat extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Cache.php b/civicrm/CRM/Core/DAO/Cache.php
index 71704bd66a00669c65dd74454a828a83dbde9708..01bc33ea91c53c34348d14bf780e11976cf54ed2 100644
--- a/civicrm/CRM/Core/DAO/Cache.php
+++ b/civicrm/CRM/Core/DAO/Cache.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Cache.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:695d7ebea0e7103ff58dfbbff111dde6)
+ * (GenCodeChecksum:af1401f844c699c6ad35366a32a8db03)
  */
 
 /**
  * Database access object for the Cache entity.
  */
 class CRM_Core_DAO_Cache extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Component.php b/civicrm/CRM/Core/DAO/Component.php
index bcc2c8008a6ddfafc839d2e52c48177d77011828..f9b7b077f9ad6a15942caa88643cf4fe129c9962 100644
--- a/civicrm/CRM/Core/DAO/Component.php
+++ b/civicrm/CRM/Core/DAO/Component.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Component.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:1f7be99fb2d2adfebdf3faa2013666f4)
+ * (GenCodeChecksum:6c3fd2c8e875746c0ceffa499624f77c)
  */
 
 /**
  * Database access object for the Component entity.
  */
 class CRM_Core_DAO_Component extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Country.php b/civicrm/CRM/Core/DAO/Country.php
index eb77528b78f5d277e2543ec99e58edd05131dc5a..24d458b99cef0c31da41f71899cfa516e5b50612 100644
--- a/civicrm/CRM/Core/DAO/Country.php
+++ b/civicrm/CRM/Core/DAO/Country.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Country.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:d40139ce11551ce445103a7fe9d3d46e)
+ * (GenCodeChecksum:cdd80b394924586274cf4b91183d3637)
  */
 
 /**
  * Database access object for the Country entity.
  */
 class CRM_Core_DAO_Country extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/County.php b/civicrm/CRM/Core/DAO/County.php
index 38167c8050444a8dcba91f04f60de3fd31dcbf62..97d2fae58ea13c74485b2ab7c3547072d5095648 100644
--- a/civicrm/CRM/Core/DAO/County.php
+++ b/civicrm/CRM/Core/DAO/County.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/County.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:095ad13ad08bd1d3c12be5094239df32)
+ * (GenCodeChecksum:96f94dbbafff9a4e1f0ff276799fcbbd)
  */
 
 /**
  * Database access object for the County entity.
  */
 class CRM_Core_DAO_County extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/CustomField.php b/civicrm/CRM/Core/DAO/CustomField.php
index e866fd532c52d757df3cbe395e7c6711d44e4a85..698d34a4956dcf58c98788594edcc6003ee43d1c 100644
--- a/civicrm/CRM/Core/DAO/CustomField.php
+++ b/civicrm/CRM/Core/DAO/CustomField.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/CustomField.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:1acb9b3538bd3005b99e6af6d9ec062f)
+ * (GenCodeChecksum:4ded3c0d1a8e34502a5957ee74c4480a)
  */
 
 /**
  * Database access object for the CustomField entity.
  */
 class CRM_Core_DAO_CustomField extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/CustomGroup.php b/civicrm/CRM/Core/DAO/CustomGroup.php
index 8d38cd71d53e5c1fc21c6a095f6002a93b1b8688..12dd47769ebcc12105696d1b50de33d0fe7be493 100644
--- a/civicrm/CRM/Core/DAO/CustomGroup.php
+++ b/civicrm/CRM/Core/DAO/CustomGroup.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/CustomGroup.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:4f58c488f8213dab928db357bc88f478)
+ * (GenCodeChecksum:494d883be861157d8067e6a6c50c23f6)
  */
 
 /**
  * Database access object for the CustomGroup entity.
  */
 class CRM_Core_DAO_CustomGroup extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Dashboard.php b/civicrm/CRM/Core/DAO/Dashboard.php
index a54895fe9e1d0cd18ebe5f8a963a4c4a7f2e4f9a..a9d40d32437c04f176e4a46787d24f6a608e69b0 100644
--- a/civicrm/CRM/Core/DAO/Dashboard.php
+++ b/civicrm/CRM/Core/DAO/Dashboard.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Dashboard.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:8cc83b4a4fa0fe28868e33887824c4e5)
+ * (GenCodeChecksum:2d134bfa6938d2e8a8d8e25e99769823)
  */
 
 /**
  * Database access object for the Dashboard entity.
  */
 class CRM_Core_DAO_Dashboard extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Discount.php b/civicrm/CRM/Core/DAO/Discount.php
index 914c2d9147a952411ef52a63f78f4e8f60f12ca3..ed2f53e1e1d5e9cdb48909c5ac9af7c5bb5ab8fb 100644
--- a/civicrm/CRM/Core/DAO/Discount.php
+++ b/civicrm/CRM/Core/DAO/Discount.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Discount.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b240911ddcf71cc294fe02614887711c)
+ * (GenCodeChecksum:a23716379d3cccf678a9d8e423690e7c)
  */
 
 /**
  * Database access object for the Discount entity.
  */
 class CRM_Core_DAO_Discount extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Domain.php b/civicrm/CRM/Core/DAO/Domain.php
index b90d6978127c7111092ea252cff45938d2e87aeb..82ccf0f5a8f9508bb40d0e62b2f2800406f9bd04 100644
--- a/civicrm/CRM/Core/DAO/Domain.php
+++ b/civicrm/CRM/Core/DAO/Domain.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Domain.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:33e01928bc64aa36fb39bbbeea1579dc)
+ * (GenCodeChecksum:57a526de0b2bc02fed832a22dc50ad80)
  */
 
 /**
  * Database access object for the Domain entity.
  */
 class CRM_Core_DAO_Domain extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Email.php b/civicrm/CRM/Core/DAO/Email.php
index 92ff31e6ddb268efea8c12e4362a9dab0ccf3e79..7c0a55b812bdbb031ec7904f0752eb9a81ee3a94 100644
--- a/civicrm/CRM/Core/DAO/Email.php
+++ b/civicrm/CRM/Core/DAO/Email.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Email.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:6e34e9759f2a50eb22dea1fceb15090c)
+ * (GenCodeChecksum:866b627595adac9091080a4e4ab146bc)
  */
 
 /**
  * Database access object for the Email entity.
  */
 class CRM_Core_DAO_Email extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/EntityFile.php b/civicrm/CRM/Core/DAO/EntityFile.php
index efef635fb806f837cc8610bc0970818dfaf63b9f..0d32257d1e18d5c8f5d46a905147ac65011fbcef 100644
--- a/civicrm/CRM/Core/DAO/EntityFile.php
+++ b/civicrm/CRM/Core/DAO/EntityFile.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/EntityFile.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b9ac4293489f424584b9983d1304987f)
+ * (GenCodeChecksum:f2d4dfec2466ad664b4949983b1c7e58)
  */
 
 /**
  * Database access object for the EntityFile entity.
  */
 class CRM_Core_DAO_EntityFile extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/EntityTag.php b/civicrm/CRM/Core/DAO/EntityTag.php
index 7ef0feff7ecc089d35c53cbf9d75f74eb9a0af08..eb2be19b6666f88d5de59b2b0aac0e7f3266d1af 100644
--- a/civicrm/CRM/Core/DAO/EntityTag.php
+++ b/civicrm/CRM/Core/DAO/EntityTag.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/EntityTag.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:3f4e098850d5540fb6ddd3e04d0084fe)
+ * (GenCodeChecksum:58f15f695b38fa4cacfdf82d2734e0f0)
  */
 
 /**
  * Database access object for the EntityTag entity.
  */
 class CRM_Core_DAO_EntityTag extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Extension.php b/civicrm/CRM/Core/DAO/Extension.php
index 3a58bb43c856096780b8a1ccb02758ba7cb66c4d..c666e3ab0222b5338cbb22a59f118ae5cd61c424 100644
--- a/civicrm/CRM/Core/DAO/Extension.php
+++ b/civicrm/CRM/Core/DAO/Extension.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Extension.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:84798b0aba44e90cbdde1d65736c616d)
+ * (GenCodeChecksum:08d2151b75e68f334bd88475b58fab7b)
  */
 
 /**
  * Database access object for the Extension entity.
  */
 class CRM_Core_DAO_Extension extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/File.php b/civicrm/CRM/Core/DAO/File.php
index 51b0b79200ed21385b3bf9b077ad284b97824b07..dc6508d4a6e48fe001f6e36370e7970094a18448 100644
--- a/civicrm/CRM/Core/DAO/File.php
+++ b/civicrm/CRM/Core/DAO/File.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/File.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:5c8f93d416373dd32add73f0bbaa59cc)
+ * (GenCodeChecksum:aa0883a815a43dd250612348f3ec470e)
  */
 
 /**
  * Database access object for the File entity.
  */
 class CRM_Core_DAO_File extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/IM.php b/civicrm/CRM/Core/DAO/IM.php
index bee4ce002290ddfc3847c006e9d9b9405b5d31e9..958b4dda190c5e123d76685c1d6ad163c543240d 100644
--- a/civicrm/CRM/Core/DAO/IM.php
+++ b/civicrm/CRM/Core/DAO/IM.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/IM.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a01a1aec350383201dbc1c3f59e9b709)
+ * (GenCodeChecksum:da6b080a31b208a71635d272fabab7ec)
  */
 
 /**
  * Database access object for the IM entity.
  */
 class CRM_Core_DAO_IM extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Job.php b/civicrm/CRM/Core/DAO/Job.php
index 39d72b975480240ef3285f45663ef4f1a1cfb68b..ebdb62818e4e9a77330b8a13efeb1ddf57d0fc03 100644
--- a/civicrm/CRM/Core/DAO/Job.php
+++ b/civicrm/CRM/Core/DAO/Job.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Job.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:8f64b00207fd2c2f98c378c01e29b911)
+ * (GenCodeChecksum:6e3a5de515fda550b1b5aeb493c50f0b)
  */
 
 /**
  * Database access object for the Job entity.
  */
 class CRM_Core_DAO_Job extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/JobLog.php b/civicrm/CRM/Core/DAO/JobLog.php
index 59e75d9fabe4f1846915baa6d2f14cca6586450a..b59a98b1f5b2c5f509febbd870024fe0bc57c72c 100644
--- a/civicrm/CRM/Core/DAO/JobLog.php
+++ b/civicrm/CRM/Core/DAO/JobLog.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/JobLog.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:d5266d899de027b3411970632fd2d6ef)
+ * (GenCodeChecksum:eca8e7af1026dbfaf7beecb95ce02361)
  */
 
 /**
  * Database access object for the JobLog entity.
  */
 class CRM_Core_DAO_JobLog extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/LocBlock.php b/civicrm/CRM/Core/DAO/LocBlock.php
index fcac92b27c5e360f8d6d8a6ec0cae89b8edf34d9..154a2e776c611457313b7891800c34bfe2a1d868 100644
--- a/civicrm/CRM/Core/DAO/LocBlock.php
+++ b/civicrm/CRM/Core/DAO/LocBlock.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/LocBlock.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:fd6b47fdf91519058cc607ab736a8934)
+ * (GenCodeChecksum:8459c5a6d25e5c70e44de49b109a82fa)
  */
 
 /**
  * Database access object for the LocBlock entity.
  */
 class CRM_Core_DAO_LocBlock extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/LocationType.php b/civicrm/CRM/Core/DAO/LocationType.php
index b75a338c7d0cee15cfa49cd30be666cee24a9f2e..70648f67db31fbe838d863c08ef0e13b3fa679a5 100644
--- a/civicrm/CRM/Core/DAO/LocationType.php
+++ b/civicrm/CRM/Core/DAO/LocationType.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/LocationType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:06904ebbc78a929b34c17ba1bb580350)
+ * (GenCodeChecksum:325ccb933339bc909efc7d6b60c7186b)
  */
 
 /**
  * Database access object for the LocationType entity.
  */
 class CRM_Core_DAO_LocationType extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Log.php b/civicrm/CRM/Core/DAO/Log.php
index c9541016bfd376f55a0241d859a21306f47944b9..182d42f5d40e5979f3dd592939d85079a18213b4 100644
--- a/civicrm/CRM/Core/DAO/Log.php
+++ b/civicrm/CRM/Core/DAO/Log.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Log.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a102ecbdf345a1df748ea11fdd997b9e)
+ * (GenCodeChecksum:5dfdb2863ba1bc7b84288a522cdaef51)
  */
 
 /**
  * Database access object for the Log entity.
  */
 class CRM_Core_DAO_Log extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/MailSettings.php b/civicrm/CRM/Core/DAO/MailSettings.php
index 343c7e6bfdbca6abd97c818fb7bae4c885d27567..acc8278c190423109dc960345d58cb4c9e33bb72 100644
--- a/civicrm/CRM/Core/DAO/MailSettings.php
+++ b/civicrm/CRM/Core/DAO/MailSettings.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/MailSettings.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:455750fee6f1acbe50aeb19387f12158)
+ * (GenCodeChecksum:b43716d8c8e362738d8d3420e8fbe03d)
  */
 
 /**
  * Database access object for the MailSettings entity.
  */
 class CRM_Core_DAO_MailSettings extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Managed.php b/civicrm/CRM/Core/DAO/Managed.php
index 4d4cbafdfdcb25249ca14b50d1e86195d8a4863f..212817ec7860199c70106ef5e58520302f432bee 100644
--- a/civicrm/CRM/Core/DAO/Managed.php
+++ b/civicrm/CRM/Core/DAO/Managed.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Managed.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:939c0ed6422323450f5b9b00f8da85ad)
+ * (GenCodeChecksum:e6146e35f8c8321e600a4198cbd6949e)
  */
 
 /**
  * Database access object for the Managed entity.
  */
 class CRM_Core_DAO_Managed extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Mapping.php b/civicrm/CRM/Core/DAO/Mapping.php
index 5b7121f90fc0714d4f9b5285c434fb1904eae377..59505d1d5fb0ade1c846554322ecd57b50f377ef 100644
--- a/civicrm/CRM/Core/DAO/Mapping.php
+++ b/civicrm/CRM/Core/DAO/Mapping.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Mapping.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ca067ae4e0d6b7c01e9c24da7cff585a)
+ * (GenCodeChecksum:c97b13ea2aaccdf8ba13b6552ccb59f2)
  */
 
 /**
  * Database access object for the Mapping entity.
  */
 class CRM_Core_DAO_Mapping extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/MappingField.php b/civicrm/CRM/Core/DAO/MappingField.php
index c8e0f48dda9bd42137d18c47cb54affab50d3746..cd5f06e741b4969d8f6c9194cf12c558d941f1be 100644
--- a/civicrm/CRM/Core/DAO/MappingField.php
+++ b/civicrm/CRM/Core/DAO/MappingField.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/MappingField.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:c29cb464b46084c28cedf37c3757ffd1)
+ * (GenCodeChecksum:3702a3c3cb9cd696eb829d15f4676439)
  */
 
 /**
  * Database access object for the MappingField entity.
  */
 class CRM_Core_DAO_MappingField extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Menu.php b/civicrm/CRM/Core/DAO/Menu.php
index a0f1b6c6ea3a248b11c64602ffa2822706250d50..98f3f4ad1491e196def19bf19f93fab70a2fde00 100644
--- a/civicrm/CRM/Core/DAO/Menu.php
+++ b/civicrm/CRM/Core/DAO/Menu.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Menu.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:8eaee41a6e8192bd26fab7f4ef443c0c)
+ * (GenCodeChecksum:8be7941b4dccb08266109e3e1599159f)
  */
 
 /**
  * Database access object for the Menu entity.
  */
 class CRM_Core_DAO_Menu extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/MessageTemplate.php b/civicrm/CRM/Core/DAO/MessageTemplate.php
index 54b4664182cae7e39261edd4c9f60391c6021714..5c7965c83f83e91cc309268fa5289dbd8548c5e4 100644
--- a/civicrm/CRM/Core/DAO/MessageTemplate.php
+++ b/civicrm/CRM/Core/DAO/MessageTemplate.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/MessageTemplate.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:542e52d6f757c9e8adfa2a5ed07b6475)
+ * (GenCodeChecksum:68dd4ac3c9f098e3577dbed8d5a2a105)
  */
 
 /**
  * Database access object for the MessageTemplate entity.
  */
 class CRM_Core_DAO_MessageTemplate extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.6';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Navigation.php b/civicrm/CRM/Core/DAO/Navigation.php
index 81125e4f02b581b42dc2a786393219ec0dac62a0..b567777810a4405d38851f507252262671f64af7 100644
--- a/civicrm/CRM/Core/DAO/Navigation.php
+++ b/civicrm/CRM/Core/DAO/Navigation.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Navigation.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:39aa4b4d2c380d3a08c4931c3e497006)
+ * (GenCodeChecksum:34c3d3b834400b49f1b8c6c99a08c99e)
  */
 
 /**
  * Database access object for the Navigation entity.
  */
 class CRM_Core_DAO_Navigation extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Note.php b/civicrm/CRM/Core/DAO/Note.php
index c5cd339ea7d18af8b695cabff4e86b4714e0f9ad..748d1076f18eca09adf58db42d3dad389ec04900 100644
--- a/civicrm/CRM/Core/DAO/Note.php
+++ b/civicrm/CRM/Core/DAO/Note.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Note.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:0e43e07bfbf7de4e7fa48f4d24a923fd)
+ * (GenCodeChecksum:86e72396a497a58c1568d0d081435e75)
  */
 
 /**
  * Database access object for the Note entity.
  */
 class CRM_Core_DAO_Note extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/OpenID.php b/civicrm/CRM/Core/DAO/OpenID.php
index 02db0f94b5ff458bbcf238fb2e1d09eca6f31bde..1a61aba156032d6f376fec6bf0310a2e264e1f87 100644
--- a/civicrm/CRM/Core/DAO/OpenID.php
+++ b/civicrm/CRM/Core/DAO/OpenID.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/OpenID.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ed6e574083475c9caf6e8f5028510263)
+ * (GenCodeChecksum:4d60933113e2b5330dd8194e7ebe6ae4)
  */
 
 /**
  * Database access object for the OpenID entity.
  */
 class CRM_Core_DAO_OpenID extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/OptionGroup.php b/civicrm/CRM/Core/DAO/OptionGroup.php
index 28611f23acf74604942ef6c42037769aa884d2b0..9feb9fd8f107cf13be5610f4976fe49de1929943 100644
--- a/civicrm/CRM/Core/DAO/OptionGroup.php
+++ b/civicrm/CRM/Core/DAO/OptionGroup.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/OptionGroup.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:8a0474f902f423a6055399c438cd406c)
+ * (GenCodeChecksum:d0011ad2bb6c090eeb86d25916c5624b)
  */
 
 /**
  * Database access object for the OptionGroup entity.
  */
 class CRM_Core_DAO_OptionGroup extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/OptionValue.php b/civicrm/CRM/Core/DAO/OptionValue.php
index ed5bf88c5f753eb682277f146a1b9254959eb082..7f0c3644f208aa9db0ef521c0f066bb9de23a826 100644
--- a/civicrm/CRM/Core/DAO/OptionValue.php
+++ b/civicrm/CRM/Core/DAO/OptionValue.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/OptionValue.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:370198cee250edd2f29743a5b5667e9a)
+ * (GenCodeChecksum:e51b16ecfe5f8302c8610b7f5dfd55e5)
  */
 
 /**
  * Database access object for the OptionValue entity.
  */
 class CRM_Core_DAO_OptionValue extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Phone.php b/civicrm/CRM/Core/DAO/Phone.php
index 444c0b3a53e71a52fcde62618700bd558f835063..9a6b668620a60c9d0e8d8bbbca940cc6a33e0016 100644
--- a/civicrm/CRM/Core/DAO/Phone.php
+++ b/civicrm/CRM/Core/DAO/Phone.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Phone.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:3c1f1743128af72b009c6cf4effc1da2)
+ * (GenCodeChecksum:efdb60e03b54f246e73588b6eb99611d)
  */
 
 /**
  * Database access object for the Phone entity.
  */
 class CRM_Core_DAO_Phone extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
@@ -172,7 +174,7 @@ class CRM_Core_DAO_Phone extends CRM_Core_DAO {
         'location_type_id' => [
           'name' => 'location_type_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Phone Location Type'),
+          'title' => ts('Phone Location Type ID'),
           'description' => ts('Which Location does this phone belong to.'),
           'where' => 'civicrm_phone.location_type_id',
           'table_name' => 'civicrm_phone',
@@ -181,6 +183,7 @@ class CRM_Core_DAO_Phone extends CRM_Core_DAO {
           'localizable' => 0,
           'html' => [
             'type' => 'Select',
+            'label' => ts("Phone Location Type"),
           ],
           'pseudoconstant' => [
             'table' => 'civicrm_location_type',
@@ -289,7 +292,7 @@ class CRM_Core_DAO_Phone extends CRM_Core_DAO {
         'phone_type_id' => [
           'name' => 'phone_type_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Phone Type'),
+          'title' => ts('Phone Type ID'),
           'description' => ts('Which type of phone does this number belongs.'),
           'where' => 'civicrm_phone.phone_type_id',
           'export' => TRUE,
@@ -299,6 +302,7 @@ class CRM_Core_DAO_Phone extends CRM_Core_DAO {
           'localizable' => 0,
           'html' => [
             'type' => 'Select',
+            'label' => ts("Phone Type"),
           ],
           'pseudoconstant' => [
             'optionGroupName' => 'phone_type',
diff --git a/civicrm/CRM/Core/DAO/PreferencesDate.php b/civicrm/CRM/Core/DAO/PreferencesDate.php
index 4f50074007651af047622224456d710d13bd0638..b1d08e7cd045d04aacab815891eba79b58b35701 100644
--- a/civicrm/CRM/Core/DAO/PreferencesDate.php
+++ b/civicrm/CRM/Core/DAO/PreferencesDate.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/PreferencesDate.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:e950a7985ee12a8b5ec6e926e8a49b20)
+ * (GenCodeChecksum:21383b05b8c8e98ed1721aab06031907)
  */
 
 /**
  * Database access object for the PreferencesDate entity.
  */
 class CRM_Core_DAO_PreferencesDate extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/PrevNextCache.php b/civicrm/CRM/Core/DAO/PrevNextCache.php
index cb581127894658cce9f0bbbf3c4a4540f31d47e1..a39549db9e6c467e88ef7eefa66dd14b9feb57ef 100644
--- a/civicrm/CRM/Core/DAO/PrevNextCache.php
+++ b/civicrm/CRM/Core/DAO/PrevNextCache.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/PrevNextCache.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:3e1ec2353f61b542cca9c3c9efc73e80)
+ * (GenCodeChecksum:8086ffe55554b0fba698136fd6dee894)
  */
 
 /**
  * Database access object for the PrevNextCache entity.
  */
 class CRM_Core_DAO_PrevNextCache extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/PrintLabel.php b/civicrm/CRM/Core/DAO/PrintLabel.php
index c0d4463fe2120be7f58220c191a77bb4456ebcfa..129e80804da9c2a2c7a5bd52aaf16ec8bbf38b5b 100644
--- a/civicrm/CRM/Core/DAO/PrintLabel.php
+++ b/civicrm/CRM/Core/DAO/PrintLabel.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/PrintLabel.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b98543c0a9d27b4f6aef0ab432bb37e2)
+ * (GenCodeChecksum:484a16ebc1b881e7718bfcf139024ee7)
  */
 
 /**
  * Database access object for the PrintLabel entity.
  */
 class CRM_Core_DAO_PrintLabel extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/RecurringEntity.php b/civicrm/CRM/Core/DAO/RecurringEntity.php
index e6463e239dfcdd4a6f16d0d37bd5b473e4f3646d..d22aa96ac773d2b782c7236cbda2939ccd5c5516 100644
--- a/civicrm/CRM/Core/DAO/RecurringEntity.php
+++ b/civicrm/CRM/Core/DAO/RecurringEntity.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/RecurringEntity.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:4d0ee948935c36ddf9c0a04ed30ec44f)
+ * (GenCodeChecksum:b78474c715335f7689a9a5fcdcb5718e)
  */
 
 /**
  * Database access object for the RecurringEntity entity.
  */
 class CRM_Core_DAO_RecurringEntity extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.6';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Setting.php b/civicrm/CRM/Core/DAO/Setting.php
index 70ac634cfd6feb7a1a99485d2ecc1d7da960f135..160f781cda0f36f080989606a55240bffd23260d 100644
--- a/civicrm/CRM/Core/DAO/Setting.php
+++ b/civicrm/CRM/Core/DAO/Setting.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Setting.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:196dea902e132f8ba6ffb8b108ee1996)
+ * (GenCodeChecksum:c1fda2807e8265021ffaa490325a7e4f)
  */
 
 /**
  * Database access object for the Setting entity.
  */
 class CRM_Core_DAO_Setting extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/StateProvince.php b/civicrm/CRM/Core/DAO/StateProvince.php
index eed42f3859b0cd9cd472cec3e8a9ac7bdf708e0f..80a91c2e1f15debe29d19e8be6370a09f7f877e2 100644
--- a/civicrm/CRM/Core/DAO/StateProvince.php
+++ b/civicrm/CRM/Core/DAO/StateProvince.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/StateProvince.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:22bfd6264c17c80a3638b9860cb97767)
+ * (GenCodeChecksum:2dced9a7a3e6be3d05ea7b7babe4b113)
  */
 
 /**
  * Database access object for the StateProvince entity.
  */
 class CRM_Core_DAO_StateProvince extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/StatusPreference.php b/civicrm/CRM/Core/DAO/StatusPreference.php
index 471513595b1f13cfc54893fff5d797a16a822f7e..b06b206f24836be57396a808f7f8b63158e4d610 100644
--- a/civicrm/CRM/Core/DAO/StatusPreference.php
+++ b/civicrm/CRM/Core/DAO/StatusPreference.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/StatusPreference.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:48ca8492c6febf737281831d72147a38)
+ * (GenCodeChecksum:1fa80acc24bcb14df3947cba2daa930f)
  */
 
 /**
  * Database access object for the StatusPreference entity.
  */
 class CRM_Core_DAO_StatusPreference extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.7';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/SystemLog.php b/civicrm/CRM/Core/DAO/SystemLog.php
index 22d57c06a4556e31fd0f84587922e2523c5354bf..c18ee55ebca7dd4faa51523d4b4a31c9d9d98781 100644
--- a/civicrm/CRM/Core/DAO/SystemLog.php
+++ b/civicrm/CRM/Core/DAO/SystemLog.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/SystemLog.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:c90dd839d7756d95a85e38cadbcb40c9)
+ * (GenCodeChecksum:46d7f317ffb5d01d9cb22898ce38abb3)
  */
 
 /**
  * Database access object for the SystemLog entity.
  */
 class CRM_Core_DAO_SystemLog extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Tag.php b/civicrm/CRM/Core/DAO/Tag.php
index 09e7ec787d8664acda5bdb832a5b603ce081f233..65ae7a378b80abf5e8ce5bd8d2110b4f3b7853a2 100644
--- a/civicrm/CRM/Core/DAO/Tag.php
+++ b/civicrm/CRM/Core/DAO/Tag.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Tag.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:71590516eaae029ef71f6f4b3d7a800d)
+ * (GenCodeChecksum:7a2eb010fd96445604104b6ada9c0b99)
  */
 
 /**
  * Database access object for the Tag entity.
  */
 class CRM_Core_DAO_Tag extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Timezone.php b/civicrm/CRM/Core/DAO/Timezone.php
index d2cd010c881b045fbc7c6d158c969306bf535998..5f9abdfc6c34bb51d37ece90343520cc7d1b4a0b 100644
--- a/civicrm/CRM/Core/DAO/Timezone.php
+++ b/civicrm/CRM/Core/DAO/Timezone.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Timezone.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:fc76675346aa5a97d5ce798b9968ee58)
+ * (GenCodeChecksum:fb1089cb65c1587b1242b9d250c664f7)
  */
 
 /**
  * Database access object for the Timezone entity.
  */
 class CRM_Core_DAO_Timezone extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/UFField.php b/civicrm/CRM/Core/DAO/UFField.php
index 1d5821d996fd3ac0b2c676d4fc288448d53b7e97..a112e1c6f32f156dbe83f04b2d37f6eb8115be10 100644
--- a/civicrm/CRM/Core/DAO/UFField.php
+++ b/civicrm/CRM/Core/DAO/UFField.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/UFField.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a77a2bfe4b288e20b6929fd3a7532850)
+ * (GenCodeChecksum:e717ec384cfe13629b4fc440af2a99d5)
  */
 
 /**
  * Database access object for the UFField entity.
  */
 class CRM_Core_DAO_UFField extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/UFGroup.php b/civicrm/CRM/Core/DAO/UFGroup.php
index 500525099792d471308d0858245cca8c6731cb83..ae79d22da8e8343abe11e7d94e866bbf7d690beb 100644
--- a/civicrm/CRM/Core/DAO/UFGroup.php
+++ b/civicrm/CRM/Core/DAO/UFGroup.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/UFGroup.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:acf9d6ef6307d473c8a32775aa8e8e66)
+ * (GenCodeChecksum:1cac6e6b80a630f69ce25f709a20e4a3)
  */
 
 /**
  * Database access object for the UFGroup entity.
  */
 class CRM_Core_DAO_UFGroup extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/UFJoin.php b/civicrm/CRM/Core/DAO/UFJoin.php
index 0477c90288ecb2f186a1053c0408bace27f49000..98639423b142cbc65deae012b9a98c4eaec1e5e3 100644
--- a/civicrm/CRM/Core/DAO/UFJoin.php
+++ b/civicrm/CRM/Core/DAO/UFJoin.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/UFJoin.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:70a227711539c0b5fdea5349d18e6b7f)
+ * (GenCodeChecksum:191143bced279d48cf34cdf6cf85a5fb)
  */
 
 /**
  * Database access object for the UFJoin entity.
  */
 class CRM_Core_DAO_UFJoin extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/UFMatch.php b/civicrm/CRM/Core/DAO/UFMatch.php
index 6712ed2c008dab6e55a1a9634e53e0cea069aa08..fa08f60b222fc97675444b17341dc41929e2df83 100644
--- a/civicrm/CRM/Core/DAO/UFMatch.php
+++ b/civicrm/CRM/Core/DAO/UFMatch.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/UFMatch.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:4d249265c27fb6dbe50e0589ddfed7d2)
+ * (GenCodeChecksum:4fca2151c2ecbd762ac9e2f067f0030f)
  */
 
 /**
  * Database access object for the UFMatch entity.
  */
 class CRM_Core_DAO_UFMatch extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Website.php b/civicrm/CRM/Core/DAO/Website.php
index 678e898f992c8e209e8704b38aa2296281e4c8c9..e9415359c1143aced76d2b52b5caa65511a2b136 100644
--- a/civicrm/CRM/Core/DAO/Website.php
+++ b/civicrm/CRM/Core/DAO/Website.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Website.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:2b048fb746a5762a6e132fe7b1e8dd07)
+ * (GenCodeChecksum:9e449b1f3a997b63c79066bd5cd782ae)
  */
 
 /**
  * Database access object for the Website entity.
  */
 class CRM_Core_DAO_Website extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/WordReplacement.php b/civicrm/CRM/Core/DAO/WordReplacement.php
index 42c2051b312249daf57c339a9186984f57ccd119..65ac087b554357b7f7e862727dc7047e9bd2a0d4 100644
--- a/civicrm/CRM/Core/DAO/WordReplacement.php
+++ b/civicrm/CRM/Core/DAO/WordReplacement.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/WordReplacement.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:d7e1414f899a3bcabd20806508741f55)
+ * (GenCodeChecksum:f4afc331da543068dba6d243d98b8e39)
  */
 
 /**
  * Database access object for the WordReplacement entity.
  */
 class CRM_Core_DAO_WordReplacement extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.4';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/DAO/Worldregion.php b/civicrm/CRM/Core/DAO/Worldregion.php
index 86b740932364db67a97e1dbe16405fe54fcd1193..1dd866013d6030c445f68c7d73acaeb18eb8dee8 100644
--- a/civicrm/CRM/Core/DAO/Worldregion.php
+++ b/civicrm/CRM/Core/DAO/Worldregion.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Core/Worldregion.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:8045c0ab7f128626a594fbb634e2f169)
+ * (GenCodeChecksum:0312ba4169a285839ec54d655ff5ceb3)
  */
 
 /**
  * Database access object for the Worldregion entity.
  */
 class CRM_Core_DAO_Worldregion extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Core/Error.php b/civicrm/CRM/Core/Error.php
index 04f7e8cd39fadc2d311ac8824c73b51340ca4034..98d1a29ded926a09bd22b8fa503e151b9f560f7a 100644
--- a/civicrm/CRM/Core/Error.php
+++ b/civicrm/CRM/Core/Error.php
@@ -586,6 +586,9 @@ class CRM_Core_Error extends PEAR_ErrorStack {
       if ($config->userSystem->is_drupal and function_exists('watchdog')) {
         watchdog('civicrm', '%message', ['%message' => $message], $priority ?? WATCHDOG_DEBUG);
       }
+      elseif ($config->userSystem->is_drupal and CIVICRM_UF == 'Drupal8') {
+        \Drupal::logger('civicrm')->log($priority ?? \Drupal\Core\Logger\RfcLogLevel::DEBUG, '%message', ['%message' => $message]);
+      }
     }
 
     return $str;
@@ -597,13 +600,15 @@ class CRM_Core_Error extends PEAR_ErrorStack {
    * @param string $string
    */
   public static function debug_query($string) {
-    if (defined('CIVICRM_DEBUG_LOG_QUERY')) {
-      if (CIVICRM_DEBUG_LOG_QUERY === 'backtrace') {
-        CRM_Core_Error::backtrace($string, TRUE);
-      }
-      elseif (CIVICRM_DEBUG_LOG_QUERY) {
-        CRM_Core_Error::debug_var('Query', $string, TRUE, TRUE, 'sql_log', PEAR_LOG_DEBUG);
-      }
+    if (!defined('CIVICRM_DEBUG_LOG_QUERY')) {
+      // TODO: When its updated to support getenv(), call CRM_Utils_Constant::value('CIVICRM_DEBUG_LOG_QUERY', FALSE)
+      define('CIVICRM_DEBUG_LOG_QUERY', getenv('CIVICRM_DEBUG_LOG_QUERY'));
+    }
+    if (CIVICRM_DEBUG_LOG_QUERY === 'backtrace') {
+      CRM_Core_Error::backtrace($string, TRUE);
+    }
+    elseif (CIVICRM_DEBUG_LOG_QUERY) {
+      CRM_Core_Error::debug_var('Query', $string, TRUE, TRUE, 'sql_log', PEAR_LOG_DEBUG);
     }
   }
 
diff --git a/civicrm/CRM/Core/Exception/PrematureExitException.php b/civicrm/CRM/Core/Exception/PrematureExitException.php
index e0eee738b9260b510cc945a8b0ac16df475874a3..f46bbfff8e55b5b40464c7b7fc697b9b73d2f123 100644
--- a/civicrm/CRM/Core/Exception/PrematureExitException.php
+++ b/civicrm/CRM/Core/Exception/PrematureExitException.php
@@ -25,6 +25,13 @@
  */
 class CRM_Core_Exception_PrematureExitException extends RuntimeException {
 
+  /**
+   * Contextual data.
+   *
+   * @var array
+   */
+  public $errorData;
+
   /**
    * Construct the exception. Note: The message is NOT binary safe.
    *
diff --git a/civicrm/CRM/Core/Lock.php b/civicrm/CRM/Core/Lock.php
index da1366b4243a19c27330be702f6e8c19d1413504..894b913ab2d60094ab567f499086d2eb576e7003 100644
--- a/civicrm/CRM/Core/Lock.php
+++ b/civicrm/CRM/Core/Lock.php
@@ -111,7 +111,8 @@ class CRM_Core_Lock implements \Civi\Core\Lock\LockInterface {
    */
   public function __construct($name, $timeout = NULL, $serverWideLock = FALSE) {
     $config = CRM_Core_Config::singleton();
-    $dsnArray = DB::parseDSN($config->dsn);
+    $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn);
+    $dsnArray = DB::parseDSN($dsn);
     $database = $dsnArray['database'];
     $domainID = CRM_Core_Config::domainID();
     if ($serverWideLock) {
diff --git a/civicrm/CRM/Core/OptionGroup.php b/civicrm/CRM/Core/OptionGroup.php
index 89018c1a21cf987980be1c6d255c55fc97aa13e9..727f2b22141b0c8914ff29c5a534d8adec1fdd98 100644
--- a/civicrm/CRM/Core/OptionGroup.php
+++ b/civicrm/CRM/Core/OptionGroup.php
@@ -106,7 +106,12 @@ class CRM_Core_OptionGroup {
     $orderBy = 'weight'
   ) {
     $cache = CRM_Utils_Cache::singleton();
-    $cacheKey = self::createCacheKey($name, $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy);
+    if (in_array($name, self::$_domainIDGroups)) {
+      $cacheKey = self::createCacheKey($name, $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy, CRM_Core_Config::domainID());
+    }
+    else {
+      $cacheKey = self::createCacheKey($name, $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy);
+    }
 
     if (!$fresh) {
       // Fetch from static var
@@ -186,8 +191,7 @@ WHERE  v.option_group_id = g.id
    * @return string
    */
   protected static function createCacheKey($id) {
-    $cacheKey = "CRM_OG_" . preg_replace('/[^a-zA-Z0-9]/', '', $id) . '_' . md5(serialize(func_get_args()));
-    return $cacheKey;
+    return "CRM_OG_" . preg_replace('/[^a-zA-Z0-9]/', '', $id) . '_' . md5(serialize(func_get_args()));
   }
 
   /**
diff --git a/civicrm/CRM/Core/Payment/AuthorizeNet.php b/civicrm/CRM/Core/Payment/AuthorizeNet.php
index 5a9fcbc9924bbdb3b129113459c8f2245b160213..0cbbc2b93984506d9abd5c2bc0708b0911662823 100644
--- a/civicrm/CRM/Core/Payment/AuthorizeNet.php
+++ b/civicrm/CRM/Core/Payment/AuthorizeNet.php
@@ -193,7 +193,6 @@ class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment {
       default:
         // Success
         $params['trxn_id'] = !empty($response_fields[6]) ? $response_fields[6] : $this->getTestTrxnID();
-        $params['gross_amount'] = $response_fields[9];
         break;
     }
 
diff --git a/civicrm/CRM/Core/Payment/BaseIPN.php b/civicrm/CRM/Core/Payment/BaseIPN.php
index 27b0074814464f3a29b549dff6981386bdb2a272..fdfce40a0f58a7d0d154459a339c09715b2389e4 100644
--- a/civicrm/CRM/Core/Payment/BaseIPN.php
+++ b/civicrm/CRM/Core/Payment/BaseIPN.php
@@ -135,12 +135,6 @@ class CRM_Core_Payment_BaseIPN {
     if (!$this->loadObjects($input, $ids, $objects, $required, $paymentProcessorID)) {
       return FALSE;
     }
-    //the process is that the loadObjects is kind of hacked by loading the objects for the original contribution and then somewhat inconsistently using them for the
-    //current contribution. Here we ensure that the original contribution is available to the complete transaction function
-    //we don't want to fix this in the payment processor classes because we would have to fix all of them - so better to fix somewhere central
-    if (isset($objects['contributionRecur'])) {
-      $objects['first_contribution'] = $objects['contribution'];
-    }
     return TRUE;
   }
 
@@ -473,8 +467,12 @@ class CRM_Core_Payment_BaseIPN {
    * @throws \CRM_Core_Exception
    * @throws \CiviCRM_API3_Exception
    */
-  public function completeTransaction(&$input, &$ids, &$objects) {
-    CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects);
+  public function completeTransaction($input, $ids, $objects) {
+    CRM_Contribute_BAO_Contribution::completeOrder($input, [
+      'related_contact' => $ids['related_contact'] ?? NULL,
+      'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL,
+      'contributionRecur' => !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL,
+    ], $objects);
   }
 
   /**
diff --git a/civicrm/CRM/Core/Payment/Dummy.php b/civicrm/CRM/Core/Payment/Dummy.php
index 4757fb296c6b0e7dedd5d34d92c388b8867b7312..7961d56e942f8c39424b242aa736e1fbe1d06fe1 100644
--- a/civicrm/CRM/Core/Payment/Dummy.php
+++ b/civicrm/CRM/Core/Payment/Dummy.php
@@ -97,7 +97,6 @@ class CRM_Core_Payment_Dummy extends CRM_Core_Payment {
 
     $params['trxn_id'] = $this->getTrxnID();;
 
-    $params['gross_amount'] = $propertyBag->getAmount();
     // Add a fee_amount so we can make sure fees are handled properly in underlying classes.
     $params['fee_amount'] = 1.50;
     $params['description'] = $this->getPaymentDescription($params);
diff --git a/civicrm/CRM/Core/Payment/PayJunction.php b/civicrm/CRM/Core/Payment/PayJunction.php
index 4fc8ec90056328788206450d6acb9863a260e85e..ce1b33442ce6fd0e5190906d9168cc7b96b51b2f 100644
--- a/civicrm/CRM/Core/Payment/PayJunction.php
+++ b/civicrm/CRM/Core/Payment/PayJunction.php
@@ -151,7 +151,6 @@ class CRM_Core_Payment_PayJunction extends CRM_Core_Payment {
     // Success
     $params['trxn_result_code'] = $pjpgResponse['dc_response_code'];
     $params['trxn_id'] = $pjpgResponse['dc_transaction_id'];
-    $params['gross_amount'] = $params['amount'];
 
     return $params;
   }
diff --git a/civicrm/CRM/Core/Payment/PayPalImpl.php b/civicrm/CRM/Core/Payment/PayPalImpl.php
index 38a6eb300d3e5e50773723945497d8dffbfeb468..2820e426c89054ebb3952a349a4bd5a2ae43a49d 100644
--- a/civicrm/CRM/Core/Payment/PayPalImpl.php
+++ b/civicrm/CRM/Core/Payment/PayPalImpl.php
@@ -349,7 +349,6 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
 
     /* Success */
     $params['trxn_id'] = $result['transactionid'];
-    $params['gross_amount'] = $result['amt'];
     $params['fee_amount'] = $result['feeamt'];
     $params['net_amount'] = $result['settleamt'] ?? NULL;
     if ($params['net_amount'] == 0 && $params['fee_amount'] != 0) {
@@ -972,6 +971,9 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
     $sub = empty($params['is_recur']) ? 'cgi-bin/webscr' : 'subscriptions';
     $paypalURL = "{$url}{$sub}?$uri";
 
+    // Allow each CMS to do a pre-flight check before redirecting to PayPal.
+    CRM_Core_Config::singleton()->userSystem->prePostRedirect();
+
     CRM_Utils_System::redirect($paypalURL);
   }
 
@@ -1130,8 +1132,13 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
     // The contribution form passes a 'button' but the event form might still set one of these fields.
     // @todo more standardisation & get paypal fully out of the form layer.
     $possibleExpressFields = [
+      // @todo - we think these top 2 are likely not required & it's still here
+      // on a precautionary basis.
+      // see https://github.com/civicrm/civicrm-core/pull/18680
       '_qf_Register_upload_express_x',
       '_qf_Payment_upload_express_x',
+      '_qf_Register_upload_express',
+      '_qf_Payment_upload_express',
       '_qf_Main_upload_express',
     ];
     if (array_intersect_key($params, array_fill_keys($possibleExpressFields, 1))) {
diff --git a/civicrm/CRM/Core/Payment/Realex.php b/civicrm/CRM/Core/Payment/Realex.php
index 6fa22c9ccd867c3dcaebe88fb6aa1c36f7248fc4..03ad040db4e24e7c7d2352a48704b16a36f2ae2f 100644
--- a/civicrm/CRM/Core/Payment/Realex.php
+++ b/civicrm/CRM/Core/Payment/Realex.php
@@ -175,7 +175,6 @@ class CRM_Core_Payment_Realex extends CRM_Core_Payment {
     $params['trxn_id'] = $response['PASREF'];
     $params['trxn_result_code'] = serialize($extras);
     $params['currencyID'] = $this->_getParam('currency');
-    $params['gross_amount'] = $this->_getParam('amount');
     $params['fee_amount'] = 0;
 
     return $params;
diff --git a/civicrm/CRM/Core/Payment/eWAY.php b/civicrm/CRM/Core/Payment/eWAY.php
index 9c37727053fcf94495047848ceae6b7000d38993..e8ec19da0dab8711c3d8b4e8f083cefef9ec1a17 100644
--- a/civicrm/CRM/Core/Payment/eWAY.php
+++ b/civicrm/CRM/Core/Payment/eWAY.php
@@ -340,7 +340,6 @@ class CRM_Core_Payment_eWAY extends CRM_Core_Payment {
       $beaglestatus = ': ' . $beaglestatus;
     }
     $params['trxn_result_code'] = $eWAYResponse->Status() . $beaglestatus;
-    $params['gross_amount'] = $eWAYResponse->Amount();
     $params['trxn_id'] = $eWAYResponse->TransactionNumber();
 
     return $params;
diff --git a/civicrm/CRM/Core/PrevNextCache/Sql.php b/civicrm/CRM/Core/PrevNextCache/Sql.php
index 5870c4424d5ebb144de716038d858d360daf037b..7f8696732fedef6bc99b698740d6fef20066f8da 100644
--- a/civicrm/CRM/Core/PrevNextCache/Sql.php
+++ b/civicrm/CRM/Core/PrevNextCache/Sql.php
@@ -40,8 +40,9 @@ class CRM_Core_PrevNextCache_Sql implements CRM_Core_PrevNextCache_Interface {
     $insertSQL = "
 INSERT INTO civicrm_prevnext_cache (cachekey, entity_id1, data)
 ";
-    $result = CRM_Core_DAO::executeQuery($insertSQL . $sql, $sqlParams, FALSE, NULL, FALSE, TRUE, TRUE);
+    $result = CRM_Core_DAO::executeQuery($insertSQL . $sql, $sqlParams, FALSE);
     if (is_a($result, 'DB_Error')) {
+      CRM_Core_Error::deprecatedFunctionWarning('errors are not expected to be returned');
       throw new CRM_Core_Exception($result->message);
     }
     return TRUE;
diff --git a/civicrm/CRM/Core/PseudoConstant.php b/civicrm/CRM/Core/PseudoConstant.php
index 758db214acba399f5fd3e18c918febbc28a0db37..c1f5af9e77f31390486d0ba084780327eae84e41 100644
--- a/civicrm/CRM/Core/PseudoConstant.php
+++ b/civicrm/CRM/Core/PseudoConstant.php
@@ -857,7 +857,7 @@ WHERE  id = %1";
    * @param bool $excludeHidden
    * @return array
    */
-  public static function nestedGroup($checkPermissions = TRUE, $groupType = NULL, $excludeHidden = TRUE) {
+  public static function nestedGroup(bool $checkPermissions = TRUE, $groupType = NULL, bool $excludeHidden = TRUE) {
     $groups = $checkPermissions ? self::group($groupType, $excludeHidden) : self::allGroup($groupType, $excludeHidden);
     return CRM_Contact_BAO_Group::getGroupsHierarchy($groups, NULL, '&nbsp;&nbsp;', TRUE);
   }
diff --git a/civicrm/CRM/Core/Region.php b/civicrm/CRM/Core/Region.php
index 242387ca65af1109ac8cffd45e6d5856d43bd221..06cece6bc9e7fd08e0da124e6e16fb39b945f710 100644
--- a/civicrm/CRM/Core/Region.php
+++ b/civicrm/CRM/Core/Region.php
@@ -48,10 +48,6 @@ class CRM_Core_Region {
    * @param string $name
    */
   public function __construct($name) {
-    // Templates injected into regions should normally be file names, but sometimes inline notation is handy.
-    require_once 'CRM/Core/Smarty/resources/String.php';
-    civicrm_smarty_register_string_resource();
-
     $this->_name = $name;
     $this->_snippets = [];
 
@@ -223,7 +219,6 @@ class CRM_Core_Region {
           break;
 
         default:
-          require_once 'CRM/Core/Exception.php';
           throw new CRM_Core_Exception(ts('Snippet type %1 is unrecognized',
             [1 => $snippet['type']]));
       }
@@ -254,48 +249,4 @@ class CRM_Core_Region {
     return 0;
   }
 
-  /**
-   * Add block of static HTML to a region.
-   *
-   * @param string $markup
-   *   HTML.
-   *
-   * public function addMarkup($markup) {
-   * return $this->add(array(
-   * 'type' => 'markup',
-   * 'markup' => $markup,
-   * ));
-   * }
-   *
-   * /**
-   * Add a Smarty template file to a region.
-   *
-   * Note: File is not evaluated until the page is rendered
-   *
-   * @param string $template
-   *   Path to the Smarty template file.
-   *
-   * public function addTemplate($template) {
-   * return $this->add(array(
-   * 'type' => 'template',
-   * 'template' => $template,
-   * ));
-   * }
-   *
-   * /**
-   * Use a callback function to extend a region.
-   *
-   * @param mixed $callback
-   * @param array $arguments
-   *   Optional, array of parameters for callback; if omitted, the default arguments are ($snippetSpec, $html).
-   *
-   * public function addCallback($callback, $arguments = FALSE) {
-   * return $this->add(array(
-   * 'type' => 'callback',
-   * 'callback' => $callback,
-   * 'arguments' => $arguments,
-   * ));
-   * }
-   */
-
 }
diff --git a/civicrm/CRM/Core/Report/Excel.php b/civicrm/CRM/Core/Report/Excel.php
index 52782e8132ac974133519310ec74ed923f37f53c..ef1c396e0c25471ecd181eed686835f27f234bfe 100644
--- a/civicrm/CRM/Core/Report/Excel.php
+++ b/civicrm/CRM/Core/Report/Excel.php
@@ -35,7 +35,7 @@ class CRM_Core_Report_Excel {
   public static function makeCSVTable($header, $rows, $outputHeader = TRUE) {
 
     $config = CRM_Core_Config::singleton();
-    $seperator = $config->fieldSeparator;
+    $separator = $config->fieldSeparator;
     $add_character = "\015\012";
 
     if ($outputHeader) {
@@ -68,7 +68,7 @@ class CRM_Core_Report_Excel {
               }
             }
 
-            $str = implode($seperator, $strArray);
+            $str = implode($separator, $strArray);
             $value = &$str;
           }
 
@@ -76,7 +76,7 @@ class CRM_Core_Report_Excel {
         }
 
         if ($colNo < $fields_cnt - 1) {
-          $schema_insert .= $seperator;
+          $schema_insert .= $separator;
         }
         $colNo++;
       }
diff --git a/civicrm/CRM/Custom/Import/Parser.php b/civicrm/CRM/Custom/Import/Parser.php
index bc934cb0c180029a8bb81dd258215efbafcbf616..0ee0ccf2e06286347aef673b9a7c1b0fa13f30db 100644
--- a/civicrm/CRM/Custom/Import/Parser.php
+++ b/civicrm/CRM/Custom/Import/Parser.php
@@ -312,7 +312,7 @@ abstract class CRM_Custom_Import_Parser extends CRM_Contact_Import_Parser {
   public function set($store, $mode = self::MODE_SUMMARY) {
     $store->set('fileSize', $this->_fileSize);
     $store->set('lineCount', $this->_lineCount);
-    $store->set('seperator', $this->_separator);
+    $store->set('separator', $this->_separator);
     $store->set('fields', $this->getSelectValues());
     $store->set('fieldTypes', $this->getSelectTypes());
 
diff --git a/civicrm/CRM/Cxn/DAO/Cxn.php b/civicrm/CRM/Cxn/DAO/Cxn.php
index f8190512c98e19e7c76ebaf3e4e2451bd89ea091..b0b70337bd8a60876f7eca1180c5177144df75cf 100644
--- a/civicrm/CRM/Cxn/DAO/Cxn.php
+++ b/civicrm/CRM/Cxn/DAO/Cxn.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Cxn/Cxn.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:bafb13cb7901c4f15b5348e2bf9a27b5)
+ * (GenCodeChecksum:059dd4994211085d728a9fc8b7d80803)
  */
 
 /**
  * Database access object for the Cxn entity.
  */
 class CRM_Cxn_DAO_Cxn extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.6';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Dedupe/BAO/Rule.php b/civicrm/CRM/Dedupe/BAO/Rule.php
index 0c40a52c58b61d03cf02092828c5e22065eeb1a5..b695d18b6e95f586d5962a949a698520874510a8 100644
--- a/civicrm/CRM/Dedupe/BAO/Rule.php
+++ b/civicrm/CRM/Dedupe/BAO/Rule.php
@@ -136,7 +136,7 @@ class CRM_Dedupe_BAO_Rule extends CRM_Dedupe_DAO_Rule {
       $from = "{$this->rule_table} t1";
       $str = 'NULL';
       if (isset($this->params[$this->rule_table][$this->rule_field])) {
-        $str = CRM_Utils_Type::escape($this->params[$this->rule_table][$this->rule_field], 'String');
+        $str = trim(CRM_Utils_Type::escape($this->params[$this->rule_table][$this->rule_field], 'String'));
       }
       if ($this->rule_length) {
         $where[] = "SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR('$str', 1, {$this->rule_length})";
diff --git a/civicrm/CRM/Dedupe/DAO/Exception.php b/civicrm/CRM/Dedupe/DAO/Exception.php
index 2d479b45dac46473c0b6dc7551ac4201884a61bc..cdbb87cc90c7b5ba3adc8a051224eec0f87b7fd3 100644
--- a/civicrm/CRM/Dedupe/DAO/Exception.php
+++ b/civicrm/CRM/Dedupe/DAO/Exception.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Dedupe/Exception.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ea7ca2395c6d2d927c35a7241d621cdd)
+ * (GenCodeChecksum:f4bc21b42b1b5c9cfb0ffa7d3eb46e65)
  */
 
 /**
  * Database access object for the Exception entity.
  */
 class CRM_Dedupe_DAO_Exception extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Dedupe/DAO/Rule.php b/civicrm/CRM/Dedupe/DAO/Rule.php
index df7521454ec10200eaaf3f26cd2203c1b8698835..dea3d38759d4c96ce5822ba452671fd266ea3c22 100644
--- a/civicrm/CRM/Dedupe/DAO/Rule.php
+++ b/civicrm/CRM/Dedupe/DAO/Rule.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Dedupe/Rule.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:e218ab6aaed8b58ef6dc189845a36755)
+ * (GenCodeChecksum:56abeb7ada5e3dfde910bc5033ca047d)
  */
 
 /**
  * Database access object for the Rule entity.
  */
 class CRM_Dedupe_DAO_Rule extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Dedupe/DAO/RuleGroup.php b/civicrm/CRM/Dedupe/DAO/RuleGroup.php
index 7caf59d70bd9375d206e97dfaae878ee842523ff..366ff64246d5e3d0ca78a01709223e41db7305b4 100644
--- a/civicrm/CRM/Dedupe/DAO/RuleGroup.php
+++ b/civicrm/CRM/Dedupe/DAO/RuleGroup.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Dedupe/RuleGroup.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:3c21b8b33d8d561b1104318fba0db930)
+ * (GenCodeChecksum:87a385df0b5bca8150117411f2c31a4a)
  */
 
 /**
  * Database access object for the RuleGroup entity.
  */
 class CRM_Dedupe_DAO_RuleGroup extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Dedupe/MergeHandler.php b/civicrm/CRM/Dedupe/MergeHandler.php
index 891bc96c90e1e8148d006f591a17510f25f85381..6b86b2f9b91d817164e9d62489775ce1c2b1ec86 100644
--- a/civicrm/CRM/Dedupe/MergeHandler.php
+++ b/civicrm/CRM/Dedupe/MergeHandler.php
@@ -33,6 +33,30 @@ class CRM_Dedupe_MergeHandler {
    */
   protected $toRemoveID;
 
+  /**
+   * Migration info array.
+   *
+   * This is a nasty bunch of data used in mysterious ways. We want to work to make it more
+   * sensible but for now we store it.
+   *
+   * @var array
+   */
+  protected $migrationInfo = [];
+
+  /**
+   * @return array
+   */
+  public function getMigrationInfo(): array {
+    return $this->migrationInfo;
+  }
+
+  /**
+   * @param array $migrationInfo
+   */
+  public function setMigrationInfo(array $migrationInfo) {
+    $this->migrationInfo = $migrationInfo;
+  }
+
   /**
    * @return mixed
    */
@@ -129,4 +153,41 @@ class CRM_Dedupe_MergeHandler {
     return \Civi::$statics[__CLASS__]['dynamic'];
   }
 
+  /**
+   * Get the location blocks designated to be moved during the merge.
+   *
+   * Note this is a refactoring step and future refactors should develop a more coherent array
+   *
+   * @return array
+   *   The format is ['address' => [0 => ['is_replace' => TRUE]], 'email' => [0...],[1....]
+   *   where the entity is address, the internal indexing for the addresses on both contact is 1
+   *   and the intent to replace the existing address is TRUE.
+   */
+  public function getLocationBlocksToMerge(): array {
+    $locBlocks = [];
+    foreach ($this->getMigrationInfo() as $key => $value) {
+      $isLocationField = (substr($key, 0, 14) === 'move_location_' and $value != NULL);
+      if (!$isLocationField) {
+        continue;
+      }
+      $locField = explode('_', $key);
+      $fieldName = $locField[2];
+      $fieldCount = $locField[3];
+
+      // Set up the operation type (add/overwrite)
+      // Ignore operation for websites
+      // @todo Tidy this up
+      $operation = 0;
+      if ($fieldName !== 'website') {
+        $operation = $this->getMigrationInfo()['location_blocks'][$fieldName][$fieldCount]['operation'] ?? NULL;
+      }
+      // default operation is overwrite.
+      if (!$operation) {
+        $operation = 2;
+      }
+      $locBlocks[$fieldName][$fieldCount]['is_replace'] = $operation === 2;
+    }
+    return $locBlocks;
+  }
+
 }
diff --git a/civicrm/CRM/Dedupe/Merger.php b/civicrm/CRM/Dedupe/Merger.php
index 74016a80a21c390e40d65133a8da2557825fea53..69827f16192f092175828e3fb217564ec6499268 100644
--- a/civicrm/CRM/Dedupe/Merger.php
+++ b/civicrm/CRM/Dedupe/Merger.php
@@ -1198,7 +1198,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
       $rows["move_$field"] = [
         'main' => self::getFieldValueAndLabel($field, $main)['label'],
         'other' => self::getFieldValueAndLabel($field, $other)['label'],
-        'title' => $fields[$field]['title'],
+        'title' => $fields[$field]['html']['label'] ?? $fields[$field]['title'],
       ];
 
       $value = self::getFieldValueAndLabel($field, $other)['value'];
@@ -1401,7 +1401,9 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         $removeTables = array_merge($moveTables, $relTables[substr($key, 5)]['tables']);
       }
     }
-    self::mergeLocations($mainId, $otherId, $migrationInfo);
+    $mergeHandler = new CRM_Dedupe_MergeHandler((int) $mainId, (int) $otherId);
+    $mergeHandler->setMigrationInfo($migrationInfo);
+    self::mergeLocations($mergeHandler);
 
     // **** Do contact related migrations
     // @todo - move all custom field processing to the move class & eventually have an
@@ -1409,7 +1411,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
     $customFieldBAO = new CRM_Core_BAO_CustomField();
     $customFieldBAO->move($otherId, $mainId, $submittedCustomFields);
     // add the related tables and unset the ones that don't sport any of the duplicate contact's info
-    $mergeHandler = new CRM_Dedupe_MergeHandler((int) $mainId, (int) $otherId);
+
     CRM_Dedupe_Merger::moveContactBelongings($mergeHandler, $moveTables, $tableOperations);
     unset($moveTables, $tableOperations);
 
@@ -1788,43 +1790,19 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    * The use of the new hook is tested, including the fact it is called before contributions are merged, as this
    * is likely to be significant data in merge hooks.
    *
-   * @param int $mainId
-   * @param int $otherId
-   *
-   * @param array $migrationInfo
-   *   Migration info for the merge. This is passed to the hook as informational only.
+   * @param \CRM_Dedupe_MergeHandler $mergeHandler
    */
-  public static function mergeLocations($mainId, $otherId, $migrationInfo) {
-    foreach ($migrationInfo as $key => $value) {
-      $isLocationField = (substr($key, 0, 14) === 'move_location_' and $value != NULL);
-      if (!$isLocationField) {
-        continue;
-      }
-      $locField = explode('_', $key);
-      $fieldName = $locField[2];
-      $fieldCount = $locField[3];
-
-      // Set up the operation type (add/overwrite)
-      // Ignore operation for websites
-      // @todo Tidy this up
-      $operation = 0;
-      if ($fieldName !== 'website') {
-        $operation = $migrationInfo['location_blocks'][$fieldName][$fieldCount]['operation'] ?? NULL;
-      }
-      // default operation is overwrite.
-      if (!$operation) {
-        $operation = 2;
-      }
-      $locBlocks[$fieldName][$fieldCount]['operation'] = $operation;
-    }
+  public static function mergeLocations($mergeHandler) {
+    $locBlocks = $mergeHandler->getLocationBlocksToMerge();
     $blocksDAO = [];
+    $migrationInfo = $mergeHandler->getMigrationInfo();
 
     // @todo Handle OpenID (not currently in API).
     if (!empty($locBlocks)) {
       $locationBlocks = self::getLocationBlockInfo();
 
-      $primaryBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mainId, ['is_primary' => 1]);
-      $billingBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mainId, ['is_billing' => 1]);
+      $primaryBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mergeHandler->getToKeepID(), ['is_primary' => 1]);
+      $billingBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mergeHandler->getToKeepID(), ['is_billing' => 1]);
 
       foreach ($locBlocks as $name => $block) {
         $blocksDAO[$name] = ['delete' => [], 'update' => []];
@@ -1845,7 +1823,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
 
           // For the block which belongs to other-contact, link the location block to main-contact
           $otherBlockDAO = new $daoName();
-          $otherBlockDAO->contact_id = $mainId;
+          $otherBlockDAO->contact_id = $mergeHandler->getToKeepID();
 
           // Get the ID of this block on the 'other' contact, otherwise skip
           $otherBlockDAO->id = $otherBlockId;
@@ -1884,9 +1862,8 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
             $otherBlockDAO->is_billing = 0;
           }
 
-          $operation = CRM_Utils_Array::value('operation', $values, 2);
           // overwrite - need to delete block which belongs to main-contact.
-          if (!empty($mainBlockId) && ($operation == 2)) {
+          if (!empty($mainBlockId) && $values['is_replace']) {
             $deleteDAO = new $daoName();
             $deleteDAO->id = $mainBlockId;
             $deleteDAO->find(TRUE);
@@ -1906,7 +1883,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
       }
     }
 
-    CRM_Utils_Hook::alterLocationMergeData($blocksDAO, $mainId, $otherId, $migrationInfo);
+    CRM_Utils_Hook::alterLocationMergeData($blocksDAO, $mergeHandler->getToKeepID(), $mergeHandler->getToRemoveID(), $migrationInfo);
     foreach ($blocksDAO as $blockDAOs) {
       if (!empty($blockDAOs['update'])) {
         foreach ($blockDAOs['update'] as $blockDAO) {
@@ -1964,12 +1941,31 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
       CRM_Core_BAO_PrevNextCache::markConflict($mainId, $otherId, $cacheKeyString, $conflicts, $mode);
     }
     else {
-      CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString);
+      self::deletePairFromPrevNextCache((int) $mainId, (int) $otherId);
     }
     self::releaseLocks($locks);
     return $resultStats;
   }
 
+  /**
+   * Delete merged pair from the previous next cache table as the are no longer a merge candidate.
+   *
+   * It's possible there may be more than one set of merge results cached, with different cache keys.
+   * Once we have merged a pair these should all go (even from a different merge search) as they
+   * can only be merged once.
+   *
+   * @param int $contactID1
+   * @param int $contactID2
+   */
+  protected static function deletePairFromPrevNextCache(int $contactID1, int $contactID2) {
+    CRM_Core_DAO::executeQuery("
+      DELETE FROM civicrm_prevnext_cache
+      WHERE  entity_table = 'civicrm_contact'
+        AND (entity_id1 = %1 AND entity_id2 = %2) OR (entity_id1 = %2 AND entity_id2 = %1)",
+      [1 => [$contactID1, 'Integer'], 2 => [$contactID2, 'Integer']]
+    );
+  }
+
   /**
    * Replace the pseudo QFKey with zero if it is present.
    *
diff --git a/civicrm/CRM/Event/BAO/Event.php b/civicrm/CRM/Event/BAO/Event.php
index 794e5d14e5a4b00c469db67c7c42805fea015a8b..b583fdd09926938c387655857f44b8053d664546 100644
--- a/civicrm/CRM/Event/BAO/Event.php
+++ b/civicrm/CRM/Event/BAO/Event.php
@@ -1158,6 +1158,8 @@ WHERE civicrm_event.is_active = 1
           'conference_sessions' => $sessions,
           'credit_card_number' => CRM_Utils_System::mungeCreditCard(CRM_Utils_Array::value('credit_card_number', $participantParams)),
           'credit_card_exp_date' => CRM_Utils_Date::mysqlToIso(CRM_Utils_Date::format(CRM_Utils_Array::value('credit_card_exp_date', $participantParams))),
+          'selfcancelxfer_time' => abs($values['event']['selfcancelxfer_time']),
+          'selfservice_preposition' => $values['event']['selfcancelxfer_time'] < 0 ? 'after' : 'before',
         ]);
 
         // CRM-13890 : NOTE wait list condition need to be given so that
diff --git a/civicrm/CRM/Event/BAO/Participant.php b/civicrm/CRM/Event/BAO/Participant.php
index 1f19c32f74b48ffa0907baad6f8910d97578b107..f18198b2096152b5c67be0cf2e5f43f158626bff 100644
--- a/civicrm/CRM/Event/BAO/Participant.php
+++ b/civicrm/CRM/Event/BAO/Participant.php
@@ -1877,7 +1877,6 @@ WHERE    civicrm_participant.contact_id = {$contactID} AND
    * Evaluate whether a participant record is eligible for self-service transfer/cancellation.  If so,
    * return additional participant/event details.
    *
-   * TODO: This function fails when the "hours until self-service" is less than zero.
    * @param int $participantId
    * @param string $url
    * @param bool $isBackOffice
@@ -1930,7 +1929,12 @@ WHERE    civicrm_participant.contact_id = {$contactID} AND
       $cancelDeadline = (new Datetime($start_date))->sub($cancelInterval);
       if ($timenow > $cancelDeadline) {
         $details['eligible'] = FALSE;
-        $details['ineligible_message'] = ts("Registration for this event cannot be cancelled or transferred less than %1 hours prior to the event's start time. Contact the event organizer if you have questions.", [1 => $time_limit]);
+        // Change the language of the status message based on whether the waitlist time limit is positive or negative.
+        $afterOrPrior = $time_limit < 0 ? 'after' : 'prior';
+        $moreOrLess = $time_limit < 0 ? 'more' : 'less';
+        $details['ineligible_message'] = ts("Registration for this event cannot be cancelled or transferred %1 than %2 hours %3 to the event's start time. Contact the event organizer if you have questions.",
+        [1 => $moreOrLess, 2 => $cancelHours, 3 => $afterOrPrior]);
+
       }
     }
     return $details;
diff --git a/civicrm/CRM/Event/Cart/DAO/Cart.php b/civicrm/CRM/Event/Cart/DAO/Cart.php
index 4683add13d075e9cc592939c65552d26cc362063..38da6b32272d210f3ee7c86576a908a48e075e56 100644
--- a/civicrm/CRM/Event/Cart/DAO/Cart.php
+++ b/civicrm/CRM/Event/Cart/DAO/Cart.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Event/Cart/Cart.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:c0fdef43850965dce80a73e66caa3ceb)
+ * (GenCodeChecksum:b4aacbeb6deddb31e520ce700e774db5)
  */
 
 /**
  * Database access object for the Cart entity.
  */
 class CRM_Event_Cart_DAO_Cart extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Event/Cart/DAO/EventInCart.php b/civicrm/CRM/Event/Cart/DAO/EventInCart.php
index e10ee7974f4ffdaf43dfbae6c4d4e02a77de9695..d1550cd54ad61d1c01258fd53aa70168817eebce 100644
--- a/civicrm/CRM/Event/Cart/DAO/EventInCart.php
+++ b/civicrm/CRM/Event/Cart/DAO/EventInCart.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Event/Cart/EventInCart.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a995a300028b34f7b866a41b2e2b96bf)
+ * (GenCodeChecksum:b1cb9524ae26740c93dda80d0cb4ff91)
  */
 
 /**
  * Database access object for the EventInCart entity.
  */
 class CRM_Event_Cart_DAO_EventInCart extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Event/DAO/Event.php b/civicrm/CRM/Event/DAO/Event.php
index adcf39c5d7aa45f2865139a9bc5dc21aa72f9b5d..a7febe5bfe94e9be9e40fd3e911f899c97da69a5 100644
--- a/civicrm/CRM/Event/DAO/Event.php
+++ b/civicrm/CRM/Event/DAO/Event.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Event/Event.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:331a210ceb36b5e9460705dbfbe71abf)
+ * (GenCodeChecksum:82ba48cbb804cf6f4b26fa50f07d44db)
  */
 
 /**
  * Database access object for the Event entity.
  */
 class CRM_Event_DAO_Event extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.7';
 
   /**
    * Static instance to hold the table name.
@@ -865,6 +867,7 @@ class CRM_Event_DAO_Event extends CRM_Core_DAO {
           'entity' => 'Event',
           'bao' => 'CRM_Event_BAO_Event',
           'localizable' => 0,
+          'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED,
           'html' => [
             'type' => 'Select',
           ],
diff --git a/civicrm/CRM/Event/DAO/Participant.php b/civicrm/CRM/Event/DAO/Participant.php
index 09f5bc0a9393665d28339b7a5d622897e33cfb81..94db537c3abe12954e9d5cbbf37d1316e1530255 100644
--- a/civicrm/CRM/Event/DAO/Participant.php
+++ b/civicrm/CRM/Event/DAO/Participant.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Event/Participant.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:5ae3aa69ac3d004f649038adbf993b8b)
+ * (GenCodeChecksum:bf8ed42264e81ccaef0ae236242990d0)
  */
 
 /**
  * Database access object for the Participant entity.
  */
 class CRM_Event_DAO_Participant extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.7';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Event/DAO/ParticipantPayment.php b/civicrm/CRM/Event/DAO/ParticipantPayment.php
index b45665ccf3d3f0bc42ceec7354d01a593d233691..ee0070dfc21351b1e72e740c038ad82c51942c89 100644
--- a/civicrm/CRM/Event/DAO/ParticipantPayment.php
+++ b/civicrm/CRM/Event/DAO/ParticipantPayment.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Event/ParticipantPayment.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ad58ecbb570f97b4a2d459d750e8f9b3)
+ * (GenCodeChecksum:0c828890e84b791e0432445eb2d01086)
  */
 
 /**
  * Database access object for the ParticipantPayment entity.
  */
 class CRM_Event_DAO_ParticipantPayment extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.7';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Event/DAO/ParticipantStatusType.php b/civicrm/CRM/Event/DAO/ParticipantStatusType.php
index a21a423e1a03bf43def614d85cea7c28924b0724..2b6e4f8f030f152499f879e9a2bad0f1d9c55d44 100644
--- a/civicrm/CRM/Event/DAO/ParticipantStatusType.php
+++ b/civicrm/CRM/Event/DAO/ParticipantStatusType.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Event/ParticipantStatusType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ede2497e706ee17bb6dce92b72a2c535)
+ * (GenCodeChecksum:4a3012f88c67826cb4264a3340e908ec)
  */
 
 /**
  * Database access object for the ParticipantStatusType entity.
  */
 class CRM_Event_DAO_ParticipantStatusType extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Event/Form/ManageEvent/Fee.php b/civicrm/CRM/Event/Form/ManageEvent/Fee.php
index 1462a3697c73d1362e23150fda24ab68f864cf91..40b40fc9126e655739992dda15e0c02448771af1 100644
--- a/civicrm/CRM/Event/Form/ManageEvent/Fee.php
+++ b/civicrm/CRM/Event/Form/ManageEvent/Fee.php
@@ -289,7 +289,7 @@ class CRM_Event_Form_ManageEvent_Fee extends CRM_Event_Form_ManageEvent {
 
       // value
       $this->add('text', "value[$i]", ts('Value'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'value'));
-      $this->addRule("value[$i]", ts('Please enter a valid money value for this field (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money');
+      $this->addRule("value[$i]", ts('Please enter a valid money value for this field (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money');
 
       // default
       $default[] = $this->createElement('radio', NULL, NULL, NULL, $i);
@@ -515,7 +515,7 @@ class CRM_Event_Form_ManageEvent_Fee extends CRM_Event_Form_ManageEvent {
       // value
       for ($j = 1; $j <= self::NUM_DISCOUNT; $j++) {
         $this->add('text', "discounted_value[$i][$j]", ts('Value'), ['size' => 10]);
-        $this->addRule("discounted_value[$i][$j]", ts('Please enter a valid money value for this field (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money');
+        $this->addRule("discounted_value[$i][$j]", ts('Please enter a valid money value for this field (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money');
       }
 
       // default
@@ -541,7 +541,7 @@ class CRM_Event_Form_ManageEvent_Fee extends CRM_Event_Form_ManageEvent {
     }
 
     if (!empty($params['payment_processor'])) {
-      $params['payment_processor'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, array_keys($params['payment_processor']));
+      $params['payment_processor'] = array_keys($params['payment_processor']);
     }
     else {
       $params['payment_processor'] = 'null';
@@ -810,7 +810,7 @@ class CRM_Event_Form_ManageEvent_Fee extends CRM_Event_Form_ManageEvent {
         $params['value'][$index] = CRM_Utils_Rule::cleanMoney(trim($value));
       }
     }
-    foreach ($params['discounted_value'] as $index => $discountedValueSet) {
+    foreach ($params['discounted_value'] ?? [] as $index => $discountedValueSet) {
       foreach ($discountedValueSet as $innerIndex => $value) {
         if (CRM_Utils_System::isNull($value)) {
           unset($params['discounted_value'][$index][$innerIndex]);
diff --git a/civicrm/CRM/Event/Form/Registration.php b/civicrm/CRM/Event/Form/Registration.php
index 97ecf1f6f8bf0ffb02cb650ed2064801d8da75b0..4b2692e67b515cb8221836b1e71692fc164ca479 100644
--- a/civicrm/CRM/Event/Form/Registration.php
+++ b/civicrm/CRM/Event/Form/Registration.php
@@ -18,6 +18,7 @@
  * This class generates form components for processing Event.
  */
 class CRM_Event_Form_Registration extends CRM_Core_Form {
+
   use CRM_Financial_Form_FrontEndPaymentFormTrait;
 
   /**
@@ -142,7 +143,9 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
   public $_isEventFull;
 
   public $_lineItem;
+
   public $_lineItemParticipantsCount;
+
   public $_availableRegistrations;
 
   /**
@@ -224,7 +227,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       $this->_values = $this->_fields = [];
 
       //retrieve event information
-      $params = array('id' => $this->_eventId);
+      $params = ['id' => $this->_eventId];
       CRM_Event_BAO_Event::retrieve($params, $this->_values['event']);
       // check for is_monetary status
       $isMonetary = $this->_values['event']['is_monetary'] ?? NULL;
@@ -294,12 +297,12 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       self::initEventFee($this, $this->_eventId);
 
       // get the profile ids
-      $ufJoinParams = array(
+      $ufJoinParams = [
         'entity_table' => 'civicrm_event',
         // CRM-4377: CiviEvent for the main participant, CiviEvent_Additional for additional participants
         'module' => 'CiviEvent',
         'entity_id' => $this->_eventId,
-      );
+      ];
       list($this->_values['custom_pre_id'],
         $this->_values['custom_post_id']
         ) = CRM_Core_BAO_UFJoin::getUFGroupIds($ufJoinParams);
@@ -336,7 +339,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       if ($this->_values['event']['is_monetary']) {
         CRM_Core_Payment_Form::setPaymentFieldsByProcessor($this, $this->_paymentProcessor);
       }
-      $params = array('entity_id' => $this->_eventId, 'entity_table' => 'civicrm_event');
+      $params = ['entity_id' => $this->_eventId, 'entity_table' => 'civicrm_event'];
       $this->_values['location'] = CRM_Core_BAO_Location::getValues($params, TRUE);
 
       $this->set('values', $this->_values);
@@ -344,9 +347,9 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
 
       $this->_availableRegistrations
         = CRM_Event_BAO_Participant::eventFull(
-          $this->_values['event']['id'], TRUE,
-          CRM_Utils_Array::value('has_waitlist', $this->_values['event'])
-        );
+        $this->_values['event']['id'], TRUE,
+        CRM_Utils_Array::value('has_waitlist', $this->_values['event'])
+      );
       $this->set('availableRegistrations', $this->_availableRegistrations);
     }
     $this->assign_by_ref('paymentProcessor', $this->_paymentProcessor);
@@ -429,14 +432,14 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     $this->assign('billingName', $name);
     $this->set('name', $name);
 
-    $vars = array(
+    $vars = [
       'amount',
       'currencyID',
       'credit_card_type',
       'trxn_id',
       'amount_level',
       'receive_date',
-    );
+    ];
 
     foreach ($vars as $v) {
       if (!empty($params[$v])) {
@@ -501,10 +504,10 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
 
       // we don't allow conflicting fields to be
       // configured via profile
-      $fieldsToIgnore = array(
+      $fieldsToIgnore = [
         'participant_fee_amount' => 1,
         'participant_fee_level' => 1,
-      );
+      ];
       if ($contactID) {
         //FIX CRM-9653
         if (is_array($id)) {
@@ -655,7 +658,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     }
     if ($isPaidEvent && empty($form->_values['fee'])) {
       if (CRM_Utils_System::getClassName($form) != 'CRM_Event_Form_Participant') {
-        CRM_Core_Error::statusBounce(ts('No Fee Level(s) or Price Set is configured for this event.<br />Click <a href=\'%1\'>CiviEvent >> Manage Event >> Configure >> Event Fees</a> to configure the Fee Level(s) or Price Set for this event.', array(1 => CRM_Utils_System::url('civicrm/event/manage/fee', 'reset=1&action=update&id=' . $form->_eventId))));
+        CRM_Core_Error::statusBounce(ts('No Fee Level(s) or Price Set is configured for this event.<br />Click <a href=\'%1\'>CiviEvent >> Manage Event >> Configure >> Event Fees</a> to configure the Fee Level(s) or Price Set for this event.', [1 => CRM_Utils_System::url('civicrm/event/manage/fee', 'reset=1&action=update&id=' . $form->_eventId)]));
       }
     }
   }
@@ -788,7 +791,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     }
 
     $participantFields = CRM_Event_DAO_Participant::fields();
-    $participantParams = array(
+    $participantParams = [
       'id' => $params['participant_id'] ?? NULL,
       'contact_id' => $contactID,
       'event_id' => $form->_eventId ? $form->_eventId : $params['event_id'],
@@ -808,7 +811,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       'discount_id' => $params['discount_id'] ?? NULL,
       'fee_currency' => $params['currencyID'] ?? NULL,
       'campaign_id' => $params['campaign_id'] ?? NULL,
-    );
+    ];
 
     if ($form->_action & CRM_Core_Action::PREVIEW || CRM_Utils_Array::value('mode', $params) == 'test') {
       $participantParams['is_test'] = 1;
@@ -888,7 +891,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     //first format the params.
     if ($singleFormParams) {
       $params = self::formatPriceSetParams($form, $params);
-      $params = array($params);
+      $params = [$params];
     }
 
     foreach ($params as $key => $values) {
@@ -976,10 +979,10 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
         $field = $priceSetDetails['fields'][$fieldId];
         if ($field['html_type'] == 'Text') {
           $fieldOption = current($field['options']);
-          $value = array($fieldOption['id'] => $value);
+          $value = [$fieldOption['id'] => $value];
         }
         else {
-          $value = array($value => TRUE);
+          $value = [$value => TRUE];
         }
       }
     }
@@ -1162,7 +1165,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
             foreach ($element->_options as $option) {
               if ($option['attr']['value'] === "crm_disabled_opt-{$value[0]}") {
                 $placeholder = html_entity_decode($option['text'], ENT_QUOTES, "UTF-8");
-                $element->updateAttributes(array('placeholder' => $placeholder));
+                $element->updateAttributes(['placeholder' => $placeholder]);
                 break;
               }
             }
@@ -1192,11 +1195,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     ) {
       return;
     }
-    foreach (array(
-      'constantValues',
-      'submitValues',
-      'defaultValues',
-    ) as $val) {
+    foreach (['constantValues', 'submitValues', 'defaultValues'] as $val) {
       $values = $form->{"_$val"};
       if (!is_array($values) || empty($values)) {
         continue;
@@ -1331,7 +1330,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
 
       //validate for price field selection.
       if (empty($fieldSelected[$pNum])) {
-        $errors[$pNum]['_qf_default'] = ts('Select at least one option from Event Fee(s).');
+        $errors[$pNum]['_qf_default'] = ts('SELECT at least one OPTION FROM EVENT Fee(s).');
       }
     }
 
@@ -1349,11 +1348,11 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
           }
           elseif (($optMax - $opDbCount) == 1) {
             $errors[$soldOutPnum[$optId]]["price_{$fieldId}"]
-              = ts('Sorry, currently only a single space is available for this option.', array(1 => ($optMax - $opDbCount)));
+              = ts('Sorry, currently only a single space is available for this option.', [1 => ($optMax - $opDbCount)]);
           }
           else {
             $errors[$soldOutPnum[$optId]]["price_{$fieldId}"]
-              = ts('Sorry, currently only %1 spaces are available for this option.', array(1 => ($optMax - $opDbCount)));
+              = ts('Sorry, currently only %1 spaces are available for this option.', [1 => ($optMax - $opDbCount)]);
           }
         }
       }
@@ -1373,7 +1372,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     $this->set('participantId', $this->_participantId);
 
     $ids = $participantValues = [];
-    $participantParams = array('id' => $this->_participantId);
+    $participantParams = ['id' => $this->_participantId];
     CRM_Event_BAO_Participant::getValues($participantParams, $participantValues, $ids);
     $this->_values['participant'] = $participantValues[$this->_participantId];
     $this->set('values', $this->_values);
@@ -1391,11 +1390,12 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
   /**
    * Check if event is valid.
    *
+   * @param string $redirect
+   *
    * @todo - combine this with CRM_Event_BAO_Event::validRegistrationRequest
    * (probably extract relevant values here & call that with them & handle bounces & redirects here -as
    * those belong in the form layer)
    *
-   * @param string $redirect
    */
   public function checkValidEvent($redirect = NULL) {
     // is the event active (enabled)?
@@ -1423,7 +1423,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       $startDate &&
       $startDate >= $now
     ) {
-      CRM_Core_Error::statusBounce(ts('Registration for this event begins on %1', array(1 => CRM_Utils_Date::customFormat(CRM_Utils_Array::value('registration_start_date', $this->_values['event'])))), $redirect, ts('Sorry'));
+      CRM_Core_Error::statusBounce(ts('Registration for this event begins on %1', [1 => CRM_Utils_Date::customFormat(CRM_Utils_Array::value('registration_start_date', $this->_values['event']))]), $redirect, ts('Sorry'));
     }
 
     $regEndDate = CRM_Utils_Date::processDate(CRM_Utils_Array::value('registration_end_date',
@@ -1435,7 +1435,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       if (empty($regEndDate)) {
         $endDate = CRM_Utils_Date::customFormat(CRM_Utils_Array::value('event_end_date', $this->_values['event']));
       }
-      CRM_Core_Error::statusBounce(ts('Registration for this event ended on %1', array(1 => $endDate)), $redirect, ts('Sorry'));
+      CRM_Core_Error::statusBounce(ts('Registration for this event ended on %1', [1 => $endDate]), $redirect, ts('Sorry'));
     }
   }
 
diff --git a/civicrm/CRM/Event/Form/Registration/Register.php b/civicrm/CRM/Event/Form/Registration/Register.php
index e44c213172e634a4e817eb5e348849d1e915880a..bd133450b0e12884b7b89e8d10103f80dfa489df 100644
--- a/civicrm/CRM/Event/Form/Registration/Register.php
+++ b/civicrm/CRM/Event/Form/Registration/Register.php
@@ -391,8 +391,7 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
       self::buildAmount($this);
     }
 
-    $pps = $this->getProcessors();
-    if ($this->getContactID() === 0 && !$this->_values['event']['is_multiple_registrations']) {
+    if ($contactID === 0 && !$this->_values['event']['is_multiple_registrations']) {
       //@todo we are blocking for multiple registrations because we haven't tested
       $this->addCIDZeroOptions();
     }
@@ -405,9 +404,7 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
 
     $this->assign('bypassPayment', $bypassPayment);
 
-    $userID = $this->getContactID();
-
-    if (!$userID) {
+    if (!$contactID) {
       $createCMSUser = FALSE;
 
       if ($this->_values['custom_pre_id']) {
diff --git a/civicrm/CRM/Event/Form/Task/Batch.php b/civicrm/CRM/Event/Form/Task/Batch.php
index 9a52015e42d35786af9a9454dc0119dbc03687f1..bc00eccc68d1a2a637eeadb938e95dacd8e4e84e 100644
--- a/civicrm/CRM/Event/Form/Task/Batch.php
+++ b/civicrm/CRM/Event/Form/Task/Batch.php
@@ -171,24 +171,22 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
               $customValue['extends_entity_column_value']
             );
           }
-          $entityColumnValueRole = $entityColumnValue[$roleId] ?? NULL;
-          $entityColumnValueEventType = in_array($eventTypeId, $entityColumnValue) ? $eventTypeId : NULL;
           if (($this->_roleCustomDataTypeID == $customValue['extends_entity_column_id']) &&
-            ($entityColumnValueRole)
+            in_array($roleId, $entityColumnValue)
           ) {
             CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $participantId);
           }
           elseif (($this->_eventNameCustomDataTypeID == $customValue['extends_entity_column_id']) &&
-            ($eventId == $entityColumnValueRole)
+            in_array($eventId, $entityColumnValue)
           ) {
             CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $participantId);
           }
           elseif ($this->_eventTypeCustomDataTypeID == $customValue['extends_entity_column_id'] &&
-            ($entityColumnValueEventType == $eventTypeId)
+            in_array($eventTypeId, $entityColumnValue)
           ) {
             CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $participantId);
           }
-          elseif (CRM_Utils_System::isNull($entityColumnValueRole)) {
+          elseif (CRM_Utils_System::isNull($entityColumnValue)) {
             CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $participantId);
           }
         }
@@ -262,14 +260,10 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
    * @param int $participantId
    * @param int $statusId
    *
-   * @return mixed
    * @throws \CRM_Core_Exception
    * @throws \CiviCRM_API3_Exception
    */
   public static function updatePendingOnlineContribution($participantId, $statusId) {
-    if (!$participantId || !$statusId) {
-      return NULL;
-    }
 
     $contributionId = CRM_Contribute_BAO_Contribution::checkOnlinePendingContribution($participantId,
       'Event'
@@ -294,7 +288,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
       $contributionStatusId = array_search('Cancelled', $contributionStatuses);
     }
 
-    if (!$contributionStatusId) {
+    if (!$contributionStatusId || !$participantId || !$contributionId) {
       return;
     }
 
@@ -307,9 +301,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
     ];
 
     //change related contribution status.
-    $updatedStatusId = self::updateContributionStatus($params);
-
-    return $updatedStatusId;
+    self::updateContributionStatus($params);
   }
 
   /**
@@ -317,7 +309,6 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
    *
    * @param array $params
    *
-   * @return NULL|int
    * @throws \CRM_Core_Exception
    * @throws \CiviCRM_API3_Exception
    * @throws \Exception
@@ -334,10 +325,6 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
     $componentName = $params['componentName'] ?? NULL;
     $contributionId = $params['contribution_id'] ?? NULL;
 
-    if (!$contributionId || !$componentId || !$componentName || !$statusId) {
-      return NULL;
-    }
-
     $input = $ids = $objects = [];
 
     //get the required ids.
@@ -391,13 +378,13 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
       $transaction = new CRM_Core_Transaction();
       $baseIPN->cancelled($objects, $transaction, $input);
       $transaction->commit();
-      return $statusId;
+      return;
     }
-    elseif ($statusId == $contributionStatuses['Failed']) {
+    if ($statusId == $contributionStatuses['Failed']) {
       $transaction = new CRM_Core_Transaction();
       $baseIPN->failed($objects, $transaction, $input);
       $transaction->commit();
-      return $statusId;
+      return;
     }
 
     // status is not pending
@@ -430,12 +417,14 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
     //complete the contribution.
     // @todo use the api - ie civicrm_api3('Contribution', 'completetransaction', $input);
     // as this method is not preferred / supported.
-    CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects);
+    CRM_Contribute_BAO_Contribution::completeOrder($input, [
+      'related_contact' => $ids['related_contact'] ?? NULL,
+      'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL,
+      'contributionRecur' => NULL,
+    ], $objects);
 
     // reset template values before processing next transactions
     $template->clearTemplateVars();
-
-    return $statusId;
   }
 
   /**
@@ -515,7 +504,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task {
         if ($statusChange) {
           CRM_Event_BAO_Participant::transitionParticipants([$key], $value['status_id'], $fromStatusId);
         }
-        if ($relatedStatusChange) {
+        if ($relatedStatusChange && $key && $value['status_id']) {
           //update related contribution status, CRM-4395
           self::updatePendingOnlineContribution($key, $value['status_id']);
         }
diff --git a/civicrm/CRM/Event/Import/Form/MapField.php b/civicrm/CRM/Event/Import/Form/MapField.php
index 8182aa80db4bb7f8f5eb28d91733d3669fd84535..50c3e1f11fd5e65cba91a63bf61fd0f06d7e9224 100644
--- a/civicrm/CRM/Event/Import/Form/MapField.php
+++ b/civicrm/CRM/Event/Import/Form/MapField.php
@@ -387,7 +387,7 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
     }
 
     $fileName = $this->controller->exportValue('DataSource', 'uploadFile');
-    $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
+    $separator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
 
     $mapperKeys = [];
@@ -461,7 +461,7 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
     }
 
     $parser = new CRM_Event_Import_Parser_Participant($mapperKeysMain);
-    $parser->run($fileName, $seperator, $mapper, $skipColumnHeader,
+    $parser->run($fileName, $separator, $mapper, $skipColumnHeader,
       CRM_Import_Parser::MODE_PREVIEW, $this->get('contactType')
     );
     // add all the necessary variables to the form
diff --git a/civicrm/CRM/Event/Import/Form/Preview.php b/civicrm/CRM/Event/Import/Form/Preview.php
index 0a7e5ca96d576c7125f519722d8b0d7a5c74ef63..629ed8e5cf84433e8ab0c5d2b9df29366ee4422d 100644
--- a/civicrm/CRM/Event/Import/Form/Preview.php
+++ b/civicrm/CRM/Event/Import/Form/Preview.php
@@ -95,7 +95,7 @@ class CRM_Event_Import_Form_Preview extends CRM_Import_Form_Preview {
    */
   public function postProcess() {
     $fileName = $this->controller->exportValue('DataSource', 'uploadFile');
-    $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
+    $separator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
     $invalidRowCount = $this->get('invalidRowCount');
     $conflictRowCount = $this->get('conflictRowCount');
@@ -119,7 +119,7 @@ class CRM_Event_Import_Form_Preview extends CRM_Import_Form_Preview {
       }
       $mapperFields[] = implode(' - ', $header);
     }
-    $parser->run($fileName, $seperator,
+    $parser->run($fileName, $separator,
       $mapperFields,
       $skipColumnHeader,
       CRM_Import_Parser::MODE_IMPORT,
diff --git a/civicrm/CRM/Event/Import/Parser.php b/civicrm/CRM/Event/Import/Parser.php
index 46e74961b971afc785211f7b57e85d69b0160e2d..097aa1313f1786d0a2a26f56fcf4739cdb18c1a0 100644
--- a/civicrm/CRM/Event/Import/Parser.php
+++ b/civicrm/CRM/Event/Import/Parser.php
@@ -30,7 +30,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser {
    *
    * @var string
    */
-  protected $_seperator;
+  protected $_separator;
 
   /**
    * Total number of lines in file.
@@ -48,7 +48,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser {
 
   /**
    * @param string $fileName
-   * @param string $seperator
+   * @param string $separator
    * @param $mapper
    * @param bool $skipColumnHeader
    * @param int $mode
@@ -60,7 +60,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser {
    */
   public function run(
     $fileName,
-    $seperator = ',',
+    $separator = ',',
     &$mapper,
     $skipColumnHeader = FALSE,
     $mode = self::MODE_PREVIEW,
@@ -89,7 +89,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser {
 
     $this->_haveColumnHeader = $skipColumnHeader;
 
-    $this->_seperator = $seperator;
+    $this->_separator = $separator;
 
     $fd = fopen($fileName, "r");
     if (!$fd) {
@@ -116,7 +116,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser {
     while (!feof($fd)) {
       $this->_lineCount++;
 
-      $values = fgetcsv($fd, 8192, $seperator);
+      $values = fgetcsv($fd, 8192, $separator);
       if (!$values) {
         continue;
       }
@@ -343,7 +343,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser {
   public function set($store, $mode = self::MODE_SUMMARY) {
     $store->set('fileSize', $this->_fileSize);
     $store->set('lineCount', $this->_lineCount);
-    $store->set('seperator', $this->_seperator);
+    $store->set('separator', $this->_separator);
     $store->set('fields', $this->getSelectValues());
     $store->set('fieldTypes', $this->getSelectTypes());
 
diff --git a/civicrm/CRM/Export/BAO/ExportProcessor.php b/civicrm/CRM/Export/BAO/ExportProcessor.php
index 1dc733d9fd534a65a9c23826f4612458fc15e9b9..c1231fecb7147a47accc22f85cccd0d8f639d481 100644
--- a/civicrm/CRM/Export/BAO/ExportProcessor.php
+++ b/civicrm/CRM/Export/BAO/ExportProcessor.php
@@ -639,6 +639,9 @@ class CRM_Export_BAO_ExportProcessor {
     $queryFields['world_region']['context'] = 'country';
     $queryFields['state_province']['context'] = 'province';
     $queryFields['contact_id'] = ['title' => ts('Contact ID'), 'type' => CRM_Utils_Type::T_INT];
+    // Set the label to gender for gender_id as we it's ... magic (not in a good way).
+    // In other places the query object offers e.g contribution_status & contribution_status_id
+    $queryFields['gender_id']['title'] = ts('Gender');
     $this->queryFields = $queryFields;
   }
 
@@ -971,7 +974,6 @@ class CRM_Export_BAO_ExportProcessor {
     if ($this->isHouseholdToSkip($iterationDAO->contact_id)) {
       return FALSE;
     }
-    $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
     $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
 
     $row = [];
@@ -1002,10 +1004,7 @@ class CRM_Export_BAO_ExportProcessor {
       if (property_exists($iterationDAO, $field)) {
         $fieldValue = $iterationDAO->$field;
         // to get phone type from phone type id
-        if ($field == 'phone_type_id' && isset($phoneTypes[$fieldValue])) {
-          $fieldValue = $phoneTypes[$fieldValue];
-        }
-        elseif ($field == 'provider_id' || $field == 'im_provider') {
+        if ($field == 'provider_id' || $field == 'im_provider') {
           $fieldValue = $imProviders[$fieldValue] ?? NULL;
         }
         elseif (strstr($field, 'master_id')) {
@@ -1152,7 +1151,7 @@ class CRM_Export_BAO_ExportProcessor {
             if (!empty($fieldSpec['context'])) {
               return $i18n->crm_translate($fieldValue, $fieldSpec);
             }
-            if (!empty($fieldSpec['pseudoconstant']) && !empty($fieldSpec['hasLocationType'])) {
+            if (!empty($fieldSpec['pseudoconstant']) && !empty($fieldSpec['hasLocationType']) && $fieldSpec['name'] !== 'phone_type_id') {
               if (!empty($fieldSpec['bao'])) {
                 $transformedValue = CRM_Core_PseudoConstant::getLabel($fieldSpec['bao'], $fieldSpec['name'], $fieldValue);
                 if ($transformedValue) {
@@ -2171,10 +2170,7 @@ WHERE  id IN ( $deleteIDString )
     foreach ($value as $relationField => $relationValue) {
       if (is_object($relDAO) && property_exists($relDAO, $relationField)) {
         $fieldValue = $relDAO->$relationField;
-        if ($relationField == 'phone_type_id') {
-          $fieldValue = $phoneTypes[$relationValue];
-        }
-        elseif ($relationField == 'provider_id') {
+        if ($relationField == 'provider_id') {
           $fieldValue = $imProviders[$relationValue] ?? NULL;
         }
         // CRM-13995
diff --git a/civicrm/CRM/Financial/BAO/Payment.php b/civicrm/CRM/Financial/BAO/Payment.php
index f841363f141ff6c8979f67f86e37fb8a99146c80..2b93c545e1ff604f83ca344eebc6b108481eaaa9 100644
--- a/civicrm/CRM/Financial/BAO/Payment.php
+++ b/civicrm/CRM/Financial/BAO/Payment.php
@@ -157,6 +157,7 @@ class CRM_Financial_BAO_Payment {
           'is_post_payment_create' => TRUE,
           'is_email_receipt' => $params['is_send_contribution_notification'],
           'trxn_date' => $params['trxn_date'],
+          'payment_instrument_id' => $paymentTrxnParams['payment_instrument_id'],
         ]);
         // Get the trxn
         $trxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC');
@@ -486,6 +487,9 @@ class CRM_Financial_BAO_Payment {
     if ($outstandingBalance !== 0.0) {
       $ratio = $params['total_amount'] / $outstandingBalance;
     }
+    elseif ($params['total_amount'] < 0) {
+      $ratio = $params['total_amount'] / (float) CRM_Core_BAO_FinancialTrxn::getTotalPayments($params['contribution_id'], TRUE);
+    }
     else {
       // Help we are making a payment but no money is owed. We won't allocate the overpayment to any line item.
       $ratio = 0;
@@ -495,12 +499,16 @@ class CRM_Financial_BAO_Payment {
       $lineItems[$lineItemID]['id'] = $lineItemID;
       $lineItems[$lineItemID]['paid'] = self::getAmountOfLineItemPaid($lineItemID);
       $lineItems[$lineItemID]['balance'] = $lineItem['subTotal'] - $lineItems[$lineItemID]['paid'];
-
       if (!empty($lineItemOverrides)) {
         $lineItems[$lineItemID]['allocation'] = $lineItemOverrides[$lineItemID] ?? NULL;
       }
       else {
-        $lineItems[$lineItemID]['allocation'] = $lineItems[$lineItemID]['balance'] * $ratio;
+        if (empty($lineItems[$lineItemID]['balance']) && !empty($ratio) && $params['total_amount'] < 0) {
+          $lineItems[$lineItemID]['allocation'] = $lineItem['subTotal'] * $ratio;
+        }
+        else {
+          $lineItems[$lineItemID]['allocation'] = $lineItems[$lineItemID]['balance'] * $ratio;
+        }
       }
     }
     return $lineItems;
diff --git a/civicrm/CRM/Financial/DAO/Currency.php b/civicrm/CRM/Financial/DAO/Currency.php
index 20dff31e7fe39f65831abeb211ddd55307ce3cce..f96650badc5908feafc6cc18b53711ebe3e8343e 100644
--- a/civicrm/CRM/Financial/DAO/Currency.php
+++ b/civicrm/CRM/Financial/DAO/Currency.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/Currency.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:9141d433586789f5ea9003fdbe9fdf9b)
+ * (GenCodeChecksum:5501c59b453dedfb8bba1f2fab44d9ea)
  */
 
 /**
  * Database access object for the Currency entity.
  */
 class CRM_Financial_DAO_Currency extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.7';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Financial/DAO/EntityFinancialAccount.php b/civicrm/CRM/Financial/DAO/EntityFinancialAccount.php
index a23822f2b408e41cd3ae7db28cc220d32c30592d..973a99ca8330f6d3c97f13ad686f74cc1b50f66a 100644
--- a/civicrm/CRM/Financial/DAO/EntityFinancialAccount.php
+++ b/civicrm/CRM/Financial/DAO/EntityFinancialAccount.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/EntityFinancialAccount.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:5ed0eeb7d3f5f2caf668448cd441b004)
+ * (GenCodeChecksum:c1d51696dd326b61f65fd064a355e7fb)
  */
 
 /**
  * Database access object for the EntityFinancialAccount entity.
  */
 class CRM_Financial_DAO_EntityFinancialAccount extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Financial/DAO/EntityFinancialTrxn.php b/civicrm/CRM/Financial/DAO/EntityFinancialTrxn.php
index b74d080894e941fe4feef6e1cf51db44c6414d87..1f959ad6117bbc7d8bbd062f4322bba7bf48fd1e 100644
--- a/civicrm/CRM/Financial/DAO/EntityFinancialTrxn.php
+++ b/civicrm/CRM/Financial/DAO/EntityFinancialTrxn.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/EntityFinancialTrxn.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a4b054ba0fc5a16cd650bda8941690b1)
+ * (GenCodeChecksum:12eb23afdf6c1208bdc01aa7db52770a)
  */
 
 /**
  * Database access object for the EntityFinancialTrxn entity.
  */
 class CRM_Financial_DAO_EntityFinancialTrxn extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Financial/DAO/FinancialAccount.php b/civicrm/CRM/Financial/DAO/FinancialAccount.php
index f6912ae03c1a60e6684e09dcb93f30d993562318..d8f161e9437bae30da0ce056bb6a33b0083f7049 100644
--- a/civicrm/CRM/Financial/DAO/FinancialAccount.php
+++ b/civicrm/CRM/Financial/DAO/FinancialAccount.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/FinancialAccount.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:e9c9d66596703d9dfbf411034ce909c4)
+ * (GenCodeChecksum:b9f200ff95d9186180eff484dcd12a57)
  */
 
 /**
  * Database access object for the FinancialAccount entity.
  */
 class CRM_Financial_DAO_FinancialAccount extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Financial/DAO/FinancialItem.php b/civicrm/CRM/Financial/DAO/FinancialItem.php
index 055c8a5de18fc7f908cb32d7c1683c5cfe73be07..a59dc38e6613ab5215b7e022fb3c045c1c9f9ec1 100644
--- a/civicrm/CRM/Financial/DAO/FinancialItem.php
+++ b/civicrm/CRM/Financial/DAO/FinancialItem.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/FinancialItem.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:4f2e977c0c7c668be342598772b0eb61)
+ * (GenCodeChecksum:8bb63ebee681c2eb4acbf8650b224dc2)
  */
 
 /**
  * Database access object for the FinancialItem entity.
  */
 class CRM_Financial_DAO_FinancialItem extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Financial/DAO/FinancialTrxn.php b/civicrm/CRM/Financial/DAO/FinancialTrxn.php
index 47f7ae219782509edf257872a09641c02c00b202..157984607fb373e9c2c0244b3da40703bb73bb0e 100644
--- a/civicrm/CRM/Financial/DAO/FinancialTrxn.php
+++ b/civicrm/CRM/Financial/DAO/FinancialTrxn.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/FinancialTrxn.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:7e2cf7d374cc6e0af61672c0318248b6)
+ * (GenCodeChecksum:5a4324ffe222bf724ab9d4cde26eb4c2)
  */
 
 /**
  * Database access object for the FinancialTrxn entity.
  */
 class CRM_Financial_DAO_FinancialTrxn extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Financial/DAO/FinancialType.php b/civicrm/CRM/Financial/DAO/FinancialType.php
index a047fd348e0f1e06224dbd0ba15206c53195f26c..cd99c56281eb7937e52c6d02eb95f591a7c8b343 100644
--- a/civicrm/CRM/Financial/DAO/FinancialType.php
+++ b/civicrm/CRM/Financial/DAO/FinancialType.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/FinancialType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:81cc1b7a95feede610081f2066eeb147)
+ * (GenCodeChecksum:024b000d94adcc65200c00d7cef5e624)
  */
 
 /**
  * Database access object for the FinancialType entity.
  */
 class CRM_Financial_DAO_FinancialType extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Financial/DAO/PaymentProcessor.php b/civicrm/CRM/Financial/DAO/PaymentProcessor.php
index 6122a230769bb9355b21a174b996574ef224fdae..7f1e8334a9678b7b65ceac85d11ad821684a2f09 100644
--- a/civicrm/CRM/Financial/DAO/PaymentProcessor.php
+++ b/civicrm/CRM/Financial/DAO/PaymentProcessor.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/PaymentProcessor.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:625ab4ba983447ea3e236b48491eeed6)
+ * (GenCodeChecksum:7e296728147d44cb68a9231c4995e461)
  */
 
 /**
  * Database access object for the PaymentProcessor entity.
  */
 class CRM_Financial_DAO_PaymentProcessor extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Financial/DAO/PaymentProcessorType.php b/civicrm/CRM/Financial/DAO/PaymentProcessorType.php
index c942177b367865aecc76888cb820f6f5685e1c4e..597b8d27f0ff19374d9f8449bfcf63a81561477c 100644
--- a/civicrm/CRM/Financial/DAO/PaymentProcessorType.php
+++ b/civicrm/CRM/Financial/DAO/PaymentProcessorType.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/PaymentProcessorType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b57486d319cf2c2022b7447a6c53c199)
+ * (GenCodeChecksum:624a9a001f451b6eb17930a9abcceb3e)
  */
 
 /**
  * Database access object for the PaymentProcessorType entity.
  */
 class CRM_Financial_DAO_PaymentProcessorType extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Financial/DAO/PaymentToken.php b/civicrm/CRM/Financial/DAO/PaymentToken.php
index 363de403c2403651b0d686675a3ca2e0a3b8c777..cb2498f0832ce707ce30376124dd732ba81670b0 100644
--- a/civicrm/CRM/Financial/DAO/PaymentToken.php
+++ b/civicrm/CRM/Financial/DAO/PaymentToken.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Financial/PaymentToken.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:c135e4bffc93086e83c082bbebc84372)
+ * (GenCodeChecksum:ce51f1e6eaf6b29f3adeb67828e85507)
  */
 
 /**
  * Database access object for the PaymentToken entity.
  */
 class CRM_Financial_DAO_PaymentToken extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.6';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Friend/DAO/Friend.php b/civicrm/CRM/Friend/DAO/Friend.php
index 854c5f5de78a03f2a7f5f1053463613ec5356209..1513ab9dccc5c25e267fb8dd5a3fa27a69a31a9c 100644
--- a/civicrm/CRM/Friend/DAO/Friend.php
+++ b/civicrm/CRM/Friend/DAO/Friend.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Friend/Friend.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ed3cb6458bdc4ee011d6b30baa9e24ff)
+ * (GenCodeChecksum:3f1c976d43e312175e85da0427f5210d)
  */
 
 /**
  * Database access object for the Friend entity.
  */
 class CRM_Friend_DAO_Friend extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.0';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Grant/BAO/Query.php b/civicrm/CRM/Grant/BAO/Query.php
index d91699ca81b2ea44b91b1e828955cc37235a840d..f34f8946121267aa64be6612e9806c3fdd04ac4b 100644
--- a/civicrm/CRM/Grant/BAO/Query.php
+++ b/civicrm/CRM/Grant/BAO/Query.php
@@ -328,10 +328,10 @@ class CRM_Grant_BAO_Query extends CRM_Core_BAO_Query {
     $form->addElement('checkbox', 'grant_decision_date_notset', ts('Date is not set'), NULL);
 
     $form->add('text', 'grant_amount_low', ts('Minimum Amount'), ['size' => 8, 'maxlength' => 8]);
-    $form->addRule('grant_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('9.99', ' ')]), 'money');
+    $form->addRule('grant_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('9.99')]), 'money');
 
     $form->add('text', 'grant_amount_high', ts('Maximum Amount'), ['size' => 8, 'maxlength' => 8]);
-    $form->addRule('grant_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money');
+    $form->addRule('grant_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money');
 
     self::addCustomFormFields($form, ['Grant']);
 
diff --git a/civicrm/CRM/Grant/DAO/Grant.php b/civicrm/CRM/Grant/DAO/Grant.php
index fa8dd9d09fc54e47319e22406669aecdc199412d..91b816359a758202611ac223750db3454c3777c7 100644
--- a/civicrm/CRM/Grant/DAO/Grant.php
+++ b/civicrm/CRM/Grant/DAO/Grant.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Grant/Grant.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b09a04b8edb982e838510dfb9d7dbc35)
+ * (GenCodeChecksum:a2e43b7f0fb8547daf5ed874bf6174c5)
  */
 
 /**
  * Database access object for the Grant entity.
  */
 class CRM_Grant_DAO_Grant extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Group/Form/Edit.php b/civicrm/CRM/Group/Form/Edit.php
index d9be20cdfb864c7cc7e2815f7e0904c0cc8be207..e384dc36cc7fe3dd3313e43f3d4e6f426845c5ff 100644
--- a/civicrm/CRM/Group/Form/Edit.php
+++ b/civicrm/CRM/Group/Form/Edit.php
@@ -371,7 +371,8 @@ WHERE  title = %1
       );
 
       $group = CRM_Contact_BAO_Group::create($params);
-
+      // Set the entity id so it is available to postProcess hook consumers
+      $this->setEntityId($group->id);
       //Remove any parent groups requested to be removed
       if (!empty($this->_groupValues['parents'])) {
         $parentGroupIds = explode(',', $this->_groupValues['parents']);
diff --git a/civicrm/CRM/Logging/Differ.php b/civicrm/CRM/Logging/Differ.php
index a124a4006956bc0689c0ed3bfac5f120a2f5d287..863322bb2aae8f04b3499783396023da5b6fe5a0 100644
--- a/civicrm/CRM/Logging/Differ.php
+++ b/civicrm/CRM/Logging/Differ.php
@@ -28,7 +28,8 @@ class CRM_Logging_Differ {
    * @param string $interval
    */
   public function __construct($log_conn_id, $log_date, $interval = '10 SECOND') {
-    $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN);
+    $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN);
+    $dsn = DB::parseDSN($dsn);
     $this->db = $dsn['database'];
     $this->log_conn_id = $log_conn_id;
     $this->log_date = $log_date;
diff --git a/civicrm/CRM/Logging/ReportDetail.php b/civicrm/CRM/Logging/ReportDetail.php
index d5822b183d515cae25111a9e4606447c52bacda1..fdbd92216a895882e7e64861f067e6119585fb40 100644
--- a/civicrm/CRM/Logging/ReportDetail.php
+++ b/civicrm/CRM/Logging/ReportDetail.php
@@ -257,7 +257,8 @@ class CRM_Logging_ReportDetail extends CRM_Report_Form {
    * Store the dsn for the logging database in $this->db.
    */
   protected function storeDB() {
-    $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN);
+    $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN);
+    $dsn = DB::parseDSN($dsn);
     $this->db = $dsn['database'];
   }
 
diff --git a/civicrm/CRM/Logging/Reverter.php b/civicrm/CRM/Logging/Reverter.php
index 72d729d03af6149c9c0fd21002d35f0241543d31..eb1c9dedc16c1f9964e89f519075838fc0821ec1 100644
--- a/civicrm/CRM/Logging/Reverter.php
+++ b/civicrm/CRM/Logging/Reverter.php
@@ -33,7 +33,8 @@ class CRM_Logging_Reverter {
    * @param $log_date
    */
   public function __construct($log_conn_id, $log_date) {
-    $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN);
+    $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN);
+    $dsn = DB::parseDSN($dsn);
     $this->db = $dsn['database'];
     $this->log_conn_id = $log_conn_id;
     $this->log_date = $log_date;
diff --git a/civicrm/CRM/Logging/Schema.php b/civicrm/CRM/Logging/Schema.php
index e9160bbdefe14defd5c7d1d185ca2680644d7d33..25b63413d2540ca1d6ccc56afc690dce002e95cd 100644
--- a/civicrm/CRM/Logging/Schema.php
+++ b/civicrm/CRM/Logging/Schema.php
@@ -158,11 +158,13 @@ AND    TABLE_NAME LIKE 'civicrm_%'
     $nonStandardTableNameString = $this->getNonStandardTableNameFilterString();
 
     if (defined('CIVICRM_LOGGING_DSN')) {
-      $dsn = DB::parseDSN(CIVICRM_LOGGING_DSN);
+      $dsn = CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN);
+      $dsn = DB::parseDSN($dsn);
       $this->useDBPrefix = (CIVICRM_LOGGING_DSN != CIVICRM_DSN);
     }
     else {
-      $dsn = DB::parseDSN(CIVICRM_DSN);
+      $dsn = CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN);
+      $dsn = DB::parseDSN($dsn);
       $this->useDBPrefix = FALSE;
     }
     $this->db = $dsn['database'];
diff --git a/civicrm/CRM/Mailing/DAO/BouncePattern.php b/civicrm/CRM/Mailing/DAO/BouncePattern.php
index 1e6c539e5dc127bbc041d0e096fbbf7eb8210a15..43822bf7c5e3db2db3a8d8ae5cdb2734634fbe2a 100644
--- a/civicrm/CRM/Mailing/DAO/BouncePattern.php
+++ b/civicrm/CRM/Mailing/DAO/BouncePattern.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/BouncePattern.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:e36e2c40e59bfe04998b2e4e216724b6)
+ * (GenCodeChecksum:90abbaf8e68b5749a084a74d77dcc3b7)
  */
 
 /**
  * Database access object for the BouncePattern entity.
  */
 class CRM_Mailing_DAO_BouncePattern extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/DAO/BounceType.php b/civicrm/CRM/Mailing/DAO/BounceType.php
index 9bb3db18070b5b55a033c3451a9a831120b52d11..a31f8324f4fe03a1a642eec0fb8ea9ee76540c23 100644
--- a/civicrm/CRM/Mailing/DAO/BounceType.php
+++ b/civicrm/CRM/Mailing/DAO/BounceType.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/BounceType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b2b6ab45aa6fed676d81a68651f0bf93)
+ * (GenCodeChecksum:4e77659bd433033396e84b6de32c99af)
  */
 
 /**
  * Database access object for the BounceType entity.
  */
 class CRM_Mailing_DAO_BounceType extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/DAO/Mailing.php b/civicrm/CRM/Mailing/DAO/Mailing.php
index e89653ee93180a84d4e0a33f91a3e66d3ed42c42..084489593dcbe2cdb9ea9deac588616ac95eb37c 100644
--- a/civicrm/CRM/Mailing/DAO/Mailing.php
+++ b/civicrm/CRM/Mailing/DAO/Mailing.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Mailing.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:bfcc85d4eb2bab05f214dd946e5fdef6)
+ * (GenCodeChecksum:6a9dc5aaff7aa7f5dcfe3f892255e357)
  */
 
 /**
  * Database access object for the Mailing entity.
  */
 class CRM_Mailing_DAO_Mailing extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/DAO/MailingAB.php b/civicrm/CRM/Mailing/DAO/MailingAB.php
index 4875b11c502192a21b625b791bdc03489015cb15..0e2215e1fc5f7a80a0f2c98d130825cecc72892c 100644
--- a/civicrm/CRM/Mailing/DAO/MailingAB.php
+++ b/civicrm/CRM/Mailing/DAO/MailingAB.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/MailingAB.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:fd7f00955d0a5eb4ec56089ccd71f68d)
+ * (GenCodeChecksum:af0f7d34ddde7f3971aaac5abccfcd8c)
  */
 
 /**
  * Database access object for the MailingAB entity.
  */
 class CRM_Mailing_DAO_MailingAB extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/DAO/MailingComponent.php b/civicrm/CRM/Mailing/DAO/MailingComponent.php
index cd35b7cce0d38c183c18ac7d54c6f07d772da54b..f1b81f15629d57afdabbb5028be4dce21039a2bc 100644
--- a/civicrm/CRM/Mailing/DAO/MailingComponent.php
+++ b/civicrm/CRM/Mailing/DAO/MailingComponent.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/MailingComponent.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:1d0efc3e5ba8dcd6d6bbc422877b86b4)
+ * (GenCodeChecksum:ca95f8566048836c03e1dc58eb51ac11)
  */
 
 /**
  * Database access object for the MailingComponent entity.
  */
 class CRM_Mailing_DAO_MailingComponent extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/DAO/MailingGroup.php b/civicrm/CRM/Mailing/DAO/MailingGroup.php
index 6c1822212c9603dce4e4cded3433370989cf21e8..220bfd13cb08a8554f8d50942feb52ad73f01c80 100644
--- a/civicrm/CRM/Mailing/DAO/MailingGroup.php
+++ b/civicrm/CRM/Mailing/DAO/MailingGroup.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/MailingGroup.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:1869c2f625f9a35820d1342217bd7feb)
+ * (GenCodeChecksum:a253e806fcb595ede70c812a10c0dbba)
  */
 
 /**
  * Database access object for the MailingGroup entity.
  */
 class CRM_Mailing_DAO_MailingGroup extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/DAO/MailingJob.php b/civicrm/CRM/Mailing/DAO/MailingJob.php
index f803973e1b93bdcc0c2b8896c3627ad3dd005e1a..bde7e35f391efa21fb03a70a15fd96c8aeac78f9 100644
--- a/civicrm/CRM/Mailing/DAO/MailingJob.php
+++ b/civicrm/CRM/Mailing/DAO/MailingJob.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/MailingJob.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b84e3c6e86ddabfe04066d742f5c0ca5)
+ * (GenCodeChecksum:0c6e76df20fe3579056c287aeed27cdb)
  */
 
 /**
  * Database access object for the MailingJob entity.
  */
 class CRM_Mailing_DAO_MailingJob extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/DAO/Recipients.php b/civicrm/CRM/Mailing/DAO/Recipients.php
index d3511d4ab73dfbc4b40247dfceb9c142901725ec..1852c7ac022a6aeb4fa60c2207c1dc4c8179e58d 100644
--- a/civicrm/CRM/Mailing/DAO/Recipients.php
+++ b/civicrm/CRM/Mailing/DAO/Recipients.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Recipients.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:589a6adc830c8f9197b5123e08b63ba3)
+ * (GenCodeChecksum:ebd2ec177861f8f82a4bc6bc8b33fd9b)
  */
 
 /**
  * Database access object for the Recipients entity.
  */
 class CRM_Mailing_DAO_Recipients extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/DAO/Spool.php b/civicrm/CRM/Mailing/DAO/Spool.php
index d33871b7b72c521bd70a26ebe589dcfd5782bb9f..6027afd1676cf9875d35a251c211c8fd107c66c8 100644
--- a/civicrm/CRM/Mailing/DAO/Spool.php
+++ b/civicrm/CRM/Mailing/DAO/Spool.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Spool.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:8e8346020e4f3174bc4c4c2e87d8e136)
+ * (GenCodeChecksum:7bd4a9b64175915a43f602f4f9cfb721)
  */
 
 /**
  * Database access object for the Spool entity.
  */
 class CRM_Mailing_DAO_Spool extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/DAO/TrackableURL.php b/civicrm/CRM/Mailing/DAO/TrackableURL.php
index 771c0e469e1750b0079ec6251ef04a15be321f21..3d39634397268e1e0df4006057af18566bc67a83 100644
--- a/civicrm/CRM/Mailing/DAO/TrackableURL.php
+++ b/civicrm/CRM/Mailing/DAO/TrackableURL.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/TrackableURL.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:6436cc3cfd3b7a43b468c5372ec081be)
+ * (GenCodeChecksum:74f858b4e9e666e05416be884002408b)
  */
 
 /**
  * Database access object for the TrackableURL entity.
  */
 class CRM_Mailing_DAO_TrackableURL extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/Bounce.php b/civicrm/CRM/Mailing/Event/DAO/Bounce.php
index b21631b738695dd2136bcb496f57a8dbc88353e5..1cd4024fb4d6cad1d6d96f0cac121e030ef19b62 100644
--- a/civicrm/CRM/Mailing/Event/DAO/Bounce.php
+++ b/civicrm/CRM/Mailing/Event/DAO/Bounce.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/Bounce.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ca3431a45bad00b42a6bf347338041ed)
+ * (GenCodeChecksum:8e0590dde97f57494203397255fd4604)
  */
 
 /**
  * Database access object for the Bounce entity.
  */
 class CRM_Mailing_Event_DAO_Bounce extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/Confirm.php b/civicrm/CRM/Mailing/Event/DAO/Confirm.php
index bdde918922219e084572dce45dfad62c0cd4fa76..343430c1579dfc5b9c48305ad68394f05bf77676 100644
--- a/civicrm/CRM/Mailing/Event/DAO/Confirm.php
+++ b/civicrm/CRM/Mailing/Event/DAO/Confirm.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/Confirm.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:01729079bec5be7346678447b6cf2844)
+ * (GenCodeChecksum:827b011dc50d032e8b74d6d164314d83)
  */
 
 /**
  * Database access object for the Confirm entity.
  */
 class CRM_Mailing_Event_DAO_Confirm extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/Delivered.php b/civicrm/CRM/Mailing/Event/DAO/Delivered.php
index 1adada28bcb1a6f3a964ac383a67181c61d1b848..147d70fcf82fc642fe9d6d6947eaeacdce6249fd 100644
--- a/civicrm/CRM/Mailing/Event/DAO/Delivered.php
+++ b/civicrm/CRM/Mailing/Event/DAO/Delivered.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/Delivered.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ced38ed0f599efdc44cca8dad6049d8c)
+ * (GenCodeChecksum:c983e11b4de5a1c4e6d9765eb7d12755)
  */
 
 /**
  * Database access object for the Delivered entity.
  */
 class CRM_Mailing_Event_DAO_Delivered extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/Forward.php b/civicrm/CRM/Mailing/Event/DAO/Forward.php
index d5bdfbf7ec1b7f67113387a89879cb672c8d6eab..ac1c8d89670b53ee6841f4354507a708a0937fac 100644
--- a/civicrm/CRM/Mailing/Event/DAO/Forward.php
+++ b/civicrm/CRM/Mailing/Event/DAO/Forward.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/Forward.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ea0e4a784e682347d7320337cb5bc1ac)
+ * (GenCodeChecksum:359e0b700860c29a1e809fd4acbf7598)
  */
 
 /**
  * Database access object for the Forward entity.
  */
 class CRM_Mailing_Event_DAO_Forward extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/Opened.php b/civicrm/CRM/Mailing/Event/DAO/Opened.php
index 15b5e0394ea3fd32933ba439d25e9f22fa8d3f8c..cbd24372dfa0ba3c77d52f2f0364fedf853544e9 100644
--- a/civicrm/CRM/Mailing/Event/DAO/Opened.php
+++ b/civicrm/CRM/Mailing/Event/DAO/Opened.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/Opened.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:8c6c5978fae73802fbd9984ef51a2576)
+ * (GenCodeChecksum:dddc76ba8461f8b0c1f3c1cdccddd111)
  */
 
 /**
  * Database access object for the Opened entity.
  */
 class CRM_Mailing_Event_DAO_Opened extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/Queue.php b/civicrm/CRM/Mailing/Event/DAO/Queue.php
index 40f075d0b363775123c09961c2b921cfbc472117..073ef521c99712886df7631bfb944d88ac07d5a4 100644
--- a/civicrm/CRM/Mailing/Event/DAO/Queue.php
+++ b/civicrm/CRM/Mailing/Event/DAO/Queue.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/Queue.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:316f29677ad04a5347bfd30cd60f474e)
+ * (GenCodeChecksum:091cb300f1b0a67dfaf40f988806e6cf)
  */
 
 /**
  * Database access object for the Queue entity.
  */
 class CRM_Mailing_Event_DAO_Queue extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/Reply.php b/civicrm/CRM/Mailing/Event/DAO/Reply.php
index 687cd8b30c455e6bc514511505ba1bad494ca081..8fc31b3eb359e704d28f7b9a81d3ee077d5733e8 100644
--- a/civicrm/CRM/Mailing/Event/DAO/Reply.php
+++ b/civicrm/CRM/Mailing/Event/DAO/Reply.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/Reply.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a8246c6739f95a8e25d17ddebe882db7)
+ * (GenCodeChecksum:b1d572f3d42f6480dc98a2e6f9710fa3)
  */
 
 /**
  * Database access object for the Reply entity.
  */
 class CRM_Mailing_Event_DAO_Reply extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/Subscribe.php b/civicrm/CRM/Mailing/Event/DAO/Subscribe.php
index 97e420d72bc92dbf5fca12025a6dcc51706da3e3..ce577b24d76a5e7433fcd0c380168c323060c6c3 100644
--- a/civicrm/CRM/Mailing/Event/DAO/Subscribe.php
+++ b/civicrm/CRM/Mailing/Event/DAO/Subscribe.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/Subscribe.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:acb361992fd2db48c82ee7b13f8b7cf4)
+ * (GenCodeChecksum:9e1dec99f17dcccde7feeca30b880a85)
  */
 
 /**
  * Database access object for the Subscribe entity.
  */
 class CRM_Mailing_Event_DAO_Subscribe extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/TrackableURLOpen.php b/civicrm/CRM/Mailing/Event/DAO/TrackableURLOpen.php
index 07af2524c7d9e2f6f23dc4ed969883db9f78b0bc..88c84b0b920119c676e43600ec6530b58b9a6cbe 100644
--- a/civicrm/CRM/Mailing/Event/DAO/TrackableURLOpen.php
+++ b/civicrm/CRM/Mailing/Event/DAO/TrackableURLOpen.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/TrackableURLOpen.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:7d55cf77c7be3a81666b51aaa55c6358)
+ * (GenCodeChecksum:b543a83312f2069a45872939517aa480)
  */
 
 /**
  * Database access object for the TrackableURLOpen entity.
  */
 class CRM_Mailing_Event_DAO_TrackableURLOpen extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Event/DAO/Unsubscribe.php b/civicrm/CRM/Mailing/Event/DAO/Unsubscribe.php
index 9448593d04146e352e8769ff1ef60a58fa2e76e9..e5a230b66dc76be60070e87969b3db4398cd89c4 100644
--- a/civicrm/CRM/Mailing/Event/DAO/Unsubscribe.php
+++ b/civicrm/CRM/Mailing/Event/DAO/Unsubscribe.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Mailing/Event/Unsubscribe.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:d16e98421cdacbf5ad1b5542810fca6c)
+ * (GenCodeChecksum:2d080a63032c9dce0331a6ed4f6c3cd2)
  */
 
 /**
  * Database access object for the Unsubscribe entity.
  */
 class CRM_Mailing_Event_DAO_Unsubscribe extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Mailing/Page/Browse.php b/civicrm/CRM/Mailing/Page/Browse.php
index 9982bd1a79b52560a07b1398d4a73132edd9e5e0..a02b6adcbb7060a23e1577f1b69c702711137cc3 100644
--- a/civicrm/CRM/Mailing/Page/Browse.php
+++ b/civicrm/CRM/Mailing/Page/Browse.php
@@ -243,11 +243,6 @@ class CRM_Mailing_Page_Browse extends CRM_Core_Page {
     $controller->setEmbedded(TRUE);
     $controller->run();
 
-    // hack to display results as per search
-    $rows = $controller->getRows($controller);
-
-    $this->assign('rows', $rows);
-
     $urlParams = 'reset=1';
     $urlString = 'civicrm/mailing/browse';
     if ($this->get('sms')) {
diff --git a/civicrm/CRM/Member/BAO/Membership.php b/civicrm/CRM/Member/BAO/Membership.php
index 6640b4b113f87e666feef366c63fb971ee2db92f..1030eaae1c9beb702129cc844f5531357d9668c0 100644
--- a/civicrm/CRM/Member/BAO/Membership.php
+++ b/civicrm/CRM/Member/BAO/Membership.php
@@ -121,7 +121,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
 
     $allStatus = CRM_Member_BAO_Membership::buildOptions('status_id', 'get');
     $activityParams = [
-      'status_id' => CRM_Utils_Array::value('membership_activity_status', $params, 'Completed'),
+      'status_id' => $params['membership_activity_status'] ?? 'Completed',
     ];
     if (in_array($allStatus[$membership->status_id], ['Pending', 'Grace'])) {
       $activityParams['status_id'] = 'Scheduled';
@@ -166,15 +166,11 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
       }
 
       foreach (['Membership Signup', 'Membership Renewal'] as $activityType) {
-        $activityParams['id'] = CRM_Utils_Array::value('id',
-          civicrm_api3('Activity', 'Get',
-            [
-              'source_record_id' => $membership->id,
-              'activity_type_id' => $activityType,
-              'status_id' => 'Scheduled',
-            ]
-          )
-        );
+        $activityParams['id'] = civicrm_api3('Activity', 'Get', [
+          'source_record_id' => $membership->id,
+          'activity_type_id' => $activityType,
+          'status_id' => 'Scheduled',
+        ])['id'] ?? NULL;
         // 1. Update Schedule Membership Signup/Renwal activity to completed on successful payment of pending membership
         // 2. OR Create renewal activity scheduled if its membership renewal will be paid later
         if (!empty($params['membership_activity_status']) && (!empty($activityParams['id']) || $activityType == 'Membership Renewal')) {
@@ -261,7 +257,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
       $params['join_date'] = trim($params['join_date']) ? date('Ymd', strtotime(trim($params['join_date']))) : 'null';
 
       //fix for CRM-3570, during import exclude the statuses those having is_admin = 1
-      $excludeIsAdmin = CRM_Utils_Array::value('exclude_is_admin', $params, FALSE);
+      $excludeIsAdmin = $params['exclude_is_admin'] ?? FALSE;
 
       //CRM-3724 always skip is_admin if is_override != true.
       if (!$excludeIsAdmin && empty($params['is_override'])) {
@@ -269,7 +265,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
       }
 
       $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($params['start_date'], $params['end_date'], $params['join_date'],
-        'today', $excludeIsAdmin, CRM_Utils_Array::value('membership_type_id', $params), $params
+        'today', $excludeIsAdmin, $params['membership_type_id'] ?? NULL, $params
       );
       if (empty($calcStatus)) {
         throw new CRM_Core_Exception(ts("The membership cannot be saved because the status cannot be calculated for start_date: {$params['start_date']} end_date {$params['end_date']} join_date {$params['join_date']} as at " . date('Y-m-d H:i:s')));
@@ -295,12 +291,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
 
     $transaction = new CRM_Core_Transaction();
 
-    // @todo remove $ids from here $ids['userId'] is still used
-    $params['id'] = CRM_Utils_Array::value('id', $params, CRM_Utils_Array::value('membership', $ids));
-    if (empty($params['modified_id']) && !empty($ids['userID'])) {
-      CRM_Core_Error::deprecatedFunctionWarning('$ids["userID"] no longer supported - use $params["modified_id"]');
-      $params['modified_id'] = $ids['userID'];
-    }
+    $params['id'] = $params['id'] ?? $ids['membership'] ?? NULL;
     $membership = self::add($params);
 
     if (is_a($membership, 'CRM_Core_Error')) {
@@ -364,7 +355,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
           if (!empty($params['contribution'])) {
             $params['line_item'][$priceSetId][$lineIndex]['contribution_id'] = $params['contribution']->id;
           }
-          if ($lineMembershipType && $lineMembershipType == CRM_Utils_Array::value('membership_type_id', $params)) {
+          if ($lineMembershipType && $lineMembershipType == ($params['membership_type_id'] ?? NULL)) {
             $params['line_item'][$priceSetId][$lineIndex]['entity_id'] = $membership->id;
             $params['line_item'][$priceSetId][$lineIndex]['entity_table'] = 'civicrm_membership';
           }
@@ -377,7 +368,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
       CRM_Price_BAO_LineItem::processPriceSet(
         $membership->id,
         $params['line_item'],
-        CRM_Utils_Array::value('contribution', $params)
+        $params['contribution'] ?? NULL
       );
     }
 
@@ -837,7 +828,7 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
             'limit' => 0,
           ],
         ]);
-        $memberTypesSameParentOrgList = implode(',', array_keys(CRM_Utils_Array::value('values', $memberTypesSameParentOrg, [])));
+        $memberTypesSameParentOrgList = implode(',', array_keys($memberTypesSameParentOrg['values'] ?? []));
         $dao->whereAdd('membership_type_id IN (' . $memberTypesSameParentOrgList . ')');
       }
     }
@@ -1148,9 +1139,9 @@ AND civicrm_membership.is_test = %2";
     }
 
     $status = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate(
-      CRM_Utils_Array::value('start_date', $currentMembership),
-      CRM_Utils_Array::value('end_date', $currentMembership),
-      CRM_Utils_Array::value('join_date', $currentMembership),
+      $currentMembership['start_date'] ?? NULL,
+      $currentMembership['end_date'] ?? NULL,
+      $currentMembership['join_date'] ?? NULL,
       $today,
       TRUE,
       $currentMembership['membership_type_id'],
@@ -1199,7 +1190,7 @@ AND civicrm_membership.is_test = %2";
           $format
         ),
         'membership_type_id' => $currentMembership['membership_type_id'],
-        'max_related' => CRM_Utils_Array::value('max_related', $currentMembership, 0),
+        'max_related' => $currentMembership['max_related'] ?? 0,
       ];
 
       $session = CRM_Core_Session::singleton();
@@ -1333,7 +1324,7 @@ WHERE  civicrm_membership.contact_id = civicrm_contact.id
    * @throws \CRM_Core_Exception
    * @throws \CiviCRM_API3_Exception
    */
-  public static function createRelatedMemberships(&$params, &$dao, $reset = FALSE) {
+  public static function createRelatedMemberships($params, $dao, $reset = FALSE) {
     // CRM-4213 check for loops, using static variable to record contacts already processed.
     if (!isset(\Civi::$statics[__CLASS__]['related_contacts'])) {
       \Civi::$statics[__CLASS__]['related_contacts'] = [];
@@ -1376,7 +1367,7 @@ WHERE  civicrm_membership.contact_id = civicrm_contact.id
     $relatedContacts = [];
     $allRelatedContacts = CRM_Member_BAO_Membership::checkMembershipRelationship($membership->membership_type_id,
       $membership->contact_id,
-      CRM_Utils_Array::value('action', $params)
+      $params['action'] ?? NULL
     );
 
     // CRM-4213, CRM-19735 check for loops, using static variable to record contacts already processed.
@@ -1448,7 +1439,7 @@ WHERE  civicrm_membership.contact_id = civicrm_contact.id
         ) {
           $params['status_id'] = $deceasedStatusId;
         }
-        elseif ((CRM_Utils_Array::value('action', $params) & CRM_Core_Action::UPDATE) &&
+        elseif ((($params['action'] ?? NULL) & CRM_Core_Action::UPDATE) &&
           ($relationshipStatus == CRM_Contact_BAO_Relationship::PAST)
         ) {
           $params['status_id'] = $expiredStatusId;
@@ -1882,7 +1873,7 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
         // Insert renewed dates for CURRENT membership
         $memParams = [];
         $memParams['join_date'] = CRM_Utils_Date::isoToMysql($membership->join_date);
-        $memParams['start_date'] = CRM_Utils_Array::value('start_date', $formDates, CRM_Utils_Date::isoToMysql($membership->start_date));
+        $memParams['start_date'] = $formDates['start_date'] ?? CRM_Utils_Date::isoToMysql($membership->start_date);
         $memParams['end_date'] = $formDates['end_date'] ?? NULL;
         if (empty($memParams['end_date'])) {
           $memParams['end_date'] = $dates['end_date'] ?? NULL;
@@ -2403,7 +2394,7 @@ WHERE {$whereClause}";
     $contributionParams = [];
     $config = CRM_Core_Config::singleton();
     $contributionParams['currency'] = $config->defaultCurrency;
-    $contributionParams['receipt_date'] = (CRM_Utils_Array::value('receipt_date', $params)) ? $params['receipt_date'] : 'null';
+    $contributionParams['receipt_date'] = !empty($params['receipt_date']) ? $params['receipt_date'] : 'null';
     $contributionParams['source'] = $params['contribution_source'] ?? NULL;
     $contributionParams['non_deductible_amount'] = 'null';
     $contributionParams['skipCleanMoney'] = TRUE;
diff --git a/civicrm/CRM/Member/DAO/Membership.php b/civicrm/CRM/Member/DAO/Membership.php
index 289f9ba2315fcb3f36e436e348e99e9a95277e0b..384c69ae6b5857b66703fee8ea4ed20262dec189 100644
--- a/civicrm/CRM/Member/DAO/Membership.php
+++ b/civicrm/CRM/Member/DAO/Membership.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Member/Membership.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:9a307c1a63b4df70ae38f36ce4171cb6)
+ * (GenCodeChecksum:835c63ea0a55b78d6d115a7a6db5dde2)
  */
 
 /**
  * Database access object for the Membership entity.
  */
 class CRM_Member_DAO_Membership extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Member/DAO/MembershipBlock.php b/civicrm/CRM/Member/DAO/MembershipBlock.php
index 36b91c2eae02ac4eab1ee3ae16e0651af583f9ea..91e65e6eeb2becf3e7d2174e8c64ed63aa0fa737 100644
--- a/civicrm/CRM/Member/DAO/MembershipBlock.php
+++ b/civicrm/CRM/Member/DAO/MembershipBlock.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Member/MembershipBlock.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:317a1eb3c0a67ffe4662f939a0b1fb69)
+ * (GenCodeChecksum:8eb2f3a6c818d449da875421b54de619)
  */
 
 /**
  * Database access object for the MembershipBlock entity.
  */
 class CRM_Member_DAO_MembershipBlock extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Member/DAO/MembershipLog.php b/civicrm/CRM/Member/DAO/MembershipLog.php
index 2f44712bc7d0c94fd60fe12e81bda0a913e061eb..bd0b4f7924e54ec306d9b02fa7035db8643b0bfd 100644
--- a/civicrm/CRM/Member/DAO/MembershipLog.php
+++ b/civicrm/CRM/Member/DAO/MembershipLog.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Member/MembershipLog.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:ff966b7edcdd126ddef1ee3b3f3bff2f)
+ * (GenCodeChecksum:4d5744b433ca7bb5385b11945cc0fe10)
  */
 
 /**
  * Database access object for the MembershipLog entity.
  */
 class CRM_Member_DAO_MembershipLog extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Member/DAO/MembershipPayment.php b/civicrm/CRM/Member/DAO/MembershipPayment.php
index 91c700e21a9571c60c33c3cade276efeaa0b0cf6..ac386eee5571056a71d1515f946399cf504202a0 100644
--- a/civicrm/CRM/Member/DAO/MembershipPayment.php
+++ b/civicrm/CRM/Member/DAO/MembershipPayment.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Member/MembershipPayment.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b2fa8ca60001d75a9049ca179c4e68ce)
+ * (GenCodeChecksum:39168603c262c909ebeee2ce821f0f0d)
  */
 
 /**
  * Database access object for the MembershipPayment entity.
  */
 class CRM_Member_DAO_MembershipPayment extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Member/DAO/MembershipStatus.php b/civicrm/CRM/Member/DAO/MembershipStatus.php
index c1b2afc1fe68c9f814c00946cd4b02db6b377124..5b780e4a5b9d21399a5f961b7a59ecd6ae7e7cfc 100644
--- a/civicrm/CRM/Member/DAO/MembershipStatus.php
+++ b/civicrm/CRM/Member/DAO/MembershipStatus.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Member/MembershipStatus.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:e6fe414d2b0dc3ff72ee5adcad75fab8)
+ * (GenCodeChecksum:f0c470d5aca6e3696a0ad8345531f8b8)
  */
 
 /**
  * Database access object for the MembershipStatus entity.
  */
 class CRM_Member_DAO_MembershipStatus extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Member/DAO/MembershipType.php b/civicrm/CRM/Member/DAO/MembershipType.php
index 63ef86aaaf653d3685eef164dea49761aafd3a21..4b1d6ce509eb7a4ff4eee81e0c9afe5c163b364d 100644
--- a/civicrm/CRM/Member/DAO/MembershipType.php
+++ b/civicrm/CRM/Member/DAO/MembershipType.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Member/MembershipType.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:20b0eec540f3bb69e386f86b29419391)
+ * (GenCodeChecksum:713057d2c1a6dcb6cbd6449b8934d28c)
  */
 
 /**
  * Database access object for the MembershipType entity.
  */
 class CRM_Member_DAO_MembershipType extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.5';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Member/Form.php b/civicrm/CRM/Member/Form.php
index 73f7cb46a915db91f77e033d904794c03f3b7b38..3ae6973ef52b097a5b3cc654d910b77f6a3a75f0 100644
--- a/civicrm/CRM/Member/Form.php
+++ b/civicrm/CRM/Member/Form.php
@@ -358,19 +358,19 @@ class CRM_Member_Form extends CRM_Contribute_Form_AbstractEditPayment {
    *
    * @param array $contributionRecurParams
    *
-   * @param int $membershipID
+   * @param int $membershipTypeID
    *
    * @return array
    * @throws \CiviCRM_API3_Exception
    */
-  protected function processRecurringContribution($contributionRecurParams, $membershipID) {
+  protected function processRecurringContribution($contributionRecurParams, $membershipTypeID) {
 
     $mapping = [
       'frequency_interval' => 'duration_interval',
       'frequency_unit' => 'duration_unit',
     ];
     $membershipType = civicrm_api3('MembershipType', 'getsingle', [
-      'id' => $membershipID,
+      'id' => $membershipTypeID,
       'return' => $mapping,
     ]);
 
diff --git a/civicrm/CRM/Member/Form/Membership.php b/civicrm/CRM/Member/Form/Membership.php
index 3c98b8570b9f71cd919120ca3ef7c79fc3e5421c..14c2f28caaf83ff4efa801705f0594e1a4a6f043 100644
--- a/civicrm/CRM/Member/Form/Membership.php
+++ b/civicrm/CRM/Member/Form/Membership.php
@@ -570,7 +570,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
         CRM_Member_StatusOverrideTypes::getSelectOptions()
       );
 
-      $this->add('datepicker', 'status_override_end_date', ts('Status Override End Date'), '', FALSE, ['minDate' => time(), 'time' => FALSE]);
+      $this->add('datepicker', 'status_override_end_date', ts('Status Override End Date'), '', FALSE, ['minDate' => date('Y-m-d'), 'time' => FALSE]);
 
       $this->addElement('checkbox', 'record_contribution', ts('Record Membership Payment?'));
 
diff --git a/civicrm/CRM/Member/Form/MembershipRenewal.php b/civicrm/CRM/Member/Form/MembershipRenewal.php
index 17b3dfc6f66a6867fd13ceefa536fb2e67fa8a2c..908e51adc9a4a40f8d4570dc55c8865bd02f9d29 100644
--- a/civicrm/CRM/Member/Form/MembershipRenewal.php
+++ b/civicrm/CRM/Member/Form/MembershipRenewal.php
@@ -553,7 +553,7 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
           'is_email_receipt' => !empty($this->_params['send_receipt']),
           'payment_instrument_id' => $this->_params['payment_instrument_id'],
           'invoice_id' => $this->_params['invoice_id'],
-        ], $membershipID = $paymentParams['membership_type_id'][1]);
+        ], $paymentParams['membership_type_id'][1]);
 
         $contributionRecurID = $contributionRecurParams['contributionRecurID'];
         $paymentParams = array_merge($paymentParams, $contributionRecurParams);
@@ -748,14 +748,8 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
    */
   public function processMembership($contactID, $membershipTypeID, $is_test, $changeToday, $customFieldsFormatted, $numRenewTerms, $membershipID, $pending, $contributionRecurID, $isPayLater) {
     $allStatus = CRM_Member_PseudoConstant::membershipStatus();
-    $membershipTypeDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($membershipTypeID);
     $ids = [];
-
-    // CRM-7297 - allow membership type to be be changed during renewal so long as the parent org of new membershipType
-    // is the same as the parent org of an existing membership of the contact
-    $currentMembership = CRM_Member_BAO_Membership::getContactMembership($contactID, $membershipTypeID,
-      $is_test, $membershipID, TRUE
-    );
+    $currentMembership = civicrm_api3('Membership', 'getsingle', ['id' => $membershipID]);
 
     // Do NOT do anything.
     //1. membership with status : PENDING/CANCELLED (CRM-2395)
@@ -765,7 +759,6 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
       // CRM-15475
       array_search('Cancelled', CRM_Member_PseudoConstant::membershipStatus(NULL, " name = 'Cancelled' ", 'name', FALSE, TRUE)),
     ])) {
-
       $memParams = [
         'id' => $currentMembership['id'],
         'status_id' => $currentMembership['status_id'],
@@ -773,7 +766,6 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
         'end_date' => $currentMembership['end_date'],
         'join_date' => $currentMembership['join_date'],
         'membership_type_id' => $membershipTypeID,
-        'max_related' => !empty($membershipTypeDetails['max_related']) ? $membershipTypeDetails['max_related'] : NULL,
         'membership_activity_status' => ($pending || $isPayLater) ? 'Scheduled' : 'Completed',
       ];
       if ($contributionRecurID) {
@@ -785,7 +777,7 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
     // Check and fix the membership if it is STALE
     CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($currentMembership, $changeToday);
 
-    $isMembershipCurrent = $currentMembership['is_current_member'];
+    $isMembershipCurrent = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus', $currentMembership['status_id'], 'is_current_member');
 
     // CRM-7297 Membership Upsell - calculate dates based on new membership type
     $dates = CRM_Member_BAO_MembershipType::getRenewalDatesForMembershipType($currentMembership['id'],
diff --git a/civicrm/CRM/Member/Import/Form/MapField.php b/civicrm/CRM/Member/Import/Form/MapField.php
index aa1ec10b215767a6462b8c522db34d717a4cf99b..4d341f98379df3a51e31c244d453ef366393140b 100644
--- a/civicrm/CRM/Member/Import/Form/MapField.php
+++ b/civicrm/CRM/Member/Import/Form/MapField.php
@@ -398,7 +398,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
     }
 
     $fileName = $this->controller->exportValue('DataSource', 'uploadFile');
-    $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
+    $separator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
 
     $mapperKeys = [];
@@ -487,7 +487,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
     }
 
     $parser = new CRM_Member_Import_Parser_Membership($mapperKeysMain, $mapperLocType, $mapperPhoneType);
-    $parser->run($fileName, $seperator, $mapper, $skipColumnHeader,
+    $parser->run($fileName, $separator, $mapper, $skipColumnHeader,
       CRM_Import_Parser::MODE_PREVIEW, $this->get('contactType')
     );
     // add all the necessary variables to the form
diff --git a/civicrm/CRM/Member/Import/Form/Preview.php b/civicrm/CRM/Member/Import/Form/Preview.php
index dbf1b1ec4e321f66ba5e39bfd4e6a568241c59e3..03484335300d078951cb405311acb75a14f6ee80 100644
--- a/civicrm/CRM/Member/Import/Form/Preview.php
+++ b/civicrm/CRM/Member/Import/Form/Preview.php
@@ -96,7 +96,7 @@ class CRM_Member_Import_Form_Preview extends CRM_Import_Form_Preview {
    */
   public function postProcess() {
     $fileName = $this->controller->exportValue('DataSource', 'uploadFile');
-    $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
+    $separator = $this->controller->exportValue('DataSource', 'fieldSeparator');
     $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
     $invalidRowCount = $this->get('invalidRowCount');
     $conflictRowCount = $this->get('conflictRowCount');
@@ -139,7 +139,7 @@ class CRM_Member_Import_Form_Preview extends CRM_Import_Form_Preview {
       }
       $mapperFields[] = implode(' - ', $header);
     }
-    $parser->run($fileName, $seperator,
+    $parser->run($fileName, $separator,
       $mapperFields,
       $skipColumnHeader,
       CRM_Import_Parser::MODE_IMPORT,
diff --git a/civicrm/CRM/Member/Import/Parser.php b/civicrm/CRM/Member/Import/Parser.php
index 045d336a847ebc3d59406fd19a9a7d8ebf010343..53d08b75e0e4dbb4358481373819d5466ee3feec 100644
--- a/civicrm/CRM/Member/Import/Parser.php
+++ b/civicrm/CRM/Member/Import/Parser.php
@@ -27,10 +27,10 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser {
   protected $_fileSize;
 
   /**
-   * Seperator being used
+   * Separator being used
    * @var string
    */
-  protected $_seperator;
+  protected $_separator;
 
   /**
    * Total number of lines in file
@@ -47,7 +47,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser {
 
   /**
    * @param string $fileName
-   * @param string $seperator
+   * @param string $separator
    * @param $mapper
    * @param bool $skipColumnHeader
    * @param int $mode
@@ -61,7 +61,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser {
    */
   public function run(
     $fileName,
-    $seperator = ',',
+    $separator = ',',
     &$mapper,
     $skipColumnHeader = FALSE,
     $mode = self::MODE_PREVIEW,
@@ -92,7 +92,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser {
 
     $this->_haveColumnHeader = $skipColumnHeader;
 
-    $this->_seperator = $seperator;
+    $this->_separator = $separator;
 
     $fd = fopen($fileName, "r");
     if (!$fd) {
@@ -123,7 +123,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser {
     while (!feof($fd)) {
       $this->_lineCount++;
 
-      $values = fgetcsv($fd, 8192, $seperator);
+      $values = fgetcsv($fd, 8192, $separator);
       if (!$values) {
         continue;
       }
@@ -342,7 +342,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser {
   public function set($store, $mode = self::MODE_SUMMARY) {
     $store->set('fileSize', $this->_fileSize);
     $store->set('lineCount', $this->_lineCount);
-    $store->set('seperator', $this->_seperator);
+    $store->set('separator', $this->_separator);
     $store->set('fields', $this->getSelectValues());
     $store->set('fieldTypes', $this->getSelectTypes());
 
diff --git a/civicrm/CRM/PCP/DAO/PCP.php b/civicrm/CRM/PCP/DAO/PCP.php
index f5d8ed236314e36c6d8f0690f44366f273fc465c..eb565696754516f8e27286cbf045c7a7e826fa8e 100644
--- a/civicrm/CRM/PCP/DAO/PCP.php
+++ b/civicrm/CRM/PCP/DAO/PCP.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/PCP/PCP.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:30c5a26e001449a2ace9e530714e833c)
+ * (GenCodeChecksum:286225e46c4e2f3c12b17cd5f83b210d)
  */
 
 /**
  * Database access object for the PCP entity.
  */
 class CRM_PCP_DAO_PCP extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/PCP/DAO/PCPBlock.php b/civicrm/CRM/PCP/DAO/PCPBlock.php
index 897d9145f3b9f11bb30df5c5017ae7443248b94e..74f5204bebd1689474013f8bbceada3d53e39dd0 100644
--- a/civicrm/CRM/PCP/DAO/PCPBlock.php
+++ b/civicrm/CRM/PCP/DAO/PCPBlock.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/PCP/PCPBlock.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:039e37edb86cfd25e6c882ce4950e3c9)
+ * (GenCodeChecksum:ea1b4158570c5a79356b1dc0ad80db6a)
  */
 
 /**
  * Database access object for the PCPBlock entity.
  */
 class CRM_PCP_DAO_PCPBlock extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Pledge/BAO/Query.php b/civicrm/CRM/Pledge/BAO/Query.php
index 4b780c59831e3fe22eec8c6d395cef7fdfbef1e7..45f9b1c37cc807b2eccc448036f47278527a56c1 100644
--- a/civicrm/CRM/Pledge/BAO/Query.php
+++ b/civicrm/CRM/Pledge/BAO/Query.php
@@ -539,10 +539,10 @@ class CRM_Pledge_BAO_Query extends CRM_Core_BAO_Query {
 
     $form->addYesNo('pledge_test', ts('Pledge is a Test?'), TRUE);
     $form->add('text', 'pledge_amount_low', ts('From'), ['size' => 8, 'maxlength' => 8]);
-    $form->addRule('pledge_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('9.99', ' ')]), 'money');
+    $form->addRule('pledge_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('9.99')]), 'money');
 
     $form->add('text', 'pledge_amount_high', ts('To'), ['size' => 8, 'maxlength' => 8]);
-    $form->addRule('pledge_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money');
+    $form->addRule('pledge_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money');
 
     $form->addYesNo('pledge_acknowledge_date_is_not_null', ts('Acknowledgement sent?'), TRUE);
 
diff --git a/civicrm/CRM/Pledge/DAO/Pledge.php b/civicrm/CRM/Pledge/DAO/Pledge.php
index a7a0fb70802b9875b85c5ce25dbb78c236bfd4d7..5f5a7632ed42f0422aae8074942ad8065b08952c 100644
--- a/civicrm/CRM/Pledge/DAO/Pledge.php
+++ b/civicrm/CRM/Pledge/DAO/Pledge.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Pledge/Pledge.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:8373762a2a35ef14b0d763eb27db0f34)
+ * (GenCodeChecksum:27003a5c2de79b60b4114bc92b65cc07)
  */
 
 /**
  * Database access object for the Pledge entity.
  */
 class CRM_Pledge_DAO_Pledge extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Pledge/DAO/PledgeBlock.php b/civicrm/CRM/Pledge/DAO/PledgeBlock.php
index 1cb20cb50e2ec0d31befe22c314296d130ba2f14..989b14383d7a69eec50cbc6f77f234a427ff31e2 100644
--- a/civicrm/CRM/Pledge/DAO/PledgeBlock.php
+++ b/civicrm/CRM/Pledge/DAO/PledgeBlock.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Pledge/PledgeBlock.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:fd4a1319ac7b6cdc29baa3e9e4df68de)
+ * (GenCodeChecksum:bf3640355f445e127c25402500d79668)
  */
 
 /**
  * Database access object for the PledgeBlock entity.
  */
 class CRM_Pledge_DAO_PledgeBlock extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Pledge/DAO/PledgePayment.php b/civicrm/CRM/Pledge/DAO/PledgePayment.php
index b072dd7f069fdc0b7a82af573e2a8484e136b3d9..234a5342b2f73204ab0f2e8b3c3c1bd56724c024 100644
--- a/civicrm/CRM/Pledge/DAO/PledgePayment.php
+++ b/civicrm/CRM/Pledge/DAO/PledgePayment.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Pledge/PledgePayment.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:64c6aef9e06ba7320d917ec5515c5ffc)
+ * (GenCodeChecksum:c47a2cbc83c672a8209bc5e725b2f81a)
  */
 
 /**
  * Database access object for the PledgePayment entity.
  */
 class CRM_Pledge_DAO_PledgePayment extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.1';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Price/BAO/LineItem.php b/civicrm/CRM/Price/BAO/LineItem.php
index b94b20755af5c0f7a12ce73bdaa4305cdbc5f7a1..b25c02c37c51820ec9a1eee01733ebaa89b51838 100644
--- a/civicrm/CRM/Price/BAO/LineItem.php
+++ b/civicrm/CRM/Price/BAO/LineItem.php
@@ -116,34 +116,6 @@ class CRM_Price_BAO_LineItem extends CRM_Price_DAO_LineItem {
     return NULL;
   }
 
-  /**
-   * Modifies $params array for filtering financial types.
-   *
-   * @param array $params
-   *   (reference ) an assoc array of name/value pairs.
-   *
-   */
-  public static function getAPILineItemParams(&$params) {
-    CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types);
-    if ($types && empty($params['financial_type_id'])) {
-      $params['financial_type_id'] = ['IN' => array_keys($types)];
-    }
-    elseif ($types) {
-      if (is_array($params['financial_type_id'])) {
-        $invalidFts = array_diff($params['financial_type_id'], array_keys($types));
-      }
-      elseif (!in_array($params['financial_type_id'], array_keys($types))) {
-        $invalidFts = $params['financial_type_id'];
-      }
-      if ($invalidFts) {
-        $params['financial_type_id'] = ['NOT IN' => $invalidFts];
-      }
-    }
-    else {
-      $params['financial_type_id'] = 0;
-    }
-  }
-
   /**
    * @param int $contributionId
    *
@@ -426,18 +398,9 @@ WHERE li.contribution_id = %1";
         }
         if (!empty($contributionDetails->id)) {
           $line['contribution_id'] = $contributionDetails->id;
-          if ($line['entity_table'] == 'civicrm_contribution') {
+          if ($line['entity_table'] === 'civicrm_contribution') {
             $line['entity_id'] = $contributionDetails->id;
           }
-          // CRM-19094: entity_table is set to civicrm_membership then ensure
-          // the entityId is set to membership ID not contribution by default
-          elseif ($line['entity_table'] == 'civicrm_membership' && !empty($line['entity_id']) && $line['entity_id'] == $contributionDetails->id) {
-            $membershipId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment', $contributionDetails->id, 'membership_id', 'contribution_id');
-            if ($membershipId && (int) $membershipId !== (int) $line['entity_id']) {
-              $line['entity_id'] = $membershipId;
-              Civi::log()->warning('Per https://lab.civicrm.org/dev/core/issues/15 this data fix should not be required. Please log a ticket at https://lab.civicrm.org/dev/core with steps to get this.', ['civi.tag' => 'deprecated']);
-            }
-          }
         }
 
         // if financial type is not set and if price field value is NOT NULL
diff --git a/civicrm/CRM/Price/BAO/PriceField.php b/civicrm/CRM/Price/BAO/PriceField.php
index 091d9810e414e42ca5a8946cfe9612b3aa835ecb..61e570335a60af037842382172d30c38a0773ffc 100644
--- a/civicrm/CRM/Price/BAO/PriceField.php
+++ b/civicrm/CRM/Price/BAO/PriceField.php
@@ -296,7 +296,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
 
     //use value field.
     $valueFieldName = 'amount';
-    $seperator = '|';
+    $separator = '|';
     $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
     $taxTerm = Civi::settings()->get('tax_term');
     $displayOpt = $invoiceSettings['tax_display_settings'] ?? NULL;
@@ -312,7 +312,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
           $qf->assign('taxTerm', $taxTerm);
           $qf->assign('invoicing', $invoicing);
         }
-        $priceVal = implode($seperator, [
+        $priceVal = implode($separator, [
           $customOption[$optionKey][$valueFieldName] + $taxAmount,
           $count,
           $max_value,
@@ -403,7 +403,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
           }
           $count = CRM_Utils_Array::value('count', $opt, '');
           $max_value = CRM_Utils_Array::value('max_value', $opt, '');
-          $priceVal = implode($seperator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]);
+          $priceVal = implode($separator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]);
           if (isset($opt['visibility_id'])) {
             $visibility_id = $opt['visibility_id'];
           }
@@ -499,7 +499,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
             }
           }
 
-          $priceVal[$opt['id']] = implode($seperator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]);
+          $priceVal[$opt['id']] = implode($separator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]);
 
           if (!in_array($opt['id'], $freezeOptions)) {
             $allowedOptions[] = $opt['id'];
@@ -562,7 +562,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
             }
             $opt['label'] = $preHelpText . $opt['label'] . $postHelpText;
           }
-          $priceVal = implode($seperator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]);
+          $priceVal = implode($separator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]);
           $check[$opId] = &$qf->createElement('checkbox', $opt['id'], NULL, $opt['label'],
             [
               'price' => json_encode([$opt['id'], $priceVal]),
diff --git a/civicrm/CRM/Price/DAO/LineItem.php b/civicrm/CRM/Price/DAO/LineItem.php
index 1b09a54dc9f2f15a3492090492994d7567de335f..2ad0da2b2957c832bd1f7e75beadee464af8ab31 100644
--- a/civicrm/CRM/Price/DAO/LineItem.php
+++ b/civicrm/CRM/Price/DAO/LineItem.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Price/LineItem.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:84c128f076238b53a8c0c6dd23a22587)
+ * (GenCodeChecksum:7403b3615b0225350d893750a547061a)
  */
 
 /**
  * Database access object for the LineItem entity.
  */
 class CRM_Price_DAO_LineItem extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.7';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Price/DAO/PriceField.php b/civicrm/CRM/Price/DAO/PriceField.php
index babe80632895216759756c1e8c821cb9e347172b..e14f60999bfe2a6297e858f5b9d80d48b781b181 100644
--- a/civicrm/CRM/Price/DAO/PriceField.php
+++ b/civicrm/CRM/Price/DAO/PriceField.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Price/PriceField.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:7e0a818f8b8641ca3bae0abe1582c8ce)
+ * (GenCodeChecksum:1492c6421f1c3cb49dcab88bc411075c)
  */
 
 /**
  * Database access object for the PriceField entity.
  */
 class CRM_Price_DAO_PriceField extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Price/DAO/PriceFieldValue.php b/civicrm/CRM/Price/DAO/PriceFieldValue.php
index 01aff179797e27985e54b00c84879293489a1e71..c7c8e0913e40b12bf692fa82ba8d990383b35068 100644
--- a/civicrm/CRM/Price/DAO/PriceFieldValue.php
+++ b/civicrm/CRM/Price/DAO/PriceFieldValue.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Price/PriceFieldValue.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:11a02f3576be10e8c2a0ea47a19e2dac)
+ * (GenCodeChecksum:a1acc613daec86c6049e545af5fc7fd1)
  */
 
 /**
  * Database access object for the PriceFieldValue entity.
  */
 class CRM_Price_DAO_PriceFieldValue extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.3';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Price/DAO/PriceSet.php b/civicrm/CRM/Price/DAO/PriceSet.php
index 8fc97986b9fa74cbc13fea32df3bff2c58714220..1dd84351706af0cc2ba0050d707cdb5ffdd8f307 100644
--- a/civicrm/CRM/Price/DAO/PriceSet.php
+++ b/civicrm/CRM/Price/DAO/PriceSet.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Price/PriceSet.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:53c1906856d1a16d2edba1caa6a4fafc)
+ * (GenCodeChecksum:52d1fb1b25eaa8f1c157012bfec0eaae)
  */
 
 /**
  * Database access object for the PriceSet entity.
  */
 class CRM_Price_DAO_PriceSet extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Price/DAO/PriceSetEntity.php b/civicrm/CRM/Price/DAO/PriceSetEntity.php
index 36ec1ec20a042b353c4874853a8a653bfc718d8c..78e479a53a754dd5d0b21e970a826ef93e9f10fc 100644
--- a/civicrm/CRM/Price/DAO/PriceSetEntity.php
+++ b/civicrm/CRM/Price/DAO/PriceSetEntity.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Price/PriceSetEntity.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:a37bda1a3508a9195c561c2b2e4f03a7)
+ * (GenCodeChecksum:f2d6aeda95e4bde969d5ccebe9f26791)
  */
 
 /**
  * Database access object for the PriceSetEntity entity.
  */
 class CRM_Price_DAO_PriceSetEntity extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Queue/DAO/QueueItem.php b/civicrm/CRM/Queue/DAO/QueueItem.php
index 64ea7e895041eb8bf07d7ca8400bda13beccb2be..a78b613e154da0ef032542f9c076e6c499e226e8 100644
--- a/civicrm/CRM/Queue/DAO/QueueItem.php
+++ b/civicrm/CRM/Queue/DAO/QueueItem.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Queue/QueueItem.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:f71816c891a0730a45d4363883a5756c)
+ * (GenCodeChecksum:7e484400a7f8cf682b9c85e8b10c7bc7)
  */
 
 /**
  * Database access object for the QueueItem entity.
  */
 class CRM_Queue_DAO_QueueItem extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Report/DAO/ReportInstance.php b/civicrm/CRM/Report/DAO/ReportInstance.php
index 591332e6701678c2610e21e28e32137252f82ac8..08a380c76fd3e548f7e3646ab57cf092d3a68010 100644
--- a/civicrm/CRM/Report/DAO/ReportInstance.php
+++ b/civicrm/CRM/Report/DAO/ReportInstance.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/Report/ReportInstance.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:5e7790bacc5ffff1ecfcaada1abeafec)
+ * (GenCodeChecksum:d0c9e5593f161f18e7979012c4c13724)
  */
 
 /**
  * Database access object for the ReportInstance entity.
  */
 class CRM_Report_DAO_ReportInstance extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '2.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/Report/Form.php b/civicrm/CRM/Report/Form.php
index 449e1a087cb16e2570ecf6cd92abf44b1530a959..aaf8bb15fd2f57656f0eba86adc208ed8541e427 100644
--- a/civicrm/CRM/Report/Form.php
+++ b/civicrm/CRM/Report/Form.php
@@ -1465,7 +1465,7 @@ class CRM_Report_Form extends CRM_Core_Form {
     if (!CRM_Core_Permission::check('view report sql')) {
       return;
     }
-    $ignored_output_modes = ['pdf', 'csv', 'print', 'excel2007'];
+    $ignored_output_modes = ['pdf', 'csv', 'print'];
     if (in_array($this->_outputMode, $ignored_output_modes)) {
       return;
     }
@@ -2900,12 +2900,6 @@ WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND
         $this->addPaging = $this->outputHandler->isAddPaging();
         $this->_absoluteUrl = $this->outputHandler->isAbsoluteUrl();
       }
-
-    }
-    elseif ($this->_outputMode == 'excel2007') {
-      $printOnly = TRUE;
-      $this->_absoluteUrl = TRUE;
-      $this->addPaging = FALSE;
     }
     elseif ($this->_outputMode == 'copy' && $this->_criteriaForm) {
       $this->_createNew = TRUE;
@@ -5943,7 +5937,9 @@ LEFT JOIN civicrm_contact {$field['alias']} ON {$field['alias']}.id = {$this->_a
         $relative = $this->_params["{$fieldName}_relative"] ?? NULL;
         $from = $this->_params["{$fieldName}_from"] ?? NULL;
         $to = $this->_params["{$fieldName}_to"] ?? NULL;
-        return $this->dateClause($field['dbAlias'], $relative, $from, $to, $field['type']);
+        $fromTime = $this->_params["{$fieldName}_from_time"] ?? NULL;
+        $toTime = $this->_params["{$fieldName}_to_time"] ?? NULL;
+        return $this->dateClause($field['dbAlias'], $relative, $from, $to, $field['type'], $fromTime, $toTime);
       }
     }
     else {
diff --git a/civicrm/CRM/Report/Form/Contribute/Bookkeeping.php b/civicrm/CRM/Report/Form/Contribute/Bookkeeping.php
index f70c7d29d190675a4d63921b46c48b481ec0ad3d..06f89ab8c959ab10f3b892059bb996e391281fd2 100644
--- a/civicrm/CRM/Report/Form/Contribute/Bookkeeping.php
+++ b/civicrm/CRM/Report/Form/Contribute/Bookkeeping.php
@@ -300,8 +300,8 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form {
             'operatorType' => CRM_Report_Form::OP_INT,
             'type' => CRM_Utils_Type::T_INT,
           ],
-          'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
-          'receipt_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
+          'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATETIME],
+          'receipt_date' => ['operatorType' => CRM_Report_Form::OP_DATETIME],
           'contribution_source' => [
             'title' => ts('Source'),
             'name' => 'source',
@@ -317,6 +317,7 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form {
         'order_bys' => [
           'contribution_id' => ['title' => ts('Contribution #')],
           'contribution_status_id' => ['title' => ts('Contribution Status')],
+          'receive_date'  => ['title' => ts('Date Received')],
         ],
         'grouping' => 'contri-fields',
       ],
@@ -364,7 +365,7 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form {
           ],
           'trxn_date' => [
             'title' => ts('Transaction Date'),
-            'operatorType' => CRM_Report_Form::OP_DATE,
+            'operatorType' => CRM_Report_Form::OP_DATETIME,
             'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
           ],
           'status_id' => [
@@ -383,6 +384,7 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form {
         ],
         'order_bys' => [
           'payment_instrument_id' => ['title' => ts('Payment Method')],
+          'trxn_date' => ['title' => ts('Transaction Date')],
         ],
       ],
       'civicrm_entity_financial_trxn' => [
@@ -516,52 +518,31 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form {
     }
   }
 
-  public function where() {
-    foreach ($this->_columns as $tableName => $table) {
-      if (array_key_exists('filters', $table)) {
-        foreach ($table['filters'] as $fieldName => $field) {
-          $clause = NULL;
-          if (in_array($fieldName, [
-            'credit_accounting_code',
-            'credit_name',
-            'credit_contact_id',
-          ])) {
-            $field['dbAlias'] = "CASE
+  /**
+   * overriding to modify dbAlias for few fields.
+   *
+   * @param array $field Field specifications
+   * @param string $op Query operator (not an exact match to sql)
+   * @param mixed $value
+   * @param float $min
+   * @param float $max
+   *
+   * @return null|string
+   */
+  public function whereClause(&$field, $op, $value, $min, $max) {
+    if ($field['alias'] == 'financial_account_civireport_credit' &&
+      in_array($field['name'], ['accounting_code', 'id', 'contact_id'])
+    ) {
+      $field['dbAlias'] = "CASE
               WHEN financial_trxn_civireport.from_financial_account_id IS NOT NULL
               THEN  financial_account_civireport_credit_1.{$field['name']}
               ELSE  financial_account_civireport_credit_2.{$field['name']}
               END";
-          }
-          if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE) {
-            $relative = $this->_params["{$fieldName}_relative"] ?? NULL;
-            $from = $this->_params["{$fieldName}_from"] ?? NULL;
-            $to = $this->_params["{$fieldName}_to"] ?? NULL;
-
-            $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']);
-          }
-          else {
-            $op = $this->_params["{$fieldName}_op"] ?? NULL;
-            if ($op) {
-              $clause = $this->whereClause($field,
-                $op,
-                CRM_Utils_Array::value("{$fieldName}_value", $this->_params),
-                CRM_Utils_Array::value("{$fieldName}_min", $this->_params),
-                CRM_Utils_Array::value("{$fieldName}_max", $this->_params)
-              );
-            }
-          }
-          if (!empty($clause)) {
-            $clauses[] = $clause;
-          }
-        }
-      }
-    }
-    if (empty($clauses)) {
-      $this->_where = 'WHERE ( 1 )';
-    }
-    else {
-      $this->_where = 'WHERE ' . implode(' AND ', $clauses);
     }
+
+    $clause = parent::whereClause($field, $op, $value, $min, $max);
+
+    return $clause;
   }
 
   public function postProcess() {
diff --git a/civicrm/CRM/Report/Form/Contribute/History.php b/civicrm/CRM/Report/Form/Contribute/History.php
index c7526b39188f8aab26aa41af0d6ff34bdf6d195b..bc1a49fe0214a90167e9f16566a26da12e746507 100644
--- a/civicrm/CRM/Report/Form/Contribute/History.php
+++ b/civicrm/CRM/Report/Form/Contribute/History.php
@@ -815,7 +815,7 @@ class CRM_Report_Form_Contribute_History extends CRM_Report_Form {
           }
 
           if ($last_primary && ($rowNum == "{$last_primary}_total")) {
-            $value = CRM_Utils_Money::format($value, ' ');
+            $value = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($value);
           }
           $row[$key] = '<strong>' . $value . '</strong>';
         }
diff --git a/civicrm/CRM/Report/Form/Event/ParticipantListing.php b/civicrm/CRM/Report/Form/Event/ParticipantListing.php
index c465fb6f85314a260c74e5fb5c65fe92c1f6915d..faefeac943a7db44acecc51e84d92377aa15a55f 100644
--- a/civicrm/CRM/Report/Form/Event/ParticipantListing.php
+++ b/civicrm/CRM/Report/Form/Event/ParticipantListing.php
@@ -693,7 +693,7 @@ ORDER BY  cv.label
         }
       }
 
-      // Handle value seperator in Fee Level
+      // Handle value separator in Fee Level
       if (array_key_exists('civicrm_participant_participant_fee_level', $row)) {
         $feeLevel = $row['civicrm_participant_participant_fee_level'];
         if ($feeLevel) {
diff --git a/civicrm/CRM/Report/Form/Mailing/Summary.php b/civicrm/CRM/Report/Form/Mailing/Summary.php
index 4289f9020bea9dfb584d50b477dd7d878304db17..1bee34bd33051989f05e73c883163d11669c2290 100644
--- a/civicrm/CRM/Report/Form/Mailing/Summary.php
+++ b/civicrm/CRM/Report/Form/Mailing/Summary.php
@@ -54,6 +54,12 @@ class CRM_Report_Form_Mailing_Summary extends CRM_Report_Form {
         'subject' => [
           'title' => ts('Subject'),
         ],
+        'from_name' => [
+          'title' => ts('Sender Name'),
+        ],
+        'from_email' => [
+          'title' => ts('Sender Email'),
+        ],
       ],
       'filters' => [
         'is_completed' => [
diff --git a/civicrm/CRM/SMS/DAO/Provider.php b/civicrm/CRM/SMS/DAO/Provider.php
index 8f87fa5a1402fc1b4607aae28cf50860be450bd6..3aa49ab0a6da3128f2b2fca659004cfdc6946ac8 100644
--- a/civicrm/CRM/SMS/DAO/Provider.php
+++ b/civicrm/CRM/SMS/DAO/Provider.php
@@ -6,13 +6,15 @@
  *
  * Generated from xml/schema/CRM/SMS/Provider.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:b512d0ed25ec37b9c890952e48c667e6)
+ * (GenCodeChecksum:6ecda65bd52b36e04764cec8ee81e1b8)
  */
 
 /**
  * Database access object for the Provider entity.
  */
 class CRM_SMS_DAO_Provider extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '4.2';
 
   /**
    * Static instance to hold the table name.
diff --git a/civicrm/CRM/SMS/Form/Group.php b/civicrm/CRM/SMS/Form/Group.php
index 4a53b78852a8bd20957985174364d3f4eef6d1b8..9755626958142e612333687f3f1075af7d85289e 100644
--- a/civicrm/CRM/SMS/Form/Group.php
+++ b/civicrm/CRM/SMS/Form/Group.php
@@ -102,7 +102,7 @@ class CRM_SMS_Form_Group extends CRM_Contact_Form_Task {
     );
 
     // Get the mailing groups.
-    $groups = CRM_Core_PseudoConstant::nestedGroup('Mailing');
+    $groups = CRM_Core_PseudoConstant::nestedGroup(TRUE, 'Mailing');
 
     // Get the sms mailing list.
     $mailings = CRM_Mailing_PseudoConstant::completed('sms');
diff --git a/civicrm/CRM/SMS/Form/Schedule.php b/civicrm/CRM/SMS/Form/Schedule.php
index 3de19681be32beaffb1605d379f769979b4d25c7..04d68bda9f85001b7542fa0b8551d0a43a8383ee 100644
--- a/civicrm/CRM/SMS/Form/Schedule.php
+++ b/civicrm/CRM/SMS/Form/Schedule.php
@@ -60,7 +60,7 @@ class CRM_SMS_Form_Schedule extends CRM_Core_Form {
       'send_later' => ['id' => 'send_later'],
     ]);
 
-    $this->add('datepicker', 'start_date', '', NULL, FALSE, ['minDate' => time()]);
+    $this->add('datepicker', 'start_date', '', NULL, FALSE, ['minDate' => date('Y-m-d')]);
 
     $this->addFormRule(['CRM_SMS_Form_Schedule', 'formRule'], $this);
 
diff --git a/civicrm/CRM/UF/Form/AdvanceSetting.php b/civicrm/CRM/UF/Form/AdvanceSetting.php
index 0a7be0b72404737bb857a03598a877d094dc606f..b78c0c5b86374c76970a39c273a903d34774c764 100644
--- a/civicrm/CRM/UF/Form/AdvanceSetting.php
+++ b/civicrm/CRM/UF/Form/AdvanceSetting.php
@@ -47,7 +47,7 @@ class CRM_UF_Form_AdvanceSetting extends CRM_UF_Form_Group {
     $form->addElement('text', 'notify', ts('Notify when profile form is submitted?'));
 
     //group where new contacts are directed.
-    $form->addElement('select', 'add_contact_to_group', ts('Add new contacts to a Group?'), $group);
+    $form->addElement('select', 'add_contact_to_group', ts('Add contacts to a group?'), $group);
 
     // add CAPTCHA To this group ?
     $form->addElement('checkbox', 'add_captcha', ts('Include reCAPTCHA?'));
diff --git a/civicrm/CRM/Upgrade/Form.php b/civicrm/CRM/Upgrade/Form.php
index 6eb65bf70d3f7092ab6983565eb6d95ecdaaaf76..2ab3c1c7e8d87d909d729d1638c3e331336e53e9 100644
--- a/civicrm/CRM/Upgrade/Form.php
+++ b/civicrm/CRM/Upgrade/Form.php
@@ -286,19 +286,6 @@ SET    version = '$version'
     return FALSE;
   }
 
-  /**
-   * @param $version
-   *
-   * @return bool
-   */
-  public function checkVersion($version) {
-    $domainID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Domain',
-      $version, 'id',
-      'version'
-    );
-    return (bool) $domainID;
-  }
-
   /**
    * @return array
    * @throws Exception
diff --git a/civicrm/CRM/Upgrade/Incremental/MessageTemplates.php b/civicrm/CRM/Upgrade/Incremental/MessageTemplates.php
index 0b86bc6a6dce72e55275d2fd4cafd1a0e7179c07..0fc233bb2b23babf415ff15c0bad2b11a7d370d5 100644
--- a/civicrm/CRM/Upgrade/Incremental/MessageTemplates.php
+++ b/civicrm/CRM/Upgrade/Incremental/MessageTemplates.php
@@ -215,7 +215,23 @@ class CRM_Upgrade_Incremental_MessageTemplates {
           ['name' => 'contribution_invoice_receipt', 'type' => 'html'],
         ],
       ],
-
+      [
+        'version' => '5.30.alpha1',
+        'upgrade_descriptor' => ts('Support negative hours for cancellation/transfer'),
+        'templates' => [
+          ['name' => 'participant_confirm', 'type' => 'html'],
+          ['name' => 'participant_confirm', 'type' => 'text'],
+          ['name' => 'event_online_receipt', 'type' => 'html'],
+          ['name' => 'event_online_receipt', 'type' => 'text'],
+        ],
+      ],
+      [
+        'version' => '5.30.beta1',
+        'upgrade_descriptor' => ts('Ensure that amount paid is shown even when fully paid'),
+        'templates' => [
+          ['name' => 'contribution_invoice_receipt', 'type' => 'html'],
+        ],
+      ],
     ];
   }
 
diff --git a/civicrm/CRM/Upgrade/Incremental/php/FiveThirty.php b/civicrm/CRM/Upgrade/Incremental/php/FiveThirty.php
new file mode 100644
index 0000000000000000000000000000000000000000..8ccaf073f383e16253339f4a01d8f8fe40f10e60
--- /dev/null
+++ b/civicrm/CRM/Upgrade/Incremental/php/FiveThirty.php
@@ -0,0 +1,114 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Upgrade logic for FiveThirty */
+class CRM_Upgrade_Incremental_php_FiveThirty extends CRM_Upgrade_Incremental_Base {
+
+  /**
+   * Compute any messages which should be displayed beforeupgrade.
+   *
+   * Note: This function is called iteratively for each upcoming
+   * revision to the database.
+   *
+   * @param string $preUpgradeMessage
+   * @param string $rev
+   *   a version number, e.g. '4.4.alpha1', '4.4.beta3', '4.4.0'.
+   * @param null $currentVer
+   */
+  public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) {
+    // Example: Generate a pre-upgrade message.
+    // if ($rev == '5.12.34') {
+    //   $preUpgradeMessage .= '<p>' . ts('A new permission, "%1", has been added. This permission is now used to control access to the Manage Tags screen.', array(1 => ts('manage tags'))) . '</p>';
+    // }
+  }
+
+  /**
+   * Compute any messages which should be displayed after upgrade.
+   *
+   * @param string $postUpgradeMessage
+   *   alterable.
+   * @param string $rev
+   *   an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs.
+   */
+  public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) {
+    // Example: Generate a post-upgrade message.
+    // if ($rev == '5.12.34') {
+    //   $postUpgradeMessage .= '<br /><br />' . ts("By default, CiviCRM now disables the ability to import directly from SQL. To use this feature, you must explicitly grant permission 'import SQL datasource'.");
+    // }
+  }
+
+  /*
+   * Important! All upgrade functions MUST add a 'runSql' task.
+   * Uncomment and use the following template for a new upgrade version
+   * (change the x in the function name):
+   */
+
+  //  /**
+  //   * Upgrade function.
+  //   *
+  //   * @param string $rev
+  //   */
+  //  public function upgrade_5_0_x($rev) {
+  //    $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev);
+  //    $this->addTask('Do the foo change', 'taskFoo', ...);
+  //    // Additional tasks here...
+  //    // Note: do not use ts() in the addTask description because it adds unnecessary strings to transifex.
+  //    // The above is an exception because 'Upgrade DB to %1: SQL' is generic & reusable.
+  //  }
+
+  /**
+   * Upgrade function.
+   *
+   * @param string $rev
+   */
+  public function upgrade_5_30_alpha1($rev) {
+    $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev);
+    $this->addTask('Add core (required) extension Financial ACLs', 'installFinancialAcls');
+  }
+
+  // public static function taskFoo(CRM_Queue_TaskContext $ctx, ...) {
+  //   return TRUE;
+  // }
+
+  /**
+   * Install financialacls extension.
+   *
+   * This feature is restructured as a core extension - which is primarily a code cleanup step.
+   *
+   * @param \CRM_Queue_TaskContext $ctx
+   *
+   * @return bool
+   *
+   * @throws \CiviCRM_API3_Exception
+   * @throws \CRM_Core_Exception
+   */
+  public static function installFinancialAcls(CRM_Queue_TaskContext $ctx) {
+    // Install via direct SQL manipulation. Note that:
+    // (1) This extension has no activation logic.
+    // (2) On new installs, the extension is activated purely via default SQL INSERT.
+    // (3) Caches are flushed at the end of the upgrade.
+    // ($) Over long term, upgrade steps are more reliable in SQL. API/BAO sometimes don't work mid-upgrade.
+    $insert = CRM_Utils_SQL_Insert::into('civicrm_extension')->row([
+      'type' => 'module',
+      'full_name' => 'financialacls',
+      'name' => 'financialacls',
+      'label' => 'Financial ACLs',
+      'file' => 'financialacls',
+      'schema_version' => NULL,
+      'is_active' => 1,
+    ]);
+    CRM_Core_DAO::executeQuery($insert->usingReplace()->toSQL());
+
+    return TRUE;
+  }
+
+}
diff --git a/civicrm/CRM/Upgrade/Incremental/php/FourFive.php b/civicrm/CRM/Upgrade/Incremental/php/FourFive.php
index d905ea536b06ac08ba0299b034f87a13bd1e4e38..0f27a0be48ed49c7f4589bb20380a8843263019d 100644
--- a/civicrm/CRM/Upgrade/Incremental/php/FourFive.php
+++ b/civicrm/CRM/Upgrade/Incremental/php/FourFive.php
@@ -52,7 +52,8 @@ class CRM_Upgrade_Incremental_php_FourFive extends CRM_Upgrade_Incremental_Base
     // if DB is been into upgrade for 3.4.2 version, it would have pdf_format_id name for FK
     // else FK_civicrm_msg_template_pdf_format_id
     $config = CRM_Core_Config::singleton();
-    $dbUf = DB::parseDSN($config->dsn);
+    $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn);
+    $dbUf = DB::parseDSN($dsn);
     $query = "
 SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
 WHERE TABLE_NAME = 'civicrm_msg_template'
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.29.0.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.29.0.mysql.tpl
deleted file mode 100644
index a25ed8c29a8dde72eb73f979a4238bec9b15a240..0000000000000000000000000000000000000000
--- a/civicrm/CRM/Upgrade/Incremental/sql/5.29.0.mysql.tpl
+++ /dev/null
@@ -1 +0,0 @@
-{* file to handle db changes in 5.29.0 during upgrade *}
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..110936b376fb54981eacb41cc27a8588a17a66ec
--- /dev/null
+++ b/civicrm/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl
@@ -0,0 +1 @@
+{* file to handle db changes in 5.30.0 during upgrade *}
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..3b0804b3c3fc5b60548ab5013575238c2be1d2ae
--- /dev/null
+++ b/civicrm/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl
@@ -0,0 +1,4 @@
+{* file to handle db changes in 5.30.alpha1 during upgrade *}
+-- Allow self-service/transfer to have a negative time.
+ALTER TABLE civicrm_event MODIFY COLUMN selfcancelxfer_time INT;
+
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..9c22ccacf110d659e6888360f8145f0844e5a662
--- /dev/null
+++ b/civicrm/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl
@@ -0,0 +1 @@
+{* file to handle db changes in 5.30.beta1 during upgrade *}
diff --git a/civicrm/CRM/Upgrade/Page/Upgrade.php b/civicrm/CRM/Upgrade/Page/Upgrade.php
index 77b7ad1103161a52bbe00f63278a4e04f819b9ed..06a377924c141abcfc58504a5249f41437250b69 100644
--- a/civicrm/CRM/Upgrade/Page/Upgrade.php
+++ b/civicrm/CRM/Upgrade/Page/Upgrade.php
@@ -78,35 +78,49 @@ class CRM_Upgrade_Page_Upgrade extends CRM_Core_Page {
     $template = CRM_Core_Smarty::singleton();
     list($currentVer, $latestVer) = $upgrade->getUpgradeVersions();
 
-    if ($error = $upgrade->checkUpgradeableVersion($currentVer, $latestVer)) {
+    // Show success msg if db already upgraded
+    if (version_compare($currentVer, $latestVer) == 0) {
+      $template->assign('upgraded', TRUE);
+      $template->assign('newVersion', $latestVer);
+      CRM_Utils_System::setTitle(ts('Your database has already been upgraded to CiviCRM %1',
+        [1 => $latestVer]
+      ));
+      $template->assign('pageTitle', ts('Your database has already been upgraded to CiviCRM %1',
+        [1 => $latestVer]
+      ));
+    }
+
+    // Throw error if db in unexpected condition
+    elseif ($error = $upgrade->checkUpgradeableVersion($currentVer, $latestVer)) {
       throw new CRM_Core_Exception($error);
     }
 
-    $config = CRM_Core_Config::singleton();
+    else {
+      $config = CRM_Core_Config::singleton();
 
-    // All cached content needs to be cleared because the civi codebase was just replaced
-    CRM_Core_Resources::singleton()->flushStrings()->resetCacheCode();
+      // All cached content needs to be cleared because the civi codebase was just replaced
+      CRM_Core_Resources::singleton()->flushStrings()->resetCacheCode();
 
-    // cleanup only the templates_c directory
-    $config->cleanup(1, FALSE);
+      // cleanup only the templates_c directory
+      $config->cleanup(1, FALSE);
 
-    $preUpgradeMessage = NULL;
-    $upgrade->setPreUpgradeMessage($preUpgradeMessage, $currentVer, $latestVer);
+      $preUpgradeMessage = NULL;
+      $upgrade->setPreUpgradeMessage($preUpgradeMessage, $currentVer, $latestVer);
 
-    $template->assign('currentVersion', $currentVer);
-    $template->assign('newVersion', $latestVer);
-    $template->assign('upgradeTitle', ts('Upgrade CiviCRM from v %1 To v %2',
-      [1 => $currentVer, 2 => $latestVer]
-    ));
-    $template->assign('upgraded', FALSE);
+      $template->assign('preUpgradeMessage', $preUpgradeMessage);
+      $template->assign('currentVersion', $currentVer);
+      $template->assign('newVersion', $latestVer);
+      $template->assign('upgradeTitle', ts('Upgrade CiviCRM from v %1 To v %2',
+        [1 => $currentVer, 2 => $latestVer]
+      ));
+      $template->assign('upgraded', FALSE);
+    }
 
     // Render page header
     if (!defined('CIVICRM_UF_HEAD') && $region = CRM_Core_Region::instance('html-header', FALSE)) {
       CRM_Utils_System::addHTMLHead($region->render(''));
     }
 
-    $template->assign('preUpgradeMessage', $preUpgradeMessage);
-
     $content = $template->fetch('CRM/common/success.tpl');
     echo CRM_Utils_System::theme($content, $this->_print, TRUE);
   }
diff --git a/civicrm/CRM/Utils/Array.php b/civicrm/CRM/Utils/Array.php
index 14563ccabe757d15bf3f1d535290189330795416..5ecdb554d58b29b404cecba3f84c0c40ff8caf90 100644
--- a/civicrm/CRM/Utils/Array.php
+++ b/civicrm/CRM/Utils/Array.php
@@ -119,26 +119,26 @@ class CRM_Utils_Array {
    *   The array to be serialized.
    * @param int $depth
    *   (optional) Indentation depth counter.
-   * @param string $seperator
+   * @param string $separator
    *   (optional) String to be appended after open/close tags.
    *
    * @return string
    *   XML fragment representing $list.
    */
-  public static function &xml(&$list, $depth = 1, $seperator = "\n") {
+  public static function &xml(&$list, $depth = 1, $separator = "\n") {
     $xml = '';
     foreach ($list as $name => $value) {
       $xml .= str_repeat(' ', $depth * 4);
       if (is_array($value)) {
-        $xml .= "<{$name}>{$seperator}";
-        $xml .= self::xml($value, $depth + 1, $seperator);
+        $xml .= "<{$name}>{$separator}";
+        $xml .= self::xml($value, $depth + 1, $separator);
         $xml .= str_repeat(' ', $depth * 4);
-        $xml .= "</{$name}>{$seperator}";
+        $xml .= "</{$name}>{$separator}";
       }
       else {
         // make sure we escape value
         $value = self::escapeXML($value);
-        $xml .= "<{$name}>$value</{$name}>{$seperator}";
+        $xml .= "<{$name}>$value</{$name}>{$separator}";
       }
     }
     return $xml;
@@ -218,14 +218,14 @@ class CRM_Utils_Array {
    *   Destination array.
    * @param string $prefix
    *   (optional) String to prepend to keys.
-   * @param string $seperator
+   * @param string $separator
    *   (optional) String that separates the concatenated keys.
    */
-  public static function flatten(&$list, &$flat, $prefix = '', $seperator = ".") {
+  public static function flatten(&$list, &$flat, $prefix = '', $separator = ".") {
     foreach ($list as $name => $value) {
-      $newPrefix = ($prefix) ? $prefix . $seperator . $name : $name;
+      $newPrefix = ($prefix) ? $prefix . $separator . $name : $name;
       if (is_array($value)) {
-        self::flatten($value, $flat, $newPrefix, $seperator);
+        self::flatten($value, $flat, $newPrefix, $separator);
       }
       else {
         $flat[$newPrefix] = $value;
diff --git a/civicrm/CRM/Utils/Check/Component.php b/civicrm/CRM/Utils/Check/Component.php
index 37a7171cab4373bd647129b919c41741f43a02da..2f57004adeac08cab6f600a86befab37e61c55f3 100644
--- a/civicrm/CRM/Utils/Check/Component.php
+++ b/civicrm/CRM/Utils/Check/Component.php
@@ -28,15 +28,9 @@ abstract class CRM_Utils_Check_Component {
    */
   public function getChecksConfig() {
     if (!isset(Civi::$statics[__FUNCTION__])) {
-      // TODO: Remove this check when MINIMUM_UPGRADABLE_VERSION goes to 4.7.
-      if (CRM_Utils_System::version() !== CRM_Core_BAO_Domain::version() && !CRM_Core_DAO::checkTableExists('civicrm_status_pref')) {
-        Civi::$statics[__FUNCTION__] = [];
-      }
-      else {
-        Civi::$statics[__FUNCTION__] = (array) StatusPreference::get(FALSE)
-          ->addWhere('domain_id', '=', 'current_domain')
-          ->execute()->indexBy('name');
-      }
+      Civi::$statics[__FUNCTION__] = (array) StatusPreference::get(FALSE)
+        ->addWhere('domain_id', '=', 'current_domain')
+        ->execute()->indexBy('name');
     }
     return Civi::$statics[__FUNCTION__];
   }
diff --git a/civicrm/CRM/Utils/Check/Component/Env.php b/civicrm/CRM/Utils/Check/Component/Env.php
index 26e77d8caedd52249cb00a0ebe8a1707a0d3b6d8..7c57df65fd6e778e9f11bccfc9f95bfb05956d02 100644
--- a/civicrm/CRM/Utils/Check/Component/Env.php
+++ b/civicrm/CRM/Utils/Check/Component/Env.php
@@ -471,13 +471,15 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component {
 
   /**
    * Checks if new versions are available
+   * @param bool $force
    * @return CRM_Utils_Check_Message[]
+   * @throws CRM_Core_Exception
    */
-  public function checkVersion() {
+  public function checkVersion($force = FALSE) {
     $messages = [];
     try {
       $vc = new CRM_Utils_VersionCheck();
-      $vc->initialize();
+      $vc->initialize($force);
     }
     catch (Exception $e) {
       $messages[] = new CRM_Utils_Check_Message(
@@ -492,7 +494,7 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component {
     }
 
     // Show a notice if the version_check job is disabled
-    if (empty($vc->cronJob['is_active'])) {
+    if (!$force && empty($vc->cronJob['is_active'])) {
       $args = empty($vc->cronJob['id']) ? ['reset' => 1] : ['reset' => 1, 'action' => 'update', 'id' => $vc->cronJob['id']];
       $messages[] = new CRM_Utils_Check_Message(
         'checkVersionDisabled',
@@ -504,15 +506,9 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component {
     }
 
     if ($vc->isInfoAvailable) {
-      $severities = [
-        'info' => CRM_Utils_Check::severityMap(\Psr\Log\LogLevel::INFO),
-        'notice' => CRM_Utils_Check::severityMap(\Psr\Log\LogLevel::NOTICE) ,
-        'warning' => CRM_Utils_Check::severityMap(\Psr\Log\LogLevel::WARNING) ,
-        'critical' => CRM_Utils_Check::severityMap(\Psr\Log\LogLevel::CRITICAL),
-      ];
       foreach ($vc->getVersionMessages() ?? [] as $msg) {
         $messages[] = new CRM_Utils_Check_Message(__FUNCTION__ . '_' . $msg['name'],
-          $msg['message'], $msg['title'], $severities[$msg['severity']], 'fa-cloud-upload');
+          $msg['message'], $msg['title'], $msg['severity'], 'fa-cloud-upload');
       }
     }
 
diff --git a/civicrm/CRM/Utils/Check/Message.php b/civicrm/CRM/Utils/Check/Message.php
index 94f51db49d9da3899c647c2fd12a9e9edcfe2372..e0a7d51d7b308c53d5b2ad7638ef50c376737b18 100644
--- a/civicrm/CRM/Utils/Check/Message.php
+++ b/civicrm/CRM/Utils/Check/Message.php
@@ -246,32 +246,25 @@ class CRM_Utils_Check_Message {
    * @throws \CiviCRM_API3_Exception
    */
   private function checkStatusPreference() {
-    // TODO: Remove this check when MINIMUM_UPGRADABLE_VERSION goes to 4.7.
-    if (CRM_Utils_System::version() !== CRM_Core_BAO_Domain::version() && !CRM_Core_DAO::checkTableExists('civicrm_status_pref')) {
-      return FALSE;
-    }
-
     $this->hiddenUntil = FALSE;
     // Debug & info can't be hidden
     if ($this->level < 2) {
       return FALSE;
     }
-    $statusPreferenceParams = [
-      'name' => $this->getName(),
-      'domain_id' => CRM_Core_Config::domainID(),
-      'sequential' => 1,
+    $where = [
+      ['name', '=', $this->getName()],
+      ['domain_id', '=', CRM_Core_Config::domainID()],
     ];
     // Check if there's a StatusPreference matching this name/domain.
-    $statusPreference = civicrm_api3('StatusPreference', 'get', $statusPreferenceParams);
-    $prefs = CRM_Utils_Array::value('values', $statusPreference, []);
-    if ($prefs) {
+    $pref = civicrm_api4('StatusPreference', 'get', ['checkPermissions' => FALSE, 'where' => $where])->first();
+    if ($pref) {
       // If so, compare severity to StatusPreference->severity.
-      if ($this->level <= $prefs[0]['ignore_severity']) {
-        if (isset($prefs[0]['hush_until'])) {
+      if ($this->level <= $pref['ignore_severity']) {
+        if (isset($pref['hush_until'])) {
           // Time-based hush.
-          $this->hiddenUntil = $prefs[0]['hush_until'];
+          $this->hiddenUntil = $pref['hush_until'];
           $today = new DateTime();
-          $snoozeDate = new DateTime($prefs[0]['hush_until']);
+          $snoozeDate = new DateTime($pref['hush_until']);
           return !($today > $snoozeDate);
         }
         else {
diff --git a/civicrm/CRM/Utils/Date.php b/civicrm/CRM/Utils/Date.php
index 831fca5bc8f5cd6397527e191616b7187a04ac39..11008a84439bc91cb17dc033b0beeab5c8ec834d 100644
--- a/civicrm/CRM/Utils/Date.php
+++ b/civicrm/CRM/Utils/Date.php
@@ -26,7 +26,7 @@ class CRM_Utils_Date {
    * @param array $date
    *   ('Y', 'M', 'd').
    * @param string $separator
-   *   The seperator to use when formatting the date.
+   *   The separator to use when formatting the date.
    * @param int|string $invalidDate what to return if the date is invalid
    *
    * @return string
diff --git a/civicrm/CRM/Utils/File.php b/civicrm/CRM/Utils/File.php
index 67da4b10a665daa9c2402aad61e81b434eeffd3b..c196b3dd712697cfe368567e025b4f244a137be1 100644
--- a/civicrm/CRM/Utils/File.php
+++ b/civicrm/CRM/Utils/File.php
@@ -325,6 +325,7 @@ class CRM_Utils_File {
     }
     else {
       require_once 'DB.php';
+      $dsn = CRM_Utils_SQL::autoSwitchDSN($dsn);
       $db = DB::connect($dsn);
     }
 
diff --git a/civicrm/CRM/Utils/Mail/EmailProcessor.php b/civicrm/CRM/Utils/Mail/EmailProcessor.php
index 9d68310b9dfef8fd455bc1e9ac62984ef1df050e..a8be02ce0ac02946a493c804388edcb99db5cd52 100644
--- a/civicrm/CRM/Utils/Mail/EmailProcessor.php
+++ b/civicrm/CRM/Utils/Mail/EmailProcessor.php
@@ -134,10 +134,10 @@ class CRM_Utils_Mail_EmailProcessor {
     }
 
     $config = CRM_Core_Config::singleton();
-    $verpSeperator = preg_quote($config->verpSeparator);
-    $twoDigitStringMin = $verpSeperator . '(\d+)' . $verpSeperator . '(\d+)';
-    $twoDigitString = $twoDigitStringMin . $verpSeperator;
-    $threeDigitString = $twoDigitString . '(\d+)' . $verpSeperator;
+    $verpSeparator = preg_quote($config->verpSeparator);
+    $twoDigitStringMin = $verpSeparator . '(\d+)' . $verpSeparator . '(\d+)';
+    $twoDigitString = $twoDigitStringMin . $verpSeparator;
+    $threeDigitString = $twoDigitString . '(\d+)' . $verpSeparator;
 
     // FIXME: legacy regexen to handle CiviCRM 2.1 address patterns, with domain id and possible VERP part
     $commonRegex = '/^' . preg_quote($dao->localpart) . '(b|bounce|c|confirm|o|optOut|r|reply|re|e|resubscribe|u|unsubscribe)' . $threeDigitString . '([0-9a-f]{16})(-.*)?@' . preg_quote($dao->domain) . '$/';
diff --git a/civicrm/CRM/Utils/SQL.php b/civicrm/CRM/Utils/SQL.php
index 70e1c5a26ed30a9c71bfd8ee43047114e45d3f14..3b9167e6265f5301a81023024ff084d4134dbce2 100644
--- a/civicrm/CRM/Utils/SQL.php
+++ b/civicrm/CRM/Utils/SQL.php
@@ -168,4 +168,39 @@ class CRM_Utils_SQL {
     return CRM_Core_DAO::singleValueQuery('SELECT VERSION()');
   }
 
+  /**
+   * Does the DSN indicate the connection should use ssl.
+   *
+   * @param string $dsn
+   *
+   * @return bool
+   */
+  public static function isSSLDSN(string $dsn):bool {
+    // Note that ssl= below is not an official PEAR::DB option. It doesn't know
+    // what to do with it. We made it up because it's not required
+    // to have client-side certificates to use ssl, so here you can specify
+    // you want that by putting ssl=1 in the DSN string.
+    //
+    // Cast to bool in case of error which we interpret as no ssl.
+    return (bool) preg_match('/[\?&](key|cert|ca|capath|cipher|ssl)=/', $dsn);
+  }
+
+  /**
+   * If DB_DSN_MODE is auto then we should replace mysql with mysqli if mysqli is available or the other way around as appropriate
+   * @param string $dsn
+   *
+   * @return string
+   */
+  public static function autoSwitchDSN($dsn) {
+    if (defined('DB_DSN_MODE') && DB_DSN_MODE === 'auto') {
+      if (extension_loaded('mysqli')) {
+        $dsn = preg_replace('/^mysql:/', 'mysqli:', $dsn);
+      }
+      else {
+        $dsn = preg_replace('/^mysqli:/', 'mysql:', $dsn);
+      }
+    }
+    return $dsn;
+  }
+
 }
diff --git a/civicrm/CRM/Utils/SQL/TempTable.php b/civicrm/CRM/Utils/SQL/TempTable.php
index 8993807f3cc5e03a12d7722ab69960b9fde469bb..89ed932432e7d33f5435263676d62ce2162d9341 100644
--- a/civicrm/CRM/Utils/SQL/TempTable.php
+++ b/civicrm/CRM/Utils/SQL/TempTable.php
@@ -50,7 +50,14 @@
  */
 class CRM_Utils_SQL_TempTable {
 
+  /**
+   * @deprecated
+   * The system will attempt to use the same as your other tables, and
+   * if you really need something else then use createWithColumns and
+   * specify it per-column there.
+   */
   const UTF8 = 'DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci';
+
   const CATEGORY_LENGTH = 12;
   const CATEGORY_REGEXP = ';^[a-zA-Z0-9]+$;';
   // MAX{64} - CATEGORY_LENGTH{12} - CONST_LENGHTH{15} = 37
@@ -135,8 +142,10 @@ class CRM_Utils_SQL_TempTable {
   /**
    * Get the utf8 string for the table.
    *
-   * Our tables are either utf8_unicode_ci OR utf8mb8_unicode_ci - check the contact table
-   * to see which & use the matching one.
+   * Our tables are either utf8_unicode_ci OR utf8mb4_unicode_ci - check the contact table
+   * to see which & use the matching one. Or early adopters may have switched
+   * switched to other collations e.g. utf8mb4_0900_ai_ci (the default in mysql
+   * 8).
    *
    * @return string
    */
@@ -241,9 +250,11 @@ class CRM_Utils_SQL_TempTable {
   }
 
   /**
+   * @deprecated
    * @return bool
    */
   public function isUtf8() {
+    CRM_Core_Error::deprecatedFunctionWarning('your own charset/collation per column with createWithColumns if you really need latin1');
     return $this->utf8;
   }
 
@@ -322,6 +333,7 @@ class CRM_Utils_SQL_TempTable {
    * @return $this
    */
   public function setUtf8($value = TRUE) {
+    CRM_Core_Error::deprecatedFunctionWarning('your own charset/collation per column with createWithColumns if you really need latin1');
     $this->utf8 = $value;
     return $this;
   }
diff --git a/civicrm/CRM/Utils/SameSite.php b/civicrm/CRM/Utils/SameSite.php
new file mode 100644
index 0000000000000000000000000000000000000000..0431761aae659c3a75addec056aedd932b9bcd43
--- /dev/null
+++ b/civicrm/CRM/Utils/SameSite.php
@@ -0,0 +1,224 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * SameSite Utility Class.
+ *
+ * Determines if the current User Agent can handle the `SameSite=None` parameter
+ * by mapping against known incompatible clients.
+ *
+ * Sample code:
+ *
+ * // Get User Agent string.
+ * $rawUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
+ * $userAgent = mb_convert_encoding($rawUserAgent, 'UTF-8');
+ *
+ * // Get boolean representing User Agent compatibility.
+ * $shouldUseSameSite = CRM_Utils_SameSite::shouldSendSameSiteNone($userAgent);
+ *
+ * Based on code provided by "The Chromium Projects".
+ *
+ * @see https://www.chromium.org/updates/same-site/incompatible-clients
+ */
+class CRM_Utils_SameSite {
+
+  /**
+   * Determine if the current User Agent can handle the `SameSite=None` parameter.
+   *
+   * @param str $userAgent The User Agent.
+   * @return bool True if the User Agent is compatible, FALSE otherwise.
+   */
+  public static function shouldSendSameSiteNone($userAgent) {
+    return !self::isSameSiteNoneIncompatible($userAgent);
+  }
+
+  /**
+   * Detect classes of browsers known to be incompatible.
+   *
+   * @param str $userAgent The User Agent.
+   * @return bool True if the User Agent is determined to be incompatible, FALSE otherwise.
+   */
+  private static function isSameSiteNoneIncompatible($userAgent) {
+    return self::hasWebKitSameSiteBug($userAgent) ||
+           self::dropsUnrecognizedSameSiteCookies($userAgent);
+  }
+
+  /**
+   * Detect versions of Safari and embedded browsers on MacOS 10.14 and all
+   * browsers on iOS 12.
+   *
+   * These versions will erroneously treat cookies marked with `SameSite=None`
+   * as if they were marked `SameSite=Strict`.
+   *
+   * @param str $userAgent The User Agent.
+   * @return bool
+   */
+  private static function hasWebKitSameSiteBug($userAgent) {
+    return self::isIosVersion(12, $userAgent) || (self::isMacosxVersion(10, 14, $userAgent) &&
+           (self::isSafari($userAgent) || self::isMacEmbeddedBrowser($userAgent)));
+  }
+
+  /**
+   * Detect versions of UC Browser on Android prior to version 12.13.2.
+   *
+   * Older versions will reject a cookie with `SameSite=None`. This behavior was
+   * correct according to the version of the cookie specification at that time,
+   * but with the addition of the new "None" value to the specification, this
+   * behavior has been updated in newer versions of UC Browser.
+   *
+   * @param str $userAgent The User Agent.
+   * @return bool
+   */
+  private static function dropsUnrecognizedSameSiteCookies($userAgent) {
+    if (self::isUcBrowser($userAgent)) {
+      return !self::isUcBrowserVersionAtLeast(12, 13, 2, $userAgent);
+    }
+
+    return self::isChromiumBased($userAgent) &&
+           self::isChromiumVersionAtLeast(51, $userAgent, '>=') &&
+           self::isChromiumVersionAtLeast(67, $userAgent, '<=');
+  }
+
+  /**
+   * Detect iOS version.
+   *
+   * @param int $major The major version to test.
+   * @param str $userAgent The User Agent.
+   * @return bool
+   */
+  private static function isIosVersion($major, $userAgent) {
+    $regex = "/\(iP.+; CPU .*OS (\d+)[_\d]*.*\) AppleWebKit\//";
+    $matched = [];
+
+    if (preg_match($regex, $userAgent, $matched)) {
+      // Extract digits from first capturing group.
+      $version = (int) $matched[1];
+      return version_compare($version, $major, '<=');
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Detect MacOS version.
+   *
+   * @param int $major The major version to test.
+   * @param int $minor The minor version to test.
+   * @param str $userAgent The User Agent.
+   * @return bool
+   */
+  private static function isMacosxVersion($major, $minor, $userAgent) {
+    $regex = "/\(Macintosh;.*Mac OS X (\d+)_(\d+)[_\d]*.*\) AppleWebKit\//";
+    $matched = [];
+
+    if (preg_match($regex, $userAgent, $matched)) {
+      // Extract digits from first and second capturing groups.
+      return version_compare((int) $matched[1], $major, '=') &&
+             version_compare((int) $matched[2], $minor, '<=');
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Detect MacOS Safari.
+   *
+   * @param str $userAgent The User Agent.
+   * @return bool
+   */
+  private static function isSafari($userAgent) {
+    $regex = "/Version\/.* Safari\//";
+    return preg_match($regex, $userAgent) && !self::isChromiumBased($userAgent);
+  }
+
+  /**
+   * Detect MacOS embedded browser.
+   *
+   * @param str $userAgent The User Agent.
+   * @return FALSE|int
+   */
+  private static function isMacEmbeddedBrowser($userAgent) {
+    $regex = "/^Mozilla\/[\.\d]+ \(Macintosh;.*Mac OS X [_\d]+\) AppleWebKit\/[\.\d]+ \(KHTML, like Gecko\)$/";
+    return preg_match($regex, $userAgent);
+  }
+
+  /**
+   * Detect if browser is Chromium based.
+   *
+   * @param str $userAgent The User Agent.
+   * @return FALSE|int
+   */
+  private static function isChromiumBased($userAgent) {
+    $regex = "/Chrom(e|ium)/";
+    return preg_match($regex, $userAgent);
+  }
+
+  /**
+   * Detect if Chromium version meets requirements.
+   *
+   * @param int $major The major version to test.
+   * @param str $userAgent The User Agent.
+   * @param str $operator
+   * @return bool|int
+   */
+  private static function isChromiumVersionAtLeast($major, $userAgent, $operator) {
+    $regex = "/Chrom[^ \/]+\/(\d+)[\.\d]* /";
+    $matched = [];
+
+    if (preg_match($regex, $userAgent, $matched)) {
+      // Extract digits from first capturing group.
+      $version = (int) $matched[1];
+      return version_compare($version, $major, $operator);
+    }
+    return FALSE;
+  }
+
+  /**
+   * Detect UCBrowser.
+   *
+   * @param str $userAgent The User Agent.
+   * @return FALSE|int
+   */
+  private static function isUcBrowser($userAgent) {
+    $regex = "/UCBrowser\//";
+    return preg_match($regex, $userAgent);
+  }
+
+  /**
+   * Detect if UCBrowser version meets requirements.
+   *
+   * @param int $major The major version to test.
+   * @param int $minor The minor version to test.
+   * @param int $build The build version to test.
+   * @param str $userAgent The User Agent.
+   * @return bool|int
+   */
+  private static function isUcBrowserVersionAtLeast($major, $minor, $build, $userAgent) {
+    $regex = "/UCBrowser\/(\d+)\.(\d+)\.(\d+)[\.\d]* /";
+    $matched = [];
+
+    if (preg_match($regex, $userAgent, $matched)) {
+      // Extract digits from three capturing groups.
+      $majorVersion = (int) $matched[1];
+      $minorVersion = (int) $matched[2];
+      $buildVersion = (int) $matched[3];
+
+      if (version_compare($majorVersion, $major, '>=')) {
+        if (version_compare($minorVersion, $minor, '>=')) {
+          return version_compare($buildVersion, $build, '>=');
+        }
+      }
+    }
+
+    return FALSE;
+  }
+
+}
diff --git a/civicrm/CRM/Utils/System.php b/civicrm/CRM/Utils/System.php
index 74ddd37d93a80fd1406968ac431a2ed02c2e0af4..51ba3572d43fe9c56e08badfbd49ee41aff2d310 100644
--- a/civicrm/CRM/Utils/System.php
+++ b/civicrm/CRM/Utils/System.php
@@ -511,7 +511,7 @@ class CRM_Utils_System {
     }
 
     self::setHttpHeader('Location', $url);
-    self::civiExit();
+    self::civiExit(0, ['url' => $url, 'context' => 'redirect']);
   }
 
   /**
@@ -717,15 +717,12 @@ class CRM_Utils_System {
      * process typically done in CLI and cron scripts. See: CRM-12648
      *
      * Q: Can we move this to the userSystem class so that it can be tuned
-     * per-CMS? For example, when dealing with UnitTests UF, there's no
-     * userFrameworkDSN.
+     * per-CMS? For example, when dealing with UnitTests UF, does it need to
+     * do this session write since the original issue was for Drupal.
      */
     $session = CRM_Core_Session::singleton();
     $session->set('civicrmInitSession', TRUE);
 
-    if ($config->userFrameworkDSN) {
-      $dbDrupal = DB::connect($config->userFrameworkDSN);
-    }
     return $config->userSystem->authenticate($name, $password, $loadCMSBootstrap, $realPath);
   }
 
@@ -1918,4 +1915,11 @@ class CRM_Utils_System {
     $config = CRM_Core_Config::singleton()->userSystem->sendResponse($response);
   }
 
+  /**
+   * Perform any necessary actions prior to redirecting via POST.
+   */
+  public static function prePostRedirect() {
+    CRM_Core_Config::singleton()->userSystem->prePostRedirect();
+  }
+
 }
diff --git a/civicrm/CRM/Utils/System/Backdrop.php b/civicrm/CRM/Utils/System/Backdrop.php
index d46e56146ce87017b36ab0221d8078019f334e11..83b66078cd1c013fc4f5c14e40b2b0cbde544a56 100644
--- a/civicrm/CRM/Utils/System/Backdrop.php
+++ b/civicrm/CRM/Utils/System/Backdrop.php
@@ -291,9 +291,10 @@ class CRM_Utils_System_Backdrop extends CRM_Utils_System_DrupalBase {
   public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
     $config = CRM_Core_Config::singleton();
 
-    $dbBackdrop = DB::connect($config->userFrameworkDSN);
+    $ufDSN = CRM_Utils_SQL::autoSwitchDSN($config->userFrameworkDSN);
+    $dbBackdrop = DB::connect($ufDSN);
     if (DB::isError($dbBackdrop)) {
-      throw new CRM_Core_Exception("Cannot connect to Backdrop database via $config->userFrameworkDSN, " . $dbBackdrop->getMessage());
+      throw new CRM_Core_Exception("Cannot connect to Backdrop database via $ufDSN, " . $dbBackdrop->getMessage());
     }
 
     $account = $userUid = $userMail = NULL;
diff --git a/civicrm/CRM/Utils/System/Base.php b/civicrm/CRM/Utils/System/Base.php
index 5c4f1d58bb14f9de4cb275da75b166b15a45dca3..aabb9aa48b4d2b27e692c7954edbbf7935650220 100644
--- a/civicrm/CRM/Utils/System/Base.php
+++ b/civicrm/CRM/Utils/System/Base.php
@@ -998,4 +998,10 @@ abstract class CRM_Utils_System_Base {
     return FALSE;
   }
 
+  /**
+   * Perform any necessary actions prior to redirecting via POST.
+   */
+  public function prePostRedirect() {
+  }
+
 }
diff --git a/civicrm/CRM/Utils/System/Drupal.php b/civicrm/CRM/Utils/System/Drupal.php
index bf4f8693cf5c72af1dbdeed7f5f3dd6121516c6e..710e1783558521c7d8ba720ed415e24710c80d45 100644
--- a/civicrm/CRM/Utils/System/Drupal.php
+++ b/civicrm/CRM/Utils/System/Drupal.php
@@ -317,9 +317,10 @@ class CRM_Utils_System_Drupal extends CRM_Utils_System_DrupalBase {
 
     $config = CRM_Core_Config::singleton();
 
-    $dbDrupal = DB::connect($config->userFrameworkDSN);
+    $ufDSN = CRM_Utils_SQL::autoSwitchDSN($config->userFrameworkDSN);
+    $dbDrupal = DB::connect($ufDSN);
     if (DB::isError($dbDrupal)) {
-      throw new CRM_Core_Exception("Cannot connect to drupal db via $config->userFrameworkDSN, " . $dbDrupal->getMessage());
+      throw new CRM_Core_Exception("Cannot connect to drupal db via $ufDSN, " . $dbDrupal->getMessage());
     }
 
     $account = $userUid = $userMail = NULL;
diff --git a/civicrm/CRM/Utils/System/Drupal6.php b/civicrm/CRM/Utils/System/Drupal6.php
index 0ac291164ef0e90f220879653340d885bef2f173..343ec7fe74e32b608d01268d4b854ce3eac6dccd 100644
--- a/civicrm/CRM/Utils/System/Drupal6.php
+++ b/civicrm/CRM/Utils/System/Drupal6.php
@@ -300,9 +300,10 @@ class CRM_Utils_System_Drupal6 extends CRM_Utils_System_DrupalBase {
 
     $config = CRM_Core_Config::singleton();
 
-    $dbDrupal = DB::connect($config->userFrameworkDSN);
+    $ufDSN = CRM_Utils_SQL::autoSwitchDSN($config->userFrameworkDSN);
+    $dbDrupal = DB::connect($ufDSN);
     if (DB::isError($dbDrupal)) {
-      throw new CRM_Core_Exception("Cannot connect to drupal db via $config->userFrameworkDSN, " . $dbDrupal->getMessage());
+      throw new CRM_Core_Exception("Cannot connect to drupal db via $ufDSN, " . $dbDrupal->getMessage());
     }
 
     $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
diff --git a/civicrm/CRM/Utils/System/WordPress.php b/civicrm/CRM/Utils/System/WordPress.php
index 5983774cd7b631d6a7c7e08368330df3dd99efed..f13ad3883035fa22d9a7fb56bb4a62b560919673 100644
--- a/civicrm/CRM/Utils/System/WordPress.php
+++ b/civicrm/CRM/Utils/System/WordPress.php
@@ -326,9 +326,9 @@ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base {
     if ($config->userFrameworkFrontend) {
       global $post;
       if (get_option('permalink_structure') != '') {
-        $script = get_permalink($post->ID);
+        $script = $post ? get_permalink($post->ID) : "";
       }
-      if ($config->wpBasePage == $post->post_name) {
+      if ($post && $config->wpBasePage == $post->post_name) {
         $basepage = TRUE;
       }
       // when shortcode is included in page
@@ -1087,4 +1087,161 @@ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base {
     }
   }
 
+  /**
+   * Perform any necessary actions prior to redirecting via POST.
+   *
+   * Redirecting via POST means that cookies need to be sent with SameSite=None.
+   */
+  public function prePostRedirect() {
+    // Get User Agent string.
+    $rawUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
+    $userAgent = mb_convert_encoding($rawUserAgent, 'UTF-8');
+
+    // Bail early if User Agent does not support `SameSite=None`.
+    $shouldUseSameSite = CRM_Utils_SameSite::shouldSendSameSiteNone($userAgent);
+    if (!$shouldUseSameSite) {
+      return;
+    }
+
+    // Make sure session cookie is present in header.
+    $cookie_params = session_name() . '=' . session_id() . '; SameSite=None; Secure';
+    CRM_Utils_System::setHttpHeader('Set-Cookie', $cookie_params);
+
+    // Add WordPress auth cookies when user is logged in.
+    $user = wp_get_current_user();
+    if ($user->exists()) {
+      self::setAuthCookies($user->ID, TRUE, TRUE);
+    }
+  }
+
+  /**
+   * Explicitly set WordPress authentication cookies.
+   *
+   * Chrome 84 introduced a cookie policy change which prevents cookies for the
+   * session and for WordPress user authentication from being indentified when
+   * a purchaser returns to the site from PayPal using the "Back to Merchant"
+   * button.
+   *
+   * In order to comply with this policy, cookies need to be sent with their
+   * "SameSite" attribute set to "None" and with the "Secure" flag set, but this
+   * isn't possible to do via `wp_set_auth_cookie()` as it stands.
+   *
+   * This method is a modified clone of `wp_set_auth_cookie()` which satisfies
+   * the Chrome policy.
+   *
+   * @see wp_set_auth_cookie()
+   *
+   * The $remember parameter increases the time that the cookie will be kept. The
+   * default the cookie is kept without remembering is two days. When $remember is
+   * set, the cookies will be kept for 14 days or two weeks.
+   *
+   * @param int $user_id The WordPress User ID.
+   * @param bool $remember Whether to remember the user.
+   * @param bool|string $secure Whether the auth cookie should only be sent over
+   *                            HTTPS. Default is an empty string which means the
+   *                            value of `is_ssl()` will be used.
+   * @param string $token Optional. User's session token to use for this cookie.
+   */
+  private function setAuthCookies($user_id, $remember = FALSE, $secure = '', $token = '') {
+    if ($remember) {
+      /** This filter is documented in wp-includes/pluggable.php */
+      $expiration = time() + apply_filters('auth_cookie_expiration', 14 * DAY_IN_SECONDS, $user_id, $remember);
+
+      /*
+       * Ensure the browser will continue to send the cookie after the expiration time is reached.
+       * Needed for the login grace period in wp_validate_auth_cookie().
+       */
+      $expire = $expiration + (12 * HOUR_IN_SECONDS);
+    }
+    else {
+      /** This filter is documented in wp-includes/pluggable.php */
+      $expiration = time() + apply_filters('auth_cookie_expiration', 2 * DAY_IN_SECONDS, $user_id, $remember);
+      $expire = 0;
+    }
+
+    if ('' === $secure) {
+      $secure = is_ssl();
+    }
+
+    // Front-end cookie is secure when the auth cookie is secure and the site's home URL is forced HTTPS.
+    $secure_logged_in_cookie = $secure && 'https' === parse_url(get_option('home'), PHP_URL_SCHEME);
+
+    /** This filter is documented in wp-includes/pluggable.php */
+    $secure = apply_filters('secure_auth_cookie', $secure, $user_id);
+
+    /** This filter is documented in wp-includes/pluggable.php */
+    $secure_logged_in_cookie = apply_filters('secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure);
+
+    if ($secure) {
+      $auth_cookie_name = SECURE_AUTH_COOKIE;
+      $scheme = 'secure_auth';
+    }
+    else {
+      $auth_cookie_name = AUTH_COOKIE;
+      $scheme = 'auth';
+    }
+
+    if ('' === $token) {
+      $manager = WP_Session_Tokens::get_instance($user_id);
+      $token = $manager->create($expiration);
+    }
+
+    $auth_cookie = wp_generate_auth_cookie($user_id, $expiration, $scheme, $token);
+    $logged_in_cookie = wp_generate_auth_cookie($user_id, $expiration, 'logged_in', $token);
+
+    /** This filter is documented in wp-includes/pluggable.php */
+    do_action('set_auth_cookie', $auth_cookie, $expire, $expiration, $user_id, $scheme, $token);
+
+    /** This filter is documented in wp-includes/pluggable.php */
+    do_action('set_logged_in_cookie', $logged_in_cookie, $expire, $expiration, $user_id, 'logged_in', $token);
+
+    /** This filter is documented in wp-includes/pluggable.php */
+    if (!apply_filters('send_auth_cookies', TRUE)) {
+      return;
+    }
+
+    $base_options = [
+      'expires' => $expire,
+      'domain' => COOKIE_DOMAIN,
+      'httponly' => TRUE,
+      'samesite' => 'None',
+    ];
+
+    self::setAuthCookie($auth_cookie_name, $auth_cookie, $base_options + ['secure' => $secure, 'path' => PLUGINS_COOKIE_PATH]);
+    self::setAuthCookie($auth_cookie_name, $auth_cookie, $base_options + ['secure' => $secure, 'path' => ADMIN_COOKIE_PATH]);
+    self::setAuthCookie(LOGGED_IN_COOKIE, $logged_in_cookie, $base_options + ['secure' => $secure_logged_in_cookie, 'path' => COOKIEPATH]);
+    if (COOKIEPATH != SITECOOKIEPATH) {
+      self::setAuthCookie(LOGGED_IN_COOKIE, $logged_in_cookie, $base_options + ['secure' => $secure_logged_in_cookie, 'path' => SITECOOKIEPATH]);
+    }
+  }
+
+  /**
+   * Set cookie with "SameSite" flag.
+   *
+   * The method here is compatible with all versions of PHP. Needed because it
+   * is only as of PHP 7.3.0 that the setcookie() method supports the "SameSite"
+   * attribute in its options and will accept "None" as a valid value.
+   *
+   * @param $name The name of the cookie.
+   * @param $value The value of the cookie.
+   * @param array $options The header options for the cookie.
+   */
+  private function setAuthCookie($name, $value, $options) {
+    $header = 'Set-Cookie: ';
+    $header .= rawurlencode($name) . '=' . rawurlencode($value) . '; ';
+    $header .= 'expires=' . gmdate('D, d-M-Y H:i:s T', $options['expires']) . '; ';
+    $header .= 'Max-Age=' . max(0, (int) ($options['expires'] - time())) . '; ';
+    $header .= 'path=' . rawurlencode($options['path']) . '; ';
+    $header .= 'domain=' . rawurlencode($options['domain']) . '; ';
+
+    if (!empty($options['secure'])) {
+      $header .= 'secure; ';
+    }
+    $header .= 'httponly; ';
+    $header .= 'SameSite=' . rawurlencode($options['samesite']);
+
+    header($header, FALSE);
+    $_COOKIE[$name] = $value;
+  }
+
 }
diff --git a/civicrm/CRM/Utils/Token.php b/civicrm/CRM/Utils/Token.php
index d3774f5d68f05d5660e2768c7d21d6478655fc0c..850a2516ac740961965fb3f131888c86b873dee8 100644
--- a/civicrm/CRM/Utils/Token.php
+++ b/civicrm/CRM/Utils/Token.php
@@ -1806,8 +1806,10 @@ class CRM_Utils_Token {
         break;
 
       case 'receive_date':
+      case 'receipt_date':
         $value = CRM_Utils_Array::retrieveValueRecursive($contribution, $token);
-        $value = CRM_Utils_Date::customFormat($value, NULL, ['j', 'm', 'Y']);
+        $config = CRM_Core_Config::singleton();
+        $value = CRM_Utils_Date::customFormat($value, $config->dateformatDatetime);
         break;
 
       default:
diff --git a/civicrm/CRM/Utils/VersionCheck.php b/civicrm/CRM/Utils/VersionCheck.php
index 0b3b9fa4607c11d8d1f643cbdfc4145ccd99d223..fbb05a1929c9cc1efad57f47d06c2bea49279082 100644
--- a/civicrm/CRM/Utils/VersionCheck.php
+++ b/civicrm/CRM/Utils/VersionCheck.php
@@ -74,9 +74,10 @@ class CRM_Utils_VersionCheck {
   /**
    * Self-populates version info
    *
-   * @throws \Exception
+   * @param bool $force
+   * @throws Exception
    */
-  public function initialize() {
+  public function initialize($force = FALSE) {
     $this->getJob();
 
     // Populate remote $versionInfo from cache file
@@ -84,9 +85,9 @@ class CRM_Utils_VersionCheck {
 
     // Fallback if scheduled job is enabled but has failed to run.
     $expiryTime = time() - self::CACHEFILE_EXPIRE;
-    if (!empty($this->cronJob['is_active']) &&
+    if ($force || (!empty($this->cronJob['is_active']) &&
       (!$this->isInfoAvailable || filemtime($this->cacheFile) < $expiryTime)
-    ) {
+    )) {
       // First try updating the files modification time, for 2 reasons:
       //  - if the file is not writeable, this saves the trouble of pinging back
       //  - if the remote server is down, this will prevent an immediate retry
diff --git a/civicrm/Civi/Api4/Generic/AbstractQueryAction.php b/civicrm/Civi/Api4/Generic/AbstractQueryAction.php
index ed7ac9bf374d88e99bf4c2df09bee9c0b087d325..85ebcfb5edd58447d268a8bbf6b258692e3054a1 100644
--- a/civicrm/Civi/Api4/Generic/AbstractQueryAction.php
+++ b/civicrm/Civi/Api4/Generic/AbstractQueryAction.php
@@ -19,6 +19,8 @@
 
 namespace Civi\Api4\Generic;
 
+use Civi\Api4\Utils\CoreUtil;
+
 /**
  * Base class for all actions that need to fetch records (`Get`, `Update`, `Delete`, etc.).
  *
@@ -86,7 +88,7 @@ abstract class AbstractQueryAction extends AbstractAction {
    * @throws \API_Exception
    */
   public function addWhere(string $fieldName, string $op, $value = NULL) {
-    if (!in_array($op, \CRM_Core_DAO::acceptedSQLOperators())) {
+    if (!in_array($op, CoreUtil::getOperators())) {
       throw new \API_Exception('Unsupported operator');
     }
     $this->where[] = [$fieldName, $op, $value];
@@ -145,7 +147,7 @@ abstract class AbstractQueryAction extends AbstractAction {
       }
       return $output . '(' . $this->whereClauseToString($whereClause, $op) . ')';
     }
-    elseif (isset($whereClause[1]) && in_array($whereClause[1], \CRM_Core_DAO::acceptedSQLOperators())) {
+    elseif (isset($whereClause[1]) && in_array($whereClause[1], CoreUtil::getOperators())) {
       $output = $whereClause[0] . ' ' . $whereClause[1] . ' ';
       if (isset($whereClause[2])) {
         $output .= is_array($whereClause[2]) ? '[' . implode(', ', $whereClause[2]) . ']' : $whereClause[2];
diff --git a/civicrm/Civi/Api4/Generic/BasicGetFieldsAction.php b/civicrm/Civi/Api4/Generic/BasicGetFieldsAction.php
index ec58714fe46dc52f43d5ca2a2091d3966052f3b0..5f5d25ef3ded38c2c1ee0edcc212aeb6f4173030 100644
--- a/civicrm/Civi/Api4/Generic/BasicGetFieldsAction.php
+++ b/civicrm/Civi/Api4/Generic/BasicGetFieldsAction.php
@@ -129,6 +129,7 @@ class BasicGetFieldsAction extends BasicGetAction {
         'data_type' => \CRM_Utils_Array::value('type', $field, 'String'),
       ], array_flip($fields));
       $field += $defaults;
+      $field['label'] = $field['label'] ?? $field['title'];
       if (isset($defaults['options'])) {
         $field['options'] = $this->formatOptionList($field['options']);
       }
@@ -217,14 +218,22 @@ class BasicGetFieldsAction extends BasicGetAction {
       [
         'name' => 'name',
         'data_type' => 'String',
+        'description' => ts('Unique field identifier'),
       ],
       [
         'name' => 'title',
         'data_type' => 'String',
+        'description' => ts('Technical name of field, shown in API and exports'),
+      ],
+      [
+        'name' => 'label',
+        'data_type' => 'String',
+        'description' => ts('User-facing label, shown on most forms and displays'),
       ],
       [
         'name' => 'description',
         'data_type' => 'String',
+        'description' => ts('Explanation of the purpose of the field'),
       ],
       [
         'name' => 'default_value',
diff --git a/civicrm/Civi/Api4/Generic/DAOGetAction.php b/civicrm/Civi/Api4/Generic/DAOGetAction.php
index 9ad1d54adc352ba4c9d2ccbdc7450ed0eb6c084c..852a333ccff065f41154e11965c0b89103a8644e 100644
--- a/civicrm/Civi/Api4/Generic/DAOGetAction.php
+++ b/civicrm/Civi/Api4/Generic/DAOGetAction.php
@@ -20,6 +20,7 @@
 namespace Civi\Api4\Generic;
 
 use Civi\Api4\Query\Api4SelectQuery;
+use Civi\Api4\Utils\CoreUtil;
 
 /**
  * Retrieve $ENTITIES based on criteria specified in the `where` parameter.
@@ -85,6 +86,13 @@ class DAOGetAction extends AbstractGetAction {
   protected $having = [];
 
   public function _run(Result $result) {
+    // Early return if table doesn't exist yet due to pending upgrade
+    $baoName = $this->getBaoName();
+    if (!$baoName::tableHasBeenAdded()) {
+      \Civi::log()->warning("Could not read from {$this->getEntityName()} before table has been added. Upgrade required.", ['civi.tag' => 'upgrade_needed']);
+      return;
+    }
+
     $this->setDefaultWhereClause();
     $this->expandSelectClauseWildcards();
     $this->getObjects($result);
@@ -147,7 +155,7 @@ class DAOGetAction extends AbstractGetAction {
    * @throws \API_Exception
    */
   public function addHaving(string $expr, string $op, $value = NULL) {
-    if (!in_array($op, \CRM_Core_DAO::acceptedSQLOperators())) {
+    if (!in_array($op, CoreUtil::getOperators())) {
       throw new \API_Exception('Unsupported operator');
     }
     $this->having[] = [$expr, $op, $value];
diff --git a/civicrm/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php b/civicrm/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php
index 4ae6096c503965784deff942f29420d785cf50a0..fd515b9a4b712a71b0076538840fa55f6c5c825e 100644
--- a/civicrm/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php
+++ b/civicrm/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php
@@ -158,6 +158,15 @@ trait ArrayQueryActionTrait {
       case 'NOT IN':
         return !in_array($value, $expected);
 
+      case 'CONTAINS':
+        if (is_array($value)) {
+          return in_array($expected, $value);
+        }
+        elseif (is_string($value) || is_numeric($value)) {
+          return strpos((string) $value, (string) $expected) !== FALSE;
+        }
+        return $value == $expected;
+
       default:
         throw new NotImplementedException("Unsupported operator: '$operator' cannot be used with array data");
     }
diff --git a/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php b/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php
index 78065d55f355ef47c40a163d93690a6ae2f77504..2fed2b7f467c08845ddc67fbb9135ce089c522b6 100644
--- a/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php
+++ b/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php
@@ -52,9 +52,8 @@ trait CustomValueActionTrait {
    * @inheritDoc
    */
   protected function writeObjects($items) {
-    $result = [];
     $fields = $this->entityFields();
-    foreach ($items as $item) {
+    foreach ($items as $idx => $item) {
       FormattingUtil::formatWriteParams($item, $fields);
 
       // Convert field names to custom_xx format
@@ -65,9 +64,16 @@ trait CustomValueActionTrait {
         }
       }
 
-      $result[] = \CRM_Core_BAO_CustomValueTable::setValues($item);
+      \CRM_Core_BAO_CustomValueTable::setValues($item);
+
+      // Darn setValues function doesn't return an id.
+      if (empty($item['id'])) {
+        $tableName = CoreUtil::getTableName($this->getEntityName());
+        $items[$idx]['id'] = (int) \CRM_Core_DAO::singleValueQuery('SELECT MAX(id) FROM ' . $tableName);
+      }
     }
-    return $result;
+    FormattingUtil::formatOutputValues($items, $this->entityFields(), $this->getEntityName(), 'create');
+    return $items;
   }
 
   /**
@@ -106,4 +112,11 @@ trait CustomValueActionTrait {
     return $this->customGroup;
   }
 
+  /**
+   * @return \CRM_Core_DAO|string
+   */
+  protected function getBaoName() {
+    return \CRM_Core_BAO_CustomValue::class;
+  }
+
 }
diff --git a/civicrm/Civi/Api4/Query/Api4SelectQuery.php b/civicrm/Civi/Api4/Query/Api4SelectQuery.php
index 5721e919688e1207f882aedd1da5ea7469c49a83..e4f8ceb187fcd6d37715c9b92cbb2cee4d403d77 100644
--- a/civicrm/Civi/Api4/Query/Api4SelectQuery.php
+++ b/civicrm/Civi/Api4/Query/Api4SelectQuery.php
@@ -26,8 +26,8 @@ use Civi\Api4\Utils\SelectUtil;
  * Leaf operators are one of:
  *
  * * '=', '<=', '>=', '>', '<', 'LIKE', "<>", "!=",
- * * "NOT LIKE", 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN',
- * * 'IS NOT NULL', or 'IS NULL'.
+ * * 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN',
+ * * 'IS NOT NULL', or 'IS NULL', 'CONTAINS'.
  */
 class Api4SelectQuery {
 
@@ -362,7 +362,7 @@ class Api4SelectQuery {
   protected function composeClause(array $clause, string $type) {
     // Pad array for unary operators
     list($expr, $operator, $value) = array_pad($clause, 3, NULL);
-    if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators(), TRUE)) {
+    if (!in_array($operator, CoreUtil::getOperators(), TRUE)) {
       throw new \API_Exception('Illegal operator');
     }
 
@@ -379,7 +379,8 @@ class Api4SelectQuery {
         $fieldAlias = $expr;
         // Attempt to format if this is a real field
         if (isset($this->apiFieldSpec[$expr])) {
-          FormattingUtil::formatInputValue($value, $expr, $this->apiFieldSpec[$expr]);
+          $field = $this->getField($expr);
+          FormattingUtil::formatInputValue($value, $expr, $field);
         }
       }
       // Expr references a non-field expression like a function; convert to alias
@@ -392,7 +393,8 @@ class Api4SelectQuery {
         foreach ($this->selectAliases as $selectAlias => $selectExpr) {
           list($selectField) = explode(':', $selectAlias);
           if ($selectAlias === $selectExpr && $fieldName === $selectField && isset($this->apiFieldSpec[$fieldName])) {
-            FormattingUtil::formatInputValue($value, $expr, $this->apiFieldSpec[$fieldName]);
+            $field = $this->getField($fieldName);
+            FormattingUtil::formatInputValue($value, $expr, $field);
             $fieldAlias = $selectAlias;
             break;
           }
@@ -415,7 +417,29 @@ class Api4SelectQuery {
         return sprintf('%s %s %s', $fieldAlias, $operator, $valExpr->render($this->apiFieldSpec));
       }
       elseif ($fieldName) {
-        FormattingUtil::formatInputValue($value, $fieldName, $this->apiFieldSpec[$fieldName]);
+        $field = $this->getField($fieldName);
+        FormattingUtil::formatInputValue($value, $fieldName, $field);
+      }
+    }
+
+    if ($operator === 'CONTAINS') {
+      switch ($field['serialize'] ?? NULL) {
+        case \CRM_Core_DAO::SERIALIZE_JSON:
+          $operator = 'LIKE';
+          $value = '%"' . $value . '"%';
+          // FIXME: Use this instead of the above hack once MIN_INSTALL_MYSQL_VER is bumped to 5.7.
+          // return sprintf('JSON_SEARCH(%s, "one", "%s") IS NOT NULL', $fieldAlias, \CRM_Core_DAO::escapeString($value));
+          break;
+
+        case \CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND:
+          $operator = 'LIKE';
+          $value = '%' . \CRM_Core_DAO::VALUE_SEPARATOR . $value . \CRM_Core_DAO::VALUE_SEPARATOR . '%';
+          break;
+
+        default:
+          $operator = 'LIKE';
+          $value = '%' . $value . '%';
+          break;
       }
     }
 
diff --git a/civicrm/Civi/Api4/Service/Spec/FieldSpec.php b/civicrm/Civi/Api4/Service/Spec/FieldSpec.php
index 8b9ba5ca2405e0924c6ae085e7c1c72ee6cb372e..eb35cf1d30822bb7069a2c5aaa38e6cb118541d3 100644
--- a/civicrm/Civi/Api4/Service/Spec/FieldSpec.php
+++ b/civicrm/Civi/Api4/Service/Spec/FieldSpec.php
@@ -32,6 +32,11 @@ class FieldSpec {
    */
   protected $name;
 
+  /**
+   * @var string
+   */
+  protected $label;
+
   /**
    * @var string
    */
@@ -165,6 +170,24 @@ class FieldSpec {
     return $this;
   }
 
+  /**
+   * @return string
+   */
+  public function getLabel() {
+    return $this->label;
+  }
+
+  /**
+   * @param string $label
+   *
+   * @return $this
+   */
+  public function setLabel($label) {
+    $this->label = $label;
+
+    return $this;
+  }
+
   /**
    * @return string
    */
diff --git a/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php b/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php
index 1da26d1520a984492b829f9ad8da81a70e3a1ec2..23209a4e242cb5b063f8196c6f0f9aa9146614dc 100644
--- a/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php
+++ b/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php
@@ -73,6 +73,7 @@ class SpecFormatter {
       $field = new FieldSpec($name, $entity, $dataTypeName);
       $field->setRequired(!empty($data['required']));
       $field->setTitle($data['title'] ?? NULL);
+      $field->setLabel($data['html']['label'] ?? NULL);
       $field->setOptions(!empty($data['pseudoconstant']));
     }
     $field->setSerialize($data['serialize'] ?? NULL);
diff --git a/civicrm/Civi/Api4/Utils/CoreUtil.php b/civicrm/Civi/Api4/Utils/CoreUtil.php
index 8d06ce96789b9ec4d4d31286eb7c47a21144ead1..b9090c97dec26daaa80e33a57d09cfd42da2ad7d 100644
--- a/civicrm/Civi/Api4/Utils/CoreUtil.php
+++ b/civicrm/Civi/Api4/Utils/CoreUtil.php
@@ -71,4 +71,13 @@ class CoreUtil {
     return $entityName;
   }
 
+  /**
+   * @return string[]
+   */
+  public static function getOperators() {
+    $operators = \CRM_Core_DAO::acceptedSQLOperators();
+    $operators[] = 'CONTAINS';
+    return $operators;
+  }
+
 }
diff --git a/civicrm/Civi/Install/Requirements.php b/civicrm/Civi/Install/Requirements.php
index da7b2cc76597194238bff22058288b01ed11c268..7d30855076b79c0380a1d6746f7deece5ce0e2b0 100644
--- a/civicrm/Civi/Install/Requirements.php
+++ b/civicrm/Civi/Install/Requirements.php
@@ -132,7 +132,24 @@ class Requirements {
     elseif (!empty($db_config['server'])) {
       $host = $db_config['server'];
     }
-    $conn = @mysqli_connect($host, $db_config['username'], $db_config['password'], $db_config['database'], !empty($db_config['port']) ? $db_config['port'] : NULL);
+    if (empty($db_config['ssl_params'])) {
+      $conn = @mysqli_connect($host, $db_config['username'], $db_config['password'], $db_config['database'], !empty($db_config['port']) ? $db_config['port'] : NULL);
+    }
+    else {
+      $conn = NULL;
+      $init = mysqli_init();
+      mysqli_ssl_set(
+        $init,
+        $db_config['ssl_params']['key'] ?? NULL,
+        $db_config['ssl_params']['cert'] ?? NULL,
+        $db_config['ssl_params']['ca'] ?? NULL,
+        $db_config['ssl_params']['capath'] ?? NULL,
+        $db_config['ssl_params']['cipher'] ?? NULL
+      );
+      if (@mysqli_real_connect($init, $host, $db_config['username'], $db_config['password'], $db_config['database'], (!empty($db_config['port']) ? $db_config['port'] : NULL), NULL, MYSQLI_CLIENT_SSL)) {
+        $conn = $init;
+      }
+    }
     return $conn;
   }
 
diff --git a/civicrm/Civi/Test.php b/civicrm/Civi/Test.php
index 1d9db12079521969e442e86d704ae96fc8fd90ff..cf9c841ab7981ccd8fdab426209df134aaf77d4e 100644
--- a/civicrm/Civi/Test.php
+++ b/civicrm/Civi/Test.php
@@ -59,7 +59,8 @@ class Test {
   public static function dsn($part = NULL) {
     if (!isset(self::$singletons['dsn'])) {
       require_once "DB.php";
-      self::$singletons['dsn'] = \DB::parseDSN(CIVICRM_DSN);
+      $dsn = \CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN);
+      self::$singletons['dsn'] = \DB::parseDSN($dsn);
     }
 
     if ($part === NULL) {
diff --git a/civicrm/ang/api4Explorer/Explorer.js b/civicrm/ang/api4Explorer/Explorer.js
index 0dca04e3bc463cacd76b7bbb8280a0dd5ddb7a74..41750691c61c750370d9a06e2caa3b461a386f6f 100644
--- a/civicrm/ang/api4Explorer/Explorer.js
+++ b/civicrm/ang/api4Explorer/Explorer.js
@@ -72,7 +72,9 @@
         {name: 'ang2', label: ts('Batch Calls'), code: ''}
       ],
       cli: [
-        {name: 'cv', label: ts('CV'), code: ''}
+        {name: 'short', label: ts('CV (short)'), code: ''},
+        {name: 'long', label: ts('CV (long)'), code: ''},
+        {name: 'pipe', label: ts('CV (pipe)'), code: ''}
       ]
     };
 
@@ -324,7 +326,11 @@
       }
       // Then lookup implicit links
       _.each(path, function(node) {
-        entity = _.find(links[entity], {alias: node}).entity;
+        var link = _.find(links[entity], {alias: node});
+        if (!link) {
+          return false;
+        }
+        entity = link.entity;
       });
       return entity;
     }
@@ -654,8 +660,44 @@
             break;
 
           case 'cli':
-            // Write cli code
-            code.cv = 'cv api4 ' + entity + '.' + action + " '" + stringify(params) + "'";
+            // Cli code using json input
+            code.long = 'cv api4 ' + entity + '.' + action + ' ' + cliFormat(JSON.stringify(params));
+            code.pipe = 'echo ' + cliFormat(JSON.stringify(params)) + ' | cv api4 ' + entity + '.' + action + ' --in=json';
+
+            // Cli code using short syntax
+            code.short = 'cv api4 ' + entity + '.' + action;
+            var limitSet = false;
+            _.each(params, function(param, key) {
+              switch (true) {
+                case (key === 'select' && !_.includes(param.join(), ' ')):
+                  code.short += ' +s ' + cliFormat(param.join(','));
+                  break;
+                case (key === 'where' && !_.intersection(_.map(param, 0), ['AND', 'OR', 'NOT']).length):
+                  _.each(param, function(clause) {
+                    code.short += ' +w ' + cliFormat(clause[0] + ' ' + clause[1] + (clause.length > 2 ? (' ' + JSON.stringify(clause[2])) : ''));
+                  });
+                  break;
+                case (key === 'orderBy'):
+                  _.each(param, function(dir, field) {
+                    code.short += ' +o ' + cliFormat(field + ' ' + dir);
+                  });
+                  break;
+                case (key === 'values'):
+                  _.each(param, function(val, field) {
+                    code.short += ' +v ' + cliFormat(field + '=' + val);
+                  });
+                  break;
+                case (key === 'limit' || key === 'offset'):
+                  // These 2 get combined
+                  if (!limitSet) {
+                    limitSet = true;
+                    code.short += ' +l ' + (params.limit || '0') + (params.offset ? ('@' + params.offset) : '');
+                  }
+                  break;
+                default:
+                  code.short += ' ' + key + '=' + (typeof param === 'string' ? cliFormat(param) : cliFormat(JSON.stringify(param)));
+              }
+            });
         }
       }
       _.each($scope.code, function(vals) {
@@ -801,6 +843,20 @@
       return JSON.stringify(val).replace(/\$/g, '\\$');
     }
 
+    // Format string to be cli-input-safe
+    function cliFormat(str) {
+      if (!_.includes(str, ' ') && !_.includes(str, '"') && !_.includes(str, "'")) {
+        return str;
+      }
+      if (!_.includes(str, "'")) {
+        return "'" + str + "'";
+      }
+      if (!_.includes(str, '"')) {
+        return '"' + str + '"';
+      }
+      return "'" + str.replace(/'/g, "\\'") + "'";
+    }
+
     function fetchMeta() {
       crmApi4(getMetaParams)
         .then(function(data) {
diff --git a/civicrm/ang/crmMailingAB/services.js b/civicrm/ang/crmMailingAB/services.js
index 2e9fa9260bd0233ee78dcfe40aef114c00e51f3d..a1d64b7da68928db9e69643a32f51a905b6093c0 100644
--- a/civicrm/ang/crmMailingAB/services.js
+++ b/civicrm/ang/crmMailingAB/services.js
@@ -69,7 +69,6 @@
             mailing_id_a: null,
             mailing_id_b: null,
             mailing_id_c: null,
-            domain_id: null,
             testing_criteria: 'subject',
             winner_criteria: null,
             specific_url: '',
diff --git a/civicrm/api/class.api.php b/civicrm/api/class.api.php
index 4ba61dd30bde6c5d50e8e25d839a895f07d596fb..df6ef1283f4c0c4149987fcc73cec06f868743b3 100644
--- a/civicrm/api/class.api.php
+++ b/civicrm/api/class.api.php
@@ -14,15 +14,15 @@
  * ```
  *   require_once('/your/civi/folder/api/class.api.php');
  *   // the path to civicrm.settings.php
- *   $api = new civicrm_api3 (array('conf_path'=> '/your/path/to/your/civicrm/or/joomla/site));
+ *   $api = new civicrm_api3 (['conf_path'=> '/your/path/to/your/civicrm/or/joomla/site']);
  * ```
  *
  * or to query a remote server via the rest api
  *
  * ```
- *   $api = new civicrm_api3 (array ('server' => 'http://example.org',
- *                                   'api_key'=>'theusersecretkey',
- *                                   'key'=>'thesitesecretkey'));
+ *   $api = new civicrm_api3 (['server' => 'http://example.org',
+ *                             'api_key'=>'theusersecretkey',
+ *                             'key'=>'thesitesecretkey']);
  * ```
  *
  * No matter how initialised and if civicrm is local or remote, you use the class the same way.
@@ -34,7 +34,7 @@
  * So, to get the individual contacts:
  *
  * ```
- *   if ($api->Contact->Get(array('contact_type'=>'Individual','return'=>'sort_name,current_employer')) {
+ *   if ($api->Contact->Get(['contact_type'=>'Individual','return'=>'sort_name,current_employer']) {
  *     // each key of the result array is an attribute of the api
  *     echo "\n contacts found " . $api->count;
  *     foreach ($api->values as $c) {
@@ -49,7 +49,7 @@
  * Or, to create an event:
  *
  * ```
- *   if ($api->Event->Create(array('title'=>'Test','event_type_id' => 1,'is_public' => 1,'start_date' => 19430429))) {
+ *   if ($api->Event->Create(['title'=>'Test','event_type_id' => 1,'is_public' => 1,'start_date' => 19430429])) {
  *     echo "created event id:". $api->id;
  *   } else {
  *     echo $api->errorMsg();
@@ -62,7 +62,7 @@
  *
  * ```
  *   $api->Activity->Get (42);
- *   $api->Activity->Get (array('id'=>42));
+ *   $api->Activity->Get (['id'=>42]);
  * ```
  *
  *
@@ -87,23 +87,23 @@ class civicrm_api3 {
     $this->local      = TRUE;
     $this->input      = [];
     $this->lastResult = [];
-    if (isset($config) && isset($config['server'])) {
+    if (!empty($config) && !empty($config['server'])) {
       // we are calling a remote server via REST
       $this->local = FALSE;
       $this->uri = $config['server'];
-      if (isset($config['path'])) {
+      if (!empty($config['path'])) {
         $this->uri .= "/" . $config['path'];
       }
       else {
         $this->uri .= '/sites/all/modules/civicrm/extern/rest.php';
       }
-      if (isset($config['key'])) {
+      if (!empty($config['key'])) {
         $this->key = $config['key'];
       }
       else {
         die("\nFATAL:param['key] missing\n");
       }
-      if (isset($config['api_key'])) {
+      if (!empty($config['api_key'])) {
         $this->api_key = $config['api_key'];
       }
       else {
@@ -111,7 +111,7 @@ class civicrm_api3 {
       }
       return;
     }
-    if (isset($config) && isset($config['conf_path'])) {
+    if (!empty($config) && !empty($config['conf_path'])) {
       if (!defined('CIVICRM_SETTINGS_PATH')) {
         define('CIVICRM_SETTINGS_PATH', $config['conf_path'] . '/civicrm.settings.php');
       }
diff --git a/civicrm/api/v3/Contribution.php b/civicrm/api/v3/Contribution.php
index 298980b4a74acced636044863de2b1cc57d0d902..1e70afa7ecbf040adebe95d88a3caed218f5dc82 100644
--- a/civicrm/api/v3/Contribution.php
+++ b/civicrm/api/v3/Contribution.php
@@ -32,7 +32,7 @@ function civicrm_api3_contribution_create($params) {
   // The BAO should not clean money - it should be done in the form layer & api wrapper
   // (although arguably the api should expect pre-cleaned it seems to do some cleaning.)
   if (empty($params['skipCleanMoney'])) {
-    foreach (['total_amount', 'net_amount', 'fee_amount'] as $field) {
+    foreach (['total_amount', 'net_amount', 'fee_amount', 'non_deductible_amount'] as $field) {
       if (isset($params[$field])) {
         $params[$field] = CRM_Utils_Rule::cleanMoney($params[$field]);
       }
@@ -602,7 +602,6 @@ function civicrm_api3_contribution_repeattransaction($params) {
     );
   }
 
-  $original_contribution = clone $contribution;
   $input['payment_processor_id'] = civicrm_api3('contributionRecur', 'getvalue', [
     'return' => 'payment_processor_id',
     'id' => $contribution->contribution_recur_id,
@@ -626,7 +625,7 @@ function civicrm_api3_contribution_repeattransaction($params) {
     ];
     $input = array_intersect_key($params, array_fill_keys($passThroughParams, NULL));
 
-    return _ipn_process_transaction($params, $contribution, $input, $ids, $original_contribution);
+    return _ipn_process_transaction($params, $contribution, $input, $ids);
   }
   catch (Exception $e) {
     throw new API_Exception('failed to load related objects' . $e->getMessage() . "\n" . $e->getTraceAsString());
@@ -645,19 +644,13 @@ function civicrm_api3_contribution_repeattransaction($params) {
  *
  * @param array $ids
  *
- * @param CRM_Contribute_BAO_Contribution $firstContribution
- *
  * @return mixed
  * @throws \CRM_Core_Exception
  * @throws \CiviCRM_API3_Exception
  */
-function _ipn_process_transaction(&$params, $contribution, $input, $ids, $firstContribution = NULL) {
+function _ipn_process_transaction($params, $contribution, $input, $ids) {
   $objects = $contribution->_relatedObjects;
   $objects['contribution'] = &$contribution;
-
-  if ($firstContribution) {
-    $objects['first_contribution'] = $firstContribution;
-  }
   $input['component'] = $contribution->_component;
   $input['is_test'] = $contribution->is_test;
   $input['amount'] = empty($input['total_amount']) ? $contribution->total_amount : $input['total_amount'];
@@ -682,7 +675,14 @@ function _ipn_process_transaction(&$params, $contribution, $input, $ids, $firstC
   }
   $input['card_type_id'] = $params['card_type_id'] ?? NULL;
   $input['pan_truncation'] = $params['pan_truncation'] ?? NULL;
-  return CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects,
+  if (!empty($params['payment_instrument_id'])) {
+    $input['payment_instrument_id'] = $params['payment_instrument_id'];
+  }
+  return CRM_Contribute_BAO_Contribution::completeOrder($input, [
+    'related_contact' => $ids['related_contact'] ?? NULL,
+    'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL,
+    'contributionRecur' => !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL,
+  ], $objects,
     $params['is_post_payment_create'] ?? NULL);
 }
 
diff --git a/civicrm/api/v3/LineItem.php b/civicrm/api/v3/LineItem.php
index a5e5f34ff37dd76ab19b455e36b65ed1ef6ad606..6e3690388fea25950caca5a77483c31ad8b11c29 100644
--- a/civicrm/api/v3/LineItem.php
+++ b/civicrm/api/v3/LineItem.php
@@ -29,9 +29,13 @@
  */
 function civicrm_api3_line_item_create($params) {
   // @todo the following line is not really appropriate for the api. The BAO should
-  // do the work, and it should be in a tighter function. The below function is  not really
-  // readable because it is handling contribution and line item together.
-  $params = CRM_Contribute_BAO_Contribution::checkTaxAmount($params, TRUE);
+  // do the work.
+  $taxRates = CRM_Core_PseudoConstant::getTaxRates();
+  if (isset($params['financial_type_id']) && array_key_exists($params['financial_type_id'], $taxRates)) {
+    $taxRate = $taxRates[$params['financial_type_id']];
+    $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($params['line_total'], $taxRate);
+    $params['tax_amount'] = round($taxAmount['tax_amount'], 2);
+  }
   return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params, 'LineItem');
 }
 
@@ -62,9 +66,6 @@ function _civicrm_api3_line_item_create_spec(&$params) {
  *   Array of matching line_items
  */
 function civicrm_api3_line_item_get($params) {
-  if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() && !empty($params['check_permissions'])) {
-    CRM_Price_BAO_LineItem::getAPILineItemParams($params);
-  }
   return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params);
 }
 
@@ -75,18 +76,11 @@ function civicrm_api3_line_item_get($params) {
  *
  * @param array $params
  *   Array containing id of the group to be deleted.
+ *
  * @return array API result array
  * @throws API_Exception
+ * @throws \CiviCRM_API3_Exception
  */
 function civicrm_api3_line_item_delete($params) {
-  if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() && !empty($params['check_permissions'])) {
-    CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types, CRM_Core_Action::DELETE);
-    if (empty($params['financial_type_id'])) {
-      $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_LineItem', $params['id'], 'financial_type_id');
-    }
-    if (!in_array($params['financial_type_id'], array_keys($types))) {
-      throw new API_Exception('You do not have permission to delete this line item');
-    }
-  }
   return _civicrm_api3_basic_delete(_civicrm_api3_get_BAO(__FUNCTION__), $params);
 }
diff --git a/civicrm/api/v3/MailingEventSubscribe.php b/civicrm/api/v3/MailingEventSubscribe.php
index 92a8795bdb20f3fd0ef60257ce411faa3466d17b..7046fe166efc5679c5880685ac65e3d77792a171 100644
--- a/civicrm/api/v3/MailingEventSubscribe.php
+++ b/civicrm/api/v3/MailingEventSubscribe.php
@@ -66,12 +66,12 @@ function civicrm_api3_mailing_event_subscribe_create($params) {
 function _civicrm_api3_mailing_event_subscribe_create_spec(&$params) {
   $params['email'] = [
     'api.required' => 1,
-    'title' => 'Unsubscribe Email',
+    'title' => 'Subscribe Email',
     'type' => CRM_Utils_Type::T_STRING,
   ];
   $params['group_id'] = [
     'api.required' => 1,
-    'title' => 'Unsubscribe From Group',
+    'title' => 'Subscribe To Group',
     'type' => CRM_Utils_Type::T_INT,
   ];
 }
diff --git a/civicrm/api/v3/MessageTemplate.php b/civicrm/api/v3/MessageTemplate.php
index 8f33ce88c59578bd0f0ed0c3cdbb9d1651642953..b75e43df5b32a01d141d806275e8694303eb85e0 100644
--- a/civicrm/api/v3/MessageTemplate.php
+++ b/civicrm/api/v3/MessageTemplate.php
@@ -181,4 +181,9 @@ function _civicrm_api3_message_template_send_spec(&$params) {
   $params['pdf_filename']['title'] = 'PDF Filename';
   $params['pdf_filename']['api.aliases'] = ['PDFFilename'];
   $params['pdf_filename']['type'] = CRM_Utils_Type::T_STRING;
+
+  $params['disable_smarty']['description'] = 'Disable Smarty. Normal CiviMail tokens are still supported. By default Smarty is enabled.';
+  $params['disable_smarty']['title'] = 'Disable Smarty';
+  $params['disable_smarty']['api.aliases'] = ['disableSmarty'];
+  $params['disable_smarty']['type'] = CRM_Utils_Type::T_BOOLEAN;
 }
diff --git a/civicrm/api/v3/Order.php b/civicrm/api/v3/Order.php
index 32d59a193954a62755d3afd6d7ba9d66a8834a97..5e9c07b81bac23ab7d71272d703db6a4b96a1aa6 100644
--- a/civicrm/api/v3/Order.php
+++ b/civicrm/api/v3/Order.php
@@ -83,7 +83,7 @@ function civicrm_api3_order_create($params) {
     $priceSetID = NULL;
     CRM_Contribute_BAO_Contribution::checkLineItems($params);
     foreach ($params['line_items'] as $lineItems) {
-      $entityParams = CRM_Utils_Array::value('params', $lineItems, []);
+      $entityParams = $lineItems['params'] ?? [];
       if (!empty($entityParams) && !empty($lineItems['line_item'])) {
         $item = reset($lineItems['line_item']);
         $entity = str_replace('civicrm_', '', $item['entity_table']);
@@ -105,7 +105,7 @@ function civicrm_api3_order_create($params) {
       }
       if (empty($priceSetID)) {
         $item = reset($lineItems['line_item']);
-        $priceSetID = civicrm_api3('PriceField', 'getvalue', [
+        $priceSetID = (int) civicrm_api3('PriceField', 'getvalue', [
           'return' => 'price_set_id',
           'id' => $item['price_field_id'],
         ]);
@@ -142,10 +142,10 @@ function civicrm_api3_order_create($params) {
       elseif ($entity == 'membership') {
         $paymentParams['isSkipLineItem'] = TRUE;
       }
-      $payments = civicrm_api3($entity . '_payment', 'create', $paymentParams);
+      civicrm_api3($entity . '_payment', 'create', $paymentParams);
     }
   }
-  return civicrm_api3_create_success(CRM_Utils_Array::value('values', $contribution), $params, 'Order', 'create');
+  return civicrm_api3_create_success($contribution['values'] ?? [], $params, 'Order', 'create');
 }
 
 /**
diff --git a/civicrm/api/v3/System.php b/civicrm/api/v3/System.php
index b597c48793fc304f27e46d6e0c7481445e4c2c17..c207ac9e13e58de51d5c29d9c3ca4a451c530b2e 100644
--- a/civicrm/api/v3/System.php
+++ b/civicrm/api/v3/System.php
@@ -397,7 +397,14 @@ function civicrm_api3_system_updatelogtables($params) {
  * @throws \API_Exception
  */
 function civicrm_api3_system_utf8conversion($params) {
-  if (CRM_Core_BAO_SchemaHandler::migrateUtf8mb4($params['is_revert'])) {
+  $params['patterns'] = explode(',', $params['patterns']);
+  $params['databases'] = empty($params['databases']) ? NULL : explode(',', $params['databases']);
+  if (CRM_Core_BAO_SchemaHandler::migrateUtf8mb4(
+    $params['is_revert'],
+    $params['patterns'],
+    $params['databases']
+    )
+  ) {
     return civicrm_api3_create_success(1);
   }
   throw new API_Exception('Conversion failed');
@@ -414,6 +421,15 @@ function _civicrm_api3_system_utf8conversion_spec(&$params) {
     'type' => CRM_Utils_Type::T_BOOLEAN,
     'api.default' => FALSE,
   ];
+  $params['patterns'] = [
+    'title' => ts('CSV list of table patterns (defaults to "civicrm\_%")'),
+    'type' => CRM_Utils_Type::T_STRING,
+    'api.default' => 'civicrm\_%',
+  ];
+  $params['databases'] = [
+    'title' => ts('CSV list of database names (defaults to CiviCRM database)'),
+    'type' => CRM_Utils_Type::T_STRING,
+  ];
 }
 
 /**
diff --git a/civicrm/api/v3/utils.php b/civicrm/api/v3/utils.php
index 50268a400c1dba09291521f5b25adef03a2aa0ea..799dc7803197063a0168e80510ee71eb7e020659 100644
--- a/civicrm/api/v3/utils.php
+++ b/civicrm/api/v3/utils.php
@@ -1221,7 +1221,7 @@ function formatCheckBoxField(&$checkboxFieldValue, $customFieldLabel, $entity) {
 /**
  * Function to do a 'standard' api get - when the api is only doing a $bao->find then use this.
  *
- * @param string $bao_name
+ * @param string|CRM_Core_DAO $bao_name
  *   Name of BAO.
  * @param array $params
  *   Params from api.
@@ -1240,20 +1240,27 @@ function _civicrm_api3_basic_get($bao_name, $params, $returnAsSuccess = TRUE, $e
   $entity = $entity ?: CRM_Core_DAO_AllCoreTables::getBriefName($bao_name);
   $options = _civicrm_api3_get_options_from_params($params);
 
-  $query = new \Civi\API\Api3SelectQuery($entity, CRM_Utils_Array::value('check_permissions', $params, FALSE));
-  $query->where = $params;
-  if ($options['is_count']) {
-    $query->select = ['count_rows'];
+  // Skip query if table doesn't exist yet due to pending upgrade
+  if (!$bao_name::tableHasBeenAdded()) {
+    \Civi::log()->warning("Could not read from {$entity} before table has been added. Upgrade required.", ['civi.tag' => 'upgrade_needed']);
+    $result = [];
   }
   else {
-    $query->select = array_keys(array_filter($options['return']));
-    $query->orderBy = $options['sort'];
-    $query->isFillUniqueFields = $uniqueFields;
-  }
-  $query->limit = $options['limit'];
-  $query->offset = $options['offset'];
-  $query->merge($sql);
-  $result = $query->run();
+    $query = new \Civi\API\Api3SelectQuery($entity, $params['check_permissions'] ?? FALSE);
+    $query->where = $params;
+    if ($options['is_count']) {
+      $query->select = ['count_rows'];
+    }
+    else {
+      $query->select = array_keys(array_filter($options['return']));
+      $query->orderBy = $options['sort'];
+      $query->isFillUniqueFields = $uniqueFields;
+    }
+    $query->limit = $options['limit'];
+    $query->offset = $options['offset'];
+    $query->merge($sql);
+    $result = $query->run();
+  }
 
   if ($returnAsSuccess) {
     return civicrm_api3_create_success($result, $params, $entity, 'get');
diff --git a/civicrm/bin/regen.sh b/civicrm/bin/regen.sh
index f7451a23fc0250556cc315273fbccdc777ea373b..6cf3eb54c643405a95347d7142a1947a8673150d 100755
--- a/civicrm/bin/regen.sh
+++ b/civicrm/bin/regen.sh
@@ -47,7 +47,7 @@ php GenerateData.php
 
 ## Prune local data
 $MYSQLCMD -e "DROP TABLE IF EXISTS civicrm_install_canary; DELETE FROM civicrm_cache; DELETE FROM civicrm_setting;"
-$MYSQLCMD -e "DELETE FROM civicrm_extension WHERE full_name NOT IN ('sequentialcreditnotes', 'eventcart', 'search', 'flexmailer');"
+$MYSQLCMD -e "DELETE FROM civicrm_extension WHERE full_name NOT IN ('sequentialcreditnotes', 'eventcart', 'search', 'flexmailer', 'financialacls');"
 TABLENAMES=$( echo "show tables like 'civicrm_%'" | $MYSQLCMD | grep ^civicrm_ | xargs )
 
 cd $CIVISOURCEDIR/sql
diff --git a/civicrm/civicrm-version.php b/civicrm/civicrm-version.php
index 2fd54269c2c7c8ebe993572a1a95d8ee9450aa99..0474649f10b21372a0cba0075913cab9a87db544 100644
--- a/civicrm/civicrm-version.php
+++ b/civicrm/civicrm-version.php
@@ -1,7 +1,7 @@
 <?php
 /** @deprecated */
 function civicrmVersion( ) {
-  return array( 'version'  => '5.29.1',
+  return array( 'version'  => '5.30.0',
                 'cms'      => 'Wordpress',
                 'revision' => '' );
 }
diff --git a/civicrm/composer.json b/civicrm/composer.json
index 0a737c6dd99edbeffd7ee266201a53becf06eaba..9dfefaa375a7725c4e1b1933a20c09efec8ddf0f 100644
--- a/civicrm/composer.json
+++ b/civicrm/composer.json
@@ -78,7 +78,8 @@
     "typo3/phar-stream-wrapper": "^2 || ^3.0",
     "brick/money": "~0.4",
     "ext-intl": "*",
-    "pear/mail_mime": "~1.10"
+    "pear/mail_mime": "~1.10",
+    "pear/db": "1.10"
   },
   "scripts": {
     "post-install-cmd": [
@@ -251,6 +252,9 @@
       "electrolinux/phpquery": {
         "PHP7.4 Fix for array access using {} instead of []": "https://raw.githubusercontent.com/civicrm/civicrm-core/fe45bdfc4f3e3d3deb27e3d853cdbc7f616620a9/tools/scripts/composer/patches/php74_array_access_fix_phpquery.patch"
       },
+      "pear/db": {
+        "Apply CiviCRM Customisations for the pear:db package": "https://raw.githubusercontent.com/civicrm/civicrm-core/a48a43c2b5f6d694fff1cfb99d522c5d9e2459a0/tools/scripts/composer/pear_db_civicrm_changes.patch"
+      },
       "pear/mail": {
         "Apply CiviCRM Customisations for CRM-1367 and CRM-5946": "https://raw.githubusercontent.com/civicrm/civicrm-core/36319938a5bf26c1e7e2110a26a65db6a5979268/tools/scripts/composer/patches/pear-mail.patch"
       },
diff --git a/civicrm/composer.lock b/civicrm/composer.lock
index e39dd5c9d291df339edfa687979e2f0342a01a01..44f39034684af8fe059b3ccc171784276190478f 100644
--- a/civicrm/composer.lock
+++ b/civicrm/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "1ff9c045fb03756148c0c66562aa61fd",
+    "content-hash": "46e891da51f0683373d9a6e62fb6f868",
     "packages": [
         {
             "name": "adrienrn/php-mimetyper",
@@ -998,6 +998,66 @@
             "description": "More info available on: http://pear.php.net/package/Console_Getopt",
             "time": "2015-07-20T20:28:12+00:00"
         },
+        {
+            "name": "pear/db",
+            "version": "v1.10.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pear/DB.git",
+                "reference": "e158c3a48246b67cd8c95856ffbb93de4ef380fe"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pear/DB/zipball/e158c3a48246b67cd8c95856ffbb93de4ef380fe",
+                "reference": "e158c3a48246b67cd8c95856ffbb93de4ef380fe",
+                "shasum": ""
+            },
+            "require": {
+                "pear/pear-core-minimal": "*"
+            },
+            "type": "library",
+            "extra": {
+                "patches_applied": {
+                    "Apply CiviCRM Customisations for the pear:db package": "https://raw.githubusercontent.com/civicrm/civicrm-core/a48a43c2b5f6d694fff1cfb99d522c5d9e2459a0/tools/scripts/composer/pear_db_civicrm_changes.patch"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "DB": "./"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                "./"
+            ],
+            "license": [
+                "PHP License v3.01"
+            ],
+            "authors": [
+                {
+                    "name": "Daniel Convissor",
+                    "email": "danielc@php.net",
+                    "role": "Lead"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net",
+                    "role": "Lead"
+                },
+                {
+                    "name": "Stig Bakken",
+                    "email": "stig@php.net",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Tomas V.V.Cox",
+                    "email": "cox@idecnet.com",
+                    "role": "Developer"
+                }
+            ],
+            "description": "More info available on: http://pear.php.net/package/DB",
+            "time": "2020-04-19T19:45:59+00:00"
+        },
         {
             "name": "pear/log",
             "version": "1.13.2",
diff --git a/civicrm/ext/eventcart/CRM/Event/Cart/StateMachine/Checkout.php b/civicrm/ext/eventcart/CRM/Event/Cart/StateMachine/Checkout.php
index 3ca22852815a1208ceea95682a9175ad2f57c21c..bff2b8adb04743beb59f6a111bae0d1286561d1c 100644
--- a/civicrm/ext/eventcart/CRM/Event/Cart/StateMachine/Checkout.php
+++ b/civicrm/ext/eventcart/CRM/Event/Cart/StateMachine/Checkout.php
@@ -19,14 +19,7 @@ class CRM_Event_Cart_StateMachine_Checkout extends CRM_Core_StateMachine {
     }
 
     $pages = [];
-    $is_monetary = FALSE;
-    $is_conference = FALSE;
-    foreach ($cart->events_in_carts as $event_in_cart) {
-      if ($event_in_cart->event->is_monetary) {
-        $is_monetary = TRUE;
-      }
-    }
-    $pages["CRM_Event_Cart_Form_Checkout_ParticipantsAndPrices"] = NULL;
+    $pages['CRM_Event_Cart_Form_Checkout_ParticipantsAndPrices'] = NULL;
     foreach ($cart->events_in_carts as $event_in_cart) {
       if ($event_in_cart->is_parent_event()) {
         foreach ($event_in_cart->participants as $participant) {
diff --git a/civicrm/ext/financialacls/LICENSE.txt b/civicrm/ext/financialacls/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..96bc3698ee014bfea8c1501557fddf8ccad1fec8
--- /dev/null
+++ b/civicrm/ext/financialacls/LICENSE.txt
@@ -0,0 +1,667 @@
+Package: financialacls
+Copyright (C) 2020, eileen <emcnaughton@wikimedia.org>
+Licensed under the GNU Affero Public License 3.0 (below).
+
+-------------------------------------------------------------------------------
+
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/civicrm/ext/financialacls/README.md b/civicrm/ext/financialacls/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..531f9bea768bd873513d7a96bbed5a51685b822f
--- /dev/null
+++ b/civicrm/ext/financialacls/README.md
@@ -0,0 +1,44 @@
+# financialacls
+
+![Screenshot](/images/screenshot.png)
+
+(*FIXME: In one or two paragraphs, describe what the extension does and why one would download it. *)
+
+The extension is licensed under [AGPL-3.0](LICENSE.txt).
+
+## Requirements
+
+* PHP v7.0+
+* CiviCRM (*FIXME: Version number*)
+
+## Installation (Web UI)
+
+This extension has not yet been published for installation via the web UI.
+
+## Installation (CLI, Zip)
+
+Sysadmins and developers may download the `.zip` file for this extension and
+install it with the command-line tool [cv](https://github.com/civicrm/cv).
+
+```bash
+cd <extension-dir>
+cv dl financialacls@https://github.com/FIXME/financialacls/archive/master.zip
+```
+
+## Installation (CLI, Git)
+
+Sysadmins and developers may clone the [Git](https://en.wikipedia.org/wiki/Git) repo for this extension and
+install it with the command-line tool [cv](https://github.com/civicrm/cv).
+
+```bash
+git clone https://github.com/FIXME/financialacls.git
+cv en financialacls
+```
+
+## Usage
+
+(* FIXME: Where would a new user navigate to get started? What changes would they see? *)
+
+## Known Issues
+
+(* FIXME *)
diff --git a/civicrm/ext/financialacls/financialacls.civix.php b/civicrm/ext/financialacls/financialacls.civix.php
new file mode 100644
index 0000000000000000000000000000000000000000..2f5da65961871ba28bb8e0f1a97a376226fa9aa2
--- /dev/null
+++ b/civicrm/ext/financialacls/financialacls.civix.php
@@ -0,0 +1,477 @@
+<?php
+
+// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
+
+/**
+ * The ExtensionUtil class provides small stubs for accessing resources of this
+ * extension.
+ */
+class CRM_Financialacls_ExtensionUtil {
+  const SHORT_NAME = "financialacls";
+  const LONG_NAME = "financialacls";
+  const CLASS_PREFIX = "CRM_Financialacls";
+
+  /**
+   * 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_Financialacls_ExtensionUtil as E;
+
+/**
+ * (Delegated) Implements hook_civicrm_config().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
+ */
+function _financialacls_civix_civicrm_config(&$config = NULL) {
+  static $configured = FALSE;
+  if ($configured) {
+    return;
+  }
+  $configured = TRUE;
+
+  $template =& CRM_Core_Smarty::singleton();
+
+  $extRoot = dirname(__FILE__) . DIRECTORY_SEPARATOR;
+  $extDir = $extRoot . 'templates';
+
+  if (is_array($template->template_dir)) {
+    array_unshift($template->template_dir, $extDir);
+  }
+  else {
+    $template->template_dir = [$extDir, $template->template_dir];
+  }
+
+  $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
+  set_include_path($include_path);
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_xmlMenu().
+ *
+ * @param $files array(string)
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu
+ */
+function _financialacls_civix_civicrm_xmlMenu(&$files) {
+  foreach (_financialacls_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) {
+    $files[] = $file;
+  }
+}
+
+/**
+ * Implements hook_civicrm_install().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install
+ */
+function _financialacls_civix_civicrm_install() {
+  _financialacls_civix_civicrm_config();
+  if ($upgrader = _financialacls_civix_upgrader()) {
+    $upgrader->onInstall();
+  }
+}
+
+/**
+ * Implements hook_civicrm_postInstall().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
+ */
+function _financialacls_civix_civicrm_postInstall() {
+  _financialacls_civix_civicrm_config();
+  if ($upgrader = _financialacls_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 _financialacls_civix_civicrm_uninstall() {
+  _financialacls_civix_civicrm_config();
+  if ($upgrader = _financialacls_civix_upgrader()) {
+    $upgrader->onUninstall();
+  }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_enable().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable
+ */
+function _financialacls_civix_civicrm_enable() {
+  _financialacls_civix_civicrm_config();
+  if ($upgrader = _financialacls_civix_upgrader()) {
+    if (is_callable([$upgrader, 'onEnable'])) {
+      $upgrader->onEnable();
+    }
+  }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_disable().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
+ * @return mixed
+ */
+function _financialacls_civix_civicrm_disable() {
+  _financialacls_civix_civicrm_config();
+  if ($upgrader = _financialacls_civix_upgrader()) {
+    if (is_callable([$upgrader, 'onDisable'])) {
+      $upgrader->onDisable();
+    }
+  }
+}
+
+/**
+ * (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
+ *
+ * @return mixed
+ *   based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending)
+ *   for 'enqueue', returns void
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
+ */
+function _financialacls_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
+  if ($upgrader = _financialacls_civix_upgrader()) {
+    return $upgrader->onUpgrade($op, $queue);
+  }
+}
+
+/**
+ * @return CRM_Financialacls_Upgrader
+ */
+function _financialacls_civix_upgrader() {
+  if (!file_exists(__DIR__ . '/CRM/Financialacls/Upgrader.php')) {
+    return NULL;
+  }
+  else {
+    return CRM_Financialacls_Upgrader_Base::instance();
+  }
+}
+
+/**
+ * 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 string $dir base dir
+ * @param string $pattern , glob pattern, eg "*.txt"
+ *
+ * @return array
+ */
+function _financialacls_civix_find_files($dir, $pattern) {
+  if (is_callable(['CRM_Utils_File', 'findFiles'])) {
+    return CRM_Utils_File::findFiles($dir, $pattern);
+  }
+
+  $todos = [$dir];
+  $result = [];
+  while (!empty($todos)) {
+    $subdir = array_shift($todos);
+    foreach (_financialacls_civix_glob("$subdir/$pattern") as $match) {
+      if (!is_dir($match)) {
+        $result[] = $match;
+      }
+    }
+    if ($dh = opendir($subdir)) {
+      while (FALSE !== ($entry = readdir($dh))) {
+        $path = $subdir . DIRECTORY_SEPARATOR . $entry;
+        if ($entry[0] == '.') {
+        }
+        elseif (is_dir($path)) {
+          $todos[] = $path;
+        }
+      }
+      closedir($dh);
+    }
+  }
+  return $result;
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_managed().
+ *
+ * Find any *.mgd.php files, merge their content, and return.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed
+ */
+function _financialacls_civix_civicrm_managed(&$entities) {
+  $mgdFiles = _financialacls_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'] = E::LONG_NAME;
+      }
+      if (empty($e['params']['version'])) {
+        $e['params']['version'] = '3';
+      }
+      $entities[] = $e;
+    }
+  }
+}
+
+/**
+ * (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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_caseTypes
+ */
+function _financialacls_civix_civicrm_caseTypes(&$caseTypes) {
+  if (!is_dir(__DIR__ . '/xml/case')) {
+    return;
+  }
+
+  foreach (_financialacls_civix_glob(__DIR__ . '/xml/case/*.xml') as $file) {
+    $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));
+      throw new CRM_Core_Exception($errorMessage);
+    }
+    $caseTypes[$name] = [
+      '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 _financialacls_civix_civicrm_angularModules(&$angularModules) {
+  if (!is_dir(__DIR__ . '/ang')) {
+    return;
+  }
+
+  $files = _financialacls_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 _financialacls_civix_civicrm_themes(&$themes) {
+  $files = _financialacls_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.
+ *
+ * The documentation for glob() says, "On some systems it is impossible to
+ * distinguish between empty match and an error." Anecdotally, the return
+ * result for an empty match is sometimes array() and sometimes FALSE.
+ * This wrapper provides consistency.
+ *
+ * @link http://php.net/glob
+ * @param string $pattern
+ *
+ * @return array
+ */
+function _financialacls_civix_glob($pattern) {
+  $result = glob($pattern);
+  return is_array($result) ? $result : [];
+}
+
+/**
+ * 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)
+ *
+ * @return bool
+ */
+function _financialacls_civix_insert_navigation_menu(&$menu, $path, $item) {
+  // If we are done going down the path, insert menu
+  if (empty($path)) {
+    $menu[] = [
+      'attributes' => array_merge([
+        'label'      => CRM_Utils_Array::value('name', $item),
+        'active'     => 1,
+      ], $item),
+    ];
+    return TRUE;
+  }
+  else {
+    // Find an recurse into the next level down
+    $found = FALSE;
+    $path = explode('/', $path);
+    $first = array_shift($path);
+    foreach ($menu as $key => &$entry) {
+      if ($entry['attributes']['name'] == $first) {
+        if (!isset($entry['child'])) {
+          $entry['child'] = [];
+        }
+        $found = _financialacls_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item);
+      }
+    }
+    return $found;
+  }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_navigationMenu().
+ */
+function _financialacls_civix_navigationMenu(&$nodes) {
+  if (!is_callable(['CRM_Core_BAO_Navigation', 'fixNavigationMenu'])) {
+    _financialacls_civix_fixNavigationMenu($nodes);
+  }
+}
+
+/**
+ * Given a navigation menu, generate navIDs for any items which are
+ * missing them.
+ */
+function _financialacls_civix_fixNavigationMenu(&$nodes) {
+  $maxNavID = 1;
+  array_walk_recursive($nodes, function($item, $key) use (&$maxNavID) {
+    if ($key === 'navID') {
+      $maxNavID = max($maxNavID, $item);
+    }
+  });
+  _financialacls_civix_fixNavigationMenuItems($nodes, $maxNavID, NULL);
+}
+
+function _financialacls_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'])) {
+      _financialacls_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 _financialacls_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
+  $settingsDir = __DIR__ . DIRECTORY_SEPARATOR . 'settings';
+  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 _financialacls_civix_civicrm_entityTypes(&$entityTypes) {
+  $entityTypes = array_merge($entityTypes, []);
+}
diff --git a/civicrm/ext/financialacls/financialacls.php b/civicrm/ext/financialacls/financialacls.php
new file mode 100644
index 0000000000000000000000000000000000000000..b079114d9f6f775872bcf5c7c47598130daee7c5
--- /dev/null
+++ b/civicrm/ext/financialacls/financialacls.php
@@ -0,0 +1,218 @@
+<?php
+
+require_once 'financialacls.civix.php';
+// phpcs:disable
+use CRM_Financialacls_ExtensionUtil as E;
+// phpcs:enable
+
+/**
+ * Implements hook_civicrm_config().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/
+ */
+function financialacls_civicrm_config(&$config) {
+  _financialacls_civix_civicrm_config($config);
+}
+
+/**
+ * Implements hook_civicrm_xmlMenu().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu
+ */
+function financialacls_civicrm_xmlMenu(&$files) {
+  _financialacls_civix_civicrm_xmlMenu($files);
+}
+
+/**
+ * Implements hook_civicrm_install().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install
+ */
+function financialacls_civicrm_install() {
+  _financialacls_civix_civicrm_install();
+}
+
+/**
+ * Implements hook_civicrm_postInstall().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
+ */
+function financialacls_civicrm_postInstall() {
+  _financialacls_civix_civicrm_postInstall();
+}
+
+/**
+ * Implements hook_civicrm_uninstall().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
+ */
+function financialacls_civicrm_uninstall() {
+  _financialacls_civix_civicrm_uninstall();
+}
+
+/**
+ * Implements hook_civicrm_enable().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable
+ */
+function financialacls_civicrm_enable() {
+  _financialacls_civix_civicrm_enable();
+}
+
+/**
+ * Implements hook_civicrm_disable().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
+ */
+function financialacls_civicrm_disable() {
+  _financialacls_civix_civicrm_disable();
+}
+
+/**
+ * Implements hook_civicrm_upgrade().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
+ */
+function financialacls_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
+  return _financialacls_civix_civicrm_upgrade($op, $queue);
+}
+
+/**
+ * Implements hook_civicrm_managed().
+ *
+ * Generate a list of entities to create/deactivate/delete when this module
+ * is installed, disabled, uninstalled.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed
+ */
+function financialacls_civicrm_managed(&$entities) {
+  _financialacls_civix_civicrm_managed($entities);
+}
+
+/**
+ * Implements hook_civicrm_caseTypes().
+ *
+ * Generate a list of case-types.
+ *
+ * Note: This hook only runs in CiviCRM 4.4+.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_caseTypes
+ */
+function financialacls_civicrm_caseTypes(&$caseTypes) {
+  _financialacls_civix_civicrm_caseTypes($caseTypes);
+}
+
+/**
+ * Implements hook_civicrm_angularModules().
+ *
+ * Generate a list of Angular modules.
+ *
+ * Note: This hook only runs in CiviCRM 4.5+. It may
+ * use features only available in v4.6+.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules
+ */
+function financialacls_civicrm_angularModules(&$angularModules) {
+  _financialacls_civix_civicrm_angularModules($angularModules);
+}
+
+/**
+ * Implements hook_civicrm_alterSettingsFolders().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterSettingsFolders
+ */
+function financialacls_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
+  _financialacls_civix_civicrm_alterSettingsFolders($metaDataFolders);
+}
+
+/**
+ * Implements hook_civicrm_entityTypes().
+ *
+ * Declare entity types provided by this module.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
+ */
+function financialacls_civicrm_entityTypes(&$entityTypes) {
+  _financialacls_civix_civicrm_entityTypes($entityTypes);
+}
+
+/**
+ * Implements hook_civicrm_thems().
+ */
+function financialacls_civicrm_themes(&$themes) {
+  _financialacls_civix_civicrm_themes($themes);
+}
+
+/**
+ * Intervene to prevent deletion, where permissions block it.
+ *
+ * @param \CRM_Core_DAO $op
+ * @param string $objectName
+ * @param int|null $id
+ * @param array $params
+ *
+ * @throws \API_Exception
+ * @throws \CRM_Core_Exception
+ */
+function financialacls_civicrm_pre($op, $objectName, $id, &$params) {
+  if ($objectName === 'LineItem' && $op === 'delete' && !empty($params['check_permissions'])) {
+    if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) {
+      CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types, CRM_Core_Action::DELETE);
+      if (empty($params['financial_type_id'])) {
+        $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_LineItem', $params['id'], 'financial_type_id');
+      }
+      if (!in_array($params['financial_type_id'], array_keys($types))) {
+        throw new API_Exception('You do not have permission to delete this line item');
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_civicrm_selectWhereClause().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_selectWhereClause
+ */
+function financialacls_civicrm_selectWhereClause($entity, &$clauses) {
+  if ($entity === 'LineItem') {
+    if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) {
+      $types = [];
+      CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types);
+      if ($types) {
+        $clauses['financial_type_id'] = 'IN (' . implode(',', array_keys($types)) . ')';
+      }
+      else {
+        $clauses['financial_type_id'] = '= 0';
+      }
+    }
+  }
+
+}
+
+// --- Functions below this ship commented out. Uncomment as required. ---
+
+/**
+ * Implements hook_civicrm_preProcess().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_preProcess
+ */
+//function financialacls_civicrm_preProcess($formName, &$form) {
+//
+//}
+
+/**
+ * Implements hook_civicrm_navigationMenu().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_navigationMenu
+ */
+//function financialacls_civicrm_navigationMenu(&$menu) {
+//  _financialacls_civix_insert_navigation_menu($menu, 'Mailings', array(
+//    'label' => E::ts('New subliminal message'),
+//    'name' => 'mailing_subliminal_message',
+//    'url' => 'civicrm/mailing/subliminal',
+//    'permission' => 'access CiviMail',
+//    'operator' => 'OR',
+//    'separator' => 0,
+//  ));
+//  _financialacls_civix_navigationMenu($menu);
+//}
diff --git a/civicrm/ext/financialacls/images/screenshot.png b/civicrm/ext/financialacls/images/screenshot.png
new file mode 100644
index 0000000000000000000000000000000000000000..6765b696fa03249ac2cd605d5f0e4aa000ad6dad
Binary files /dev/null and b/civicrm/ext/financialacls/images/screenshot.png differ
diff --git a/civicrm/ext/financialacls/info.xml b/civicrm/ext/financialacls/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8539355b325693617bebccd5ed8cc4788178be19
--- /dev/null
+++ b/civicrm/ext/financialacls/info.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<extension key="financialacls" type="module">
+  <file>financialacls</file>
+  <name>Financial ACLs</name>
+  <description>Implements financial ACLS - may not be disabled without risk of site breakage while in progress</description>
+  <license>AGPL-3.0</license>
+  <maintainer>
+    <author>CiviCRM</author>
+    <email>info@civicrm.org</email>
+  </maintainer>
+  <urls>
+    <url desc="Main Extension Page">http://FIXME</url>
+    <url desc="Documentation">http://FIXME</url>
+    <url desc="Support">http://FIXME</url>
+    <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
+  </urls>
+  <releaseDate>2020-08-27</releaseDate>
+  <version>1.0</version>
+  <develStage>stable</develStage>
+  <compatibility>
+    <ver>5.30</ver>
+  </compatibility>
+  <tags>
+    <tag>mgmt:hidden</tag>
+  </tags>
+  <comments>Financial acls - working towards moving this code to the extension from core</comments>
+  <classloader>
+    <psr4 prefix="Civi\" path="Civi"/>
+  </classloader>
+  <civix>
+    <namespace>CRM/Financialacls</namespace>
+  </civix>
+</extension>
diff --git a/civicrm/ext/financialacls/phpunit.xml.dist b/civicrm/ext/financialacls/phpunit.xml.dist
new file mode 100644
index 0000000000000000000000000000000000000000..fc8f870b723b86a3cdf77609656b6ce38d0288ce
--- /dev/null
+++ b/civicrm/ext/financialacls/phpunit.xml.dist
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<phpunit backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" bootstrap="tests/phpunit/bootstrap.php">
+  <testsuites>
+    <testsuite name="My Test Suite">
+      <directory>./tests/phpunit</directory>
+    </testsuite>
+  </testsuites>
+  <filter>
+    <whitelist>
+      <directory suffix=".php">./</directory>
+    </whitelist>
+  </filter>
+  <listeners>
+    <listener class="Civi\Test\CiviTestListener">
+      <arguments/>
+    </listener>
+  </listeners>
+</phpunit>
diff --git a/civicrm/ext/financialacls/tests/phpunit/LineItemTest.php b/civicrm/ext/financialacls/tests/phpunit/LineItemTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..71eb6380c4d084b8183cacce60b2aa8ed20741b8
--- /dev/null
+++ b/civicrm/ext/financialacls/tests/phpunit/LineItemTest.php
@@ -0,0 +1,114 @@
+<?php
+
+use CRM_Financialacls_ExtensionUtil as E;
+use Civi\Test\HeadlessInterface;
+use Civi\Test\HookInterface;
+use Civi\Test\TransactionalInterface;
+use Civi\Api4\PriceField;
+
+/**
+ * FIXME - Add test description.
+ *
+ * Tips:
+ *  - With HookInterface, you may implement CiviCRM hooks directly in the test class.
+ *    Simply create corresponding functions (e.g. "hook_civicrm_post(...)" or similar).
+ *  - With TransactionalInterface, any data changes made by setUp() or test****() functions will
+ *    rollback automatically -- as long as you don't manipulate schema or truncate tables.
+ *    If this test needs to manipulate schema or truncate tables, then either:
+ *       a. Do all that using setupHeadless() and Civi\Test.
+ *       b. Disable TransactionalInterface, and handle all setup/teardown yourself.
+ *
+ * @group headless
+ */
+class LineItemTest extends \PHPUnit\Framework\TestCase implements HeadlessInterface, HookInterface, TransactionalInterface {
+
+  use Civi\Test\ContactTestTrait;
+  use Civi\Test\Api3TestTrait;
+
+  /**
+   * @return \Civi\Test\CiviEnvBuilder
+   * @throws \CRM_Extension_Exception_ParseException
+   */
+  public function setUpHeadless() {
+    // Civi\Test has many helpers, like install(), uninstall(), sql(), and sqlFile().
+    // See: https://docs.civicrm.org/dev/en/latest/testing/phpunit/#civitest
+    return \Civi\Test::headless()
+      ->installMe(__DIR__)
+      ->apply();
+  }
+
+  /**
+   * Test api applies permissions on line item actions (delete & get).
+   */
+  public function testLineItemApiPermissions() {
+    $contact1 = $this->individualCreate();
+    $defaultPriceFieldID = $this->getDefaultPriceFieldID();
+    $this->callAPISuccess('Order', 'create', [
+      'financial_type_id' => 'Donation',
+      'contact_id' => $contact1,
+      'line_items' => [
+        [
+          'line_item' => [
+            [
+              'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'),
+              'line_total' => 40,
+              'price_field_id' => $defaultPriceFieldID,
+              'qty' => 1,
+            ],
+            [
+              'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Member Dues'),
+              'line_total' => 50,
+              'price_field_id' => $defaultPriceFieldID,
+              'qty' => 1,
+            ],
+          ],
+        ],
+      ],
+    ]);
+
+    $this->setPermissions([
+      'access CiviCRM',
+      'access CiviContribute',
+      'edit contributions',
+      'delete in CiviContribute',
+      'view contributions of type Donation',
+      'delete contributions of type Donation',
+    ]);
+    Civi::settings()->set('acl_financial_type', TRUE);
+    $this->createLoggedInUser();
+
+    $lineItems = $this->callAPISuccess('LineItem', 'get', ['sequential' => TRUE])['values'];
+    $this->assertCount(2, $lineItems);
+    $this->callAPISuccessGetCount('LineItem', ['check_permissions' => TRUE], 1);
+
+    $this->callAPISuccess('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[0]['id']]);
+    $this->callAPIFailure('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[1]['id']]);
+  }
+
+  /**
+   * Set ACL permissions, overwriting any existing ones.
+   *
+   * @param array $permissions
+   *   Array of permissions e.g ['access CiviCRM','access CiviContribute'],
+   */
+  protected function setPermissions($permissions) {
+    CRM_Core_Config::singleton()->userPermissionClass->permissions = $permissions;
+    if (isset(\Civi::$statics['CRM_Financial_BAO_FinancialType'])) {
+      unset(\Civi::$statics['CRM_Financial_BAO_FinancialType']);
+    }
+  }
+
+  /**
+   * @return mixed
+   * @throws \API_Exception
+   * @throws \Civi\API\Exception\UnauthorizedException
+   */
+  protected function getDefaultPriceFieldID(): int {
+    return PriceField::get()
+      ->addWhere('price_set_id:name', '=', 'default_contribution_amount')
+      ->addWhere('name', '=', 'contribution_amount')
+      ->addWhere('html_type', '=', 'Text')
+      ->addSelect('id')->execute()->first()['id'];
+  }
+
+}
diff --git a/civicrm/ext/financialacls/tests/phpunit/bootstrap.php b/civicrm/ext/financialacls/tests/phpunit/bootstrap.php
new file mode 100644
index 0000000000000000000000000000000000000000..a5b49253c819c5ab65947070e7ee67d0cac48ea1
--- /dev/null
+++ b/civicrm/ext/financialacls/tests/phpunit/bootstrap.php
@@ -0,0 +1,63 @@
+<?php
+
+ini_set('memory_limit', '2G');
+ini_set('safe_mode', 0);
+// phpcs:disable
+eval(cv('php:boot --level=classloader', 'phpcode'));
+// phpcs:enable
+// Allow autoloading of PHPUnit helper classes in this extension.
+$loader = new \Composer\Autoload\ClassLoader();
+$loader->add('CRM_', __DIR__);
+$loader->add('Civi\\', __DIR__);
+$loader->add('api_', __DIR__);
+$loader->add('api\\', __DIR__);
+$loader->register();
+
+/**
+ * Call the "cv" command.
+ *
+ * @param string $cmd
+ *   The rest of the command to send.
+ * @param string $decode
+ *   Ex: 'json' or 'phpcode'.
+ * @return string
+ *   Response output (if the command executed normally).
+ * @throws \RuntimeException
+ *   If the command terminates abnormally.
+ */
+function cv($cmd, $decode = 'json') {
+  $cmd = 'cv ' . $cmd;
+  $descriptorSpec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => STDERR);
+  $oldOutput = getenv('CV_OUTPUT');
+  putenv("CV_OUTPUT=json");
+
+  // Execute `cv` in the original folder. This is a work-around for
+  // phpunit/codeception, which seem to manipulate PWD.
+  $cmd = sprintf('cd %s; %s', escapeshellarg(getenv('PWD')), $cmd);
+
+  $process = proc_open($cmd, $descriptorSpec, $pipes, __DIR__);
+  putenv("CV_OUTPUT=$oldOutput");
+  fclose($pipes[0]);
+  $result = stream_get_contents($pipes[1]);
+  fclose($pipes[1]);
+  if (proc_close($process) !== 0) {
+    throw new RuntimeException("Command failed ($cmd):\n$result");
+  }
+  switch ($decode) {
+    case 'raw':
+      return $result;
+
+    case 'phpcode':
+      // If the last output is /*PHPCODE*/, then we managed to complete execution.
+      if (substr(trim($result), 0, 12) !== "/*BEGINPHP*/" || substr(trim($result), -10) !== "/*ENDPHP*/") {
+        throw new \RuntimeException("Command failed ($cmd):\n$result");
+      }
+      return $result;
+
+    case 'json':
+      return json_decode($result, 1);
+
+    default:
+      throw new RuntimeException("Bad decoder format ($decode)");
+  }
+}
diff --git a/civicrm/ext/search/CRM/Search/Page/Ang.php b/civicrm/ext/search/CRM/Search/Page/Ang.php
new file mode 100644
index 0000000000000000000000000000000000000000..b6a03e2bbf76d216dfb430ace86f300d39785ff2
--- /dev/null
+++ b/civicrm/ext/search/CRM/Search/Page/Ang.php
@@ -0,0 +1,197 @@
+<?php
+
+class CRM_Search_Page_Ang extends CRM_Core_Page {
+  /**
+   * @var string[]
+   */
+  private $loadOptions = ['id', 'name', 'label', 'description', 'color', 'icon'];
+
+  /**
+   * @var array
+   */
+  private $schema = [];
+
+  /**
+   * @var string[]
+   */
+  private $allowedEntities = [];
+
+  public function run() {
+    $breadCrumb = [
+      'title' => ts('Search'),
+      'url' => CRM_Utils_System::url('civicrm/search'),
+    ];
+    CRM_Utils_System::appendBreadCrumb([$breadCrumb]);
+
+    $this->getSchema();
+
+    // If user does not have permission to search any entity, bye bye.
+    if (!$this->allowedEntities) {
+      CRM_Utils_System::permissionDenied();
+    }
+
+    // Add client-side vars for the search UI
+    $vars = [
+      'operators' => CRM_Utils_Array::makeNonAssociative($this->getOperators()),
+      'schema' => $this->schema,
+      'links' => $this->getLinks(),
+      'loadOptions' => $this->loadOptions,
+      'actions' => $this->getActions(),
+      'functions' => CRM_Api4_Page_Api4Explorer::getSqlFunctions(),
+    ];
+
+    Civi::resources()
+      ->addPermissions(['edit groups', 'administer reserved groups'])
+      ->addVars('search', $vars);
+
+    // Load angular module
+    $loader = new Civi\Angular\AngularLoader();
+    $loader->setModules(['search']);
+    $loader->setPageName('civicrm/search');
+    $loader->useApp([
+      'defaultRoute' => '/Contact',
+    ]);
+    $loader->load();
+    parent::run();
+  }
+
+  /**
+   * @return string[]
+   */
+  private function getOperators() {
+    return [
+      '=' => '=',
+      '!=' => '≠',
+      '>' => '>',
+      '<' => '<',
+      '>=' => '≥',
+      '<=' => '≤',
+      'CONTAINS' => ts('Contains'),
+      'IN' => ts('Is In'),
+      'NOT IN' => ts('Not In'),
+      'LIKE' => ts('Is Like'),
+      'NOT LIKE' => ts('Not Like'),
+      'BETWEEN' => ts('Is Between'),
+      'NOT BETWEEN' => ts('Not Between'),
+      'IS NULL' => ts('Is Null'),
+      'IS NOT NULL' => ts('Not Null'),
+    ];
+  }
+
+  /**
+   * Populates $this->schema & $this->allowedEntities
+   */
+  private function getSchema() {
+    $schema = \Civi\Api4\Entity::get()
+      ->addSelect('name', 'title', 'description', 'icon')
+      ->addWhere('name', '!=', 'Entity')
+      ->addOrderBy('title')
+      ->setChain([
+        'get' => ['$name', 'getActions', ['where' => [['name', '=', 'get']]], ['params']],
+      ])->execute();
+    $getFields = ['name', 'label', 'description', 'options', 'input_type', 'input_attrs', 'data_type', 'serialize'];
+    foreach ($schema as $entity) {
+      // Skip if entity doesn't have a 'get' action or the user doesn't have permission to use get
+      if ($entity['get']) {
+        // Get fields and pre-load options for certain prominent entities
+        $loadOptions = in_array($entity['name'], ['Contact', 'Group']) ? $this->loadOptions : FALSE;
+        if ($loadOptions) {
+          $entity['optionsLoaded'] = TRUE;
+        }
+        $entity['fields'] = civicrm_api4($entity['name'], 'getFields', [
+          'select' => $getFields,
+          'where' => [['permission', 'IS NULL']],
+          'orderBy' => ['label'],
+          'loadOptions' => $loadOptions,
+        ]);
+        // Get the names of params this entity supports (minus some obvious ones)
+        $params = $entity['get'][0];
+        CRM_Utils_Array::remove($params, 'checkPermissions', 'debug', 'chain', 'language');
+        unset($entity['get']);
+        $this->schema[] = ['params' => array_keys($params)] + array_filter($entity);
+        $this->allowedEntities[] = $entity['name'];
+      }
+    }
+  }
+
+  /**
+   * @return array
+   */
+  private function getLinks() {
+    $results = [];
+    $keys = array_flip(['alias', 'entity', 'joinType']);
+    foreach (civicrm_api4('Entity', 'getLinks', ['where' => [['entity', 'IN', $this->allowedEntities]]], ['entity' => 'links']) as $entity => $links) {
+      $entityLinks = [];
+      foreach ($links as $link) {
+        if (!empty($link['entity']) && in_array($link['entity'], $this->allowedEntities)) {
+          // Use entity.alias as array key to avoid duplicates
+          $entityLinks[$link['entity'] . $link['alias']] = array_intersect_key($link, $keys);
+        }
+      }
+      $results[$entity] = array_values($entityLinks);
+    }
+    return array_filter($results);
+  }
+
+  /**
+   * @return array[]
+   */
+  private function getActions() {
+    // Note: the placeholder %1 will be replaced with entity name on the clientside
+    $actions = [
+      'export' => [
+        'title' => ts('Export %1'),
+        'icon' => 'fa-file-excel-o',
+        'entities' => array_keys(CRM_Export_BAO_Export::getComponents()),
+        'crmPopup' => [
+          'path' => "'civicrm/export/standalone'",
+          'query' => "{entity: entity, id: ids.join(',')}",
+        ],
+      ],
+      'update' => [
+        'title' => ts('Update %1'),
+        'icon' => 'fa-save',
+        'entities' => [],
+        'uiDialog' => ['templateUrl' => '~/search/crmSearchActions/crmSearchActionUpdate.html'],
+      ],
+      'delete' => [
+        'title' => ts('Delete %1'),
+        'icon' => 'fa-trash',
+        'entities' => [],
+        'uiDialog' => ['templateUrl' => '~/search/crmSearchActions/crmSearchActionDelete.html'],
+      ],
+    ];
+
+    // Check permissions for update & delete actions
+    foreach ($this->allowedEntities as $entity) {
+      $result = civicrm_api4($entity, 'getActions', [
+        'where' => [['name', 'IN', ['update', 'delete']]],
+      ], ['name']);
+      foreach ($result as $action) {
+        // Contacts have their own delete action
+        if (!($entity === 'Contact' && $action === 'delete')) {
+          $actions[$action]['entities'][] = $entity;
+        }
+      }
+    }
+
+    // Add contact tasks which support standalone mode (with a 'url' property)
+    $contactTasks = CRM_Contact_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission());
+    foreach (CRM_Contact_Task::tasks() as $id => $task) {
+      if (isset($contactTasks[$id]) && !empty($task['url'])) {
+        $actions['contact.' . $id] = [
+          'title' => $task['title'],
+          'entities' => ['Contact'],
+          'icon' => $task['icon'] ?? 'fa-gear',
+          'crmPopup' => [
+            'path' => "'{$task['url']}'",
+            'query' => "{cids: ids.join(',')}",
+          ],
+        ];
+      }
+    }
+
+    return $actions;
+  }
+
+}
diff --git a/civicrm/ext/search/CRM/Search/Upgrader.php b/civicrm/ext/search/CRM/Search/Upgrader.php
new file mode 100644
index 0000000000000000000000000000000000000000..7260e39813cf397682d0a1ca04c41e72ba2e9c8d
--- /dev/null
+++ b/civicrm/ext/search/CRM/Search/Upgrader.php
@@ -0,0 +1,34 @@
+<?php
+use CRM_Search_ExtensionUtil as E;
+
+/**
+ * Collection of upgrade steps.
+ */
+class CRM_Search_Upgrader extends CRM_Search_Upgrader_Base {
+
+  /**
+   * Add menu item when enabled.
+   */
+  public function enable() {
+    \Civi\Api4\Navigation::create(FALSE)
+      ->addValue('parent_id:name', 'Search')
+      ->addValue('label', E::ts('Create Search...'))
+      ->addValue('name', 'create_search')
+      ->addValue('url', 'civicrm/search')
+      ->addValue('icon', 'crm-i fa-search-plus')
+      ->addValue('has_separator', 2)
+      ->addValue('weight', 99)
+      ->execute();
+  }
+
+  /**
+   * Delete menu item when disabled.
+   */
+  public function disable() {
+    \Civi\Api4\Navigation::delete(FALSE)
+      ->addWhere('name', '=', 'create_search')
+      ->addWhere('domain_id', '=', 'current_domain')
+      ->execute();
+  }
+
+}
diff --git a/civicrm/ext/search/CRM/Search/Upgrader/Base.php b/civicrm/ext/search/CRM/Search/Upgrader/Base.php
new file mode 100644
index 0000000000000000000000000000000000000000..3015d7908f092e586d961095a6360dc668bf126e
--- /dev/null
+++ b/civicrm/ext/search/CRM/Search/Upgrader/Base.php
@@ -0,0 +1,391 @@
+<?php
+
+// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
+use CRM_Search_ExtensionUtil as E;
+
+/**
+ * Base class which provides helpers to execute upgrade logic
+ */
+class CRM_Search_Upgrader_Base {
+
+  /**
+   * @var CRM_Search_Upgrader_Base
+   */
+  public static $instance;
+
+  /**
+   * @var CRM_Queue_TaskContext
+   */
+  protected $ctx;
+
+  /**
+   * @var string
+   *   eg 'com.example.myextension'
+   */
+  protected $extensionName;
+
+  /**
+   * @var string
+   *   full path to the extension's source tree
+   */
+  protected $extensionDir;
+
+  /**
+   * @var array
+   *   sorted numerically
+   */
+  private $revisions;
+
+  /**
+   * @var bool
+   *   Flag to clean up extension revision data in civicrm_setting
+   */
+  private $revisionStorageIsDeprecated = FALSE;
+
+  /**
+   * Obtain a reference to the active upgrade handler.
+   */
+  public static function instance() {
+    if (!self::$instance) {
+      // FIXME auto-generate
+      self::$instance = new CRM_Search_Upgrader(
+        'org.civicrm.search',
+        realpath(__DIR__ . '/../../../')
+      );
+    }
+    return self::$instance;
+  }
+
+  /**
+   * Adapter that lets you add normal (non-static) member functions to the queue.
+   *
+   * Note: Each upgrader instance should only be associated with one
+   * task-context; otherwise, this will be non-reentrant.
+   *
+   * @code
+   * CRM_Search_Upgrader_Base::_queueAdapter($ctx, 'methodName', 'arg1', 'arg2');
+   * @endcode
+   */
+  public static function _queueAdapter() {
+    $instance = self::instance();
+    $args = func_get_args();
+    $instance->ctx = array_shift($args);
+    $instance->queue = $instance->ctx->queue;
+    $method = array_shift($args);
+    return call_user_func_array([$instance, $method], $args);
+  }
+
+  /**
+   * CRM_Search_Upgrader_Base constructor.
+   *
+   * @param $extensionName
+   * @param $extensionDir
+   */
+  public function __construct($extensionName, $extensionDir) {
+    $this->extensionName = $extensionName;
+    $this->extensionDir = $extensionDir;
+  }
+
+  // ******** Task helpers ********
+
+  /**
+   * Run a CustomData file.
+   *
+   * @param string $relativePath the CustomData XML file path (relative to this extension's dir)
+   * @return bool
+   */
+  public function executeCustomDataFile($relativePath) {
+    $xml_file = $this->extensionDir . '/' . $relativePath;
+    return $this->executeCustomDataFileByAbsPath($xml_file);
+  }
+
+  /**
+   * Run a CustomData file
+   *
+   * @param string $xml_file  the CustomData XML file path (absolute path)
+   *
+   * @return bool
+   */
+  protected function executeCustomDataFileByAbsPath($xml_file) {
+    $import = new CRM_Utils_Migrate_Import();
+    $import->run($xml_file);
+    return TRUE;
+  }
+
+  /**
+   * Run a SQL file.
+   *
+   * @param string $relativePath the SQL file path (relative to this extension's dir)
+   *
+   * @return bool
+   */
+  public function executeSqlFile($relativePath) {
+    CRM_Utils_File::sourceSQLFile(
+      CIVICRM_DSN,
+      $this->extensionDir . DIRECTORY_SEPARATOR . $relativePath
+    );
+    return TRUE;
+  }
+
+  /**
+   * Run the sql commands in the specified file.
+   *
+   * @param string $tplFile
+   *   The SQL file path (relative to this extension's dir).
+   *   Ex: "sql/mydata.mysql.tpl".
+   *
+   * @return bool
+   * @throws \CRM_Core_Exception
+   */
+  public function executeSqlTemplate($tplFile) {
+    // Assign multilingual variable to Smarty.
+    $upgrade = new CRM_Upgrade_Form();
+
+    $tplFile = CRM_Utils_File::isAbsolute($tplFile) ? $tplFile : $this->extensionDir . DIRECTORY_SEPARATOR . $tplFile;
+    $smarty = CRM_Core_Smarty::singleton();
+    $smarty->assign('domainID', CRM_Core_Config::domainID());
+    CRM_Utils_File::sourceSQLFile(
+      CIVICRM_DSN, $smarty->fetch($tplFile), NULL, TRUE
+    );
+    return TRUE;
+  }
+
+  /**
+   * Run one SQL query.
+   *
+   * This is just a wrapper for CRM_Core_DAO::executeSql, but it
+   * provides syntactic sugar for queueing several tasks that
+   * run different queries
+   *
+   * @return bool
+   */
+  public function executeSql($query, $params = []) {
+    // FIXME verify that we raise an exception on error
+    CRM_Core_DAO::executeQuery($query, $params);
+    return TRUE;
+  }
+
+  /**
+   * Syntactic sugar for enqueuing a task which calls a function in this class.
+   *
+   * The task is weighted so that it is processed
+   * as part of the currently-pending revision.
+   *
+   * After passing the $funcName, you can also pass parameters that will go to
+   * the function. Note that all params must be serializable.
+   */
+  public function addTask($title) {
+    $args = func_get_args();
+    $title = array_shift($args);
+    $task = new CRM_Queue_Task(
+      [get_class($this), '_queueAdapter'],
+      $args,
+      $title
+    );
+    return $this->queue->createItem($task, ['weight' => -1]);
+  }
+
+  // ******** Revision-tracking helpers ********
+
+  /**
+   * Determine if there are any pending revisions.
+   *
+   * @return bool
+   */
+  public function hasPendingRevisions() {
+    $revisions = $this->getRevisions();
+    $currentRevision = $this->getCurrentRevision();
+
+    if (empty($revisions)) {
+      return FALSE;
+    }
+    if (empty($currentRevision)) {
+      return TRUE;
+    }
+
+    return ($currentRevision < max($revisions));
+  }
+
+  /**
+   * Add any pending revisions to the queue.
+   */
+  public function enqueuePendingRevisions(CRM_Queue_Queue $queue) {
+    $this->queue = $queue;
+
+    $currentRevision = $this->getCurrentRevision();
+    foreach ($this->getRevisions() as $revision) {
+      if ($revision > $currentRevision) {
+        $title = E::ts('Upgrade %1 to revision %2', [
+          1 => $this->extensionName,
+          2 => $revision,
+        ]);
+
+        // note: don't use addTask() because it sets weight=-1
+
+        $task = new CRM_Queue_Task(
+          [get_class($this), '_queueAdapter'],
+          ['upgrade_' . $revision],
+          $title
+        );
+        $this->queue->createItem($task);
+
+        $task = new CRM_Queue_Task(
+          [get_class($this), '_queueAdapter'],
+          ['setCurrentRevision', $revision],
+          $title
+        );
+        $this->queue->createItem($task);
+      }
+    }
+  }
+
+  /**
+   * Get a list of revisions.
+   *
+   * @return array(revisionNumbers) sorted numerically
+   */
+  public function getRevisions() {
+    if (!is_array($this->revisions)) {
+      $this->revisions = [];
+
+      $clazz = new ReflectionClass(get_class($this));
+      $methods = $clazz->getMethods();
+      foreach ($methods as $method) {
+        if (preg_match('/^upgrade_(.*)/', $method->name, $matches)) {
+          $this->revisions[] = $matches[1];
+        }
+      }
+      sort($this->revisions, SORT_NUMERIC);
+    }
+
+    return $this->revisions;
+  }
+
+  public function getCurrentRevision() {
+    $revision = CRM_Core_BAO_Extension::getSchemaVersion($this->extensionName);
+    if (!$revision) {
+      $revision = $this->getCurrentRevisionDeprecated();
+    }
+    return $revision;
+  }
+
+  private function getCurrentRevisionDeprecated() {
+    $key = $this->extensionName . ':version';
+    if ($revision = \Civi::settings()->get($key)) {
+      $this->revisionStorageIsDeprecated = TRUE;
+    }
+    return $revision;
+  }
+
+  public function setCurrentRevision($revision) {
+    CRM_Core_BAO_Extension::setSchemaVersion($this->extensionName, $revision);
+    // clean up legacy schema version store (CRM-19252)
+    $this->deleteDeprecatedRevision();
+    return TRUE;
+  }
+
+  private function deleteDeprecatedRevision() {
+    if ($this->revisionStorageIsDeprecated) {
+      $setting = new CRM_Core_BAO_Setting();
+      $setting->name = $this->extensionName . ':version';
+      $setting->delete();
+      CRM_Core_Error::debug_log_message("Migrated extension schema revision ID for {$this->extensionName} from civicrm_setting (deprecated) to civicrm_extension.\n");
+    }
+  }
+
+  // ******** Hook delegates ********
+
+  /**
+   * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install
+   */
+  public function onInstall() {
+    $files = glob($this->extensionDir . '/sql/*_install.sql');
+    if (is_array($files)) {
+      foreach ($files as $file) {
+        CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file);
+      }
+    }
+    $files = glob($this->extensionDir . '/sql/*_install.mysql.tpl');
+    if (is_array($files)) {
+      foreach ($files as $file) {
+        $this->executeSqlTemplate($file);
+      }
+    }
+    $files = glob($this->extensionDir . '/xml/*_install.xml');
+    if (is_array($files)) {
+      foreach ($files as $file) {
+        $this->executeCustomDataFileByAbsPath($file);
+      }
+    }
+    if (is_callable([$this, 'install'])) {
+      $this->install();
+    }
+  }
+
+  /**
+   * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
+   */
+  public function onPostInstall() {
+    $revisions = $this->getRevisions();
+    if (!empty($revisions)) {
+      $this->setCurrentRevision(max($revisions));
+    }
+    if (is_callable([$this, 'postInstall'])) {
+      $this->postInstall();
+    }
+  }
+
+  /**
+   * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
+   */
+  public function onUninstall() {
+    $files = glob($this->extensionDir . '/sql/*_uninstall.mysql.tpl');
+    if (is_array($files)) {
+      foreach ($files as $file) {
+        $this->executeSqlTemplate($file);
+      }
+    }
+    if (is_callable([$this, 'uninstall'])) {
+      $this->uninstall();
+    }
+    $files = glob($this->extensionDir . '/sql/*_uninstall.sql');
+    if (is_array($files)) {
+      foreach ($files as $file) {
+        CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file);
+      }
+    }
+  }
+
+  /**
+   * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable
+   */
+  public function onEnable() {
+    // stub for possible future use
+    if (is_callable([$this, 'enable'])) {
+      $this->enable();
+    }
+  }
+
+  /**
+   * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
+   */
+  public function onDisable() {
+    // stub for possible future use
+    if (is_callable([$this, 'disable'])) {
+      $this->disable();
+    }
+  }
+
+  public function onUpgrade($op, CRM_Queue_Queue $queue = NULL) {
+    switch ($op) {
+      case 'check':
+        return [$this->hasPendingRevisions()];
+
+      case 'enqueue':
+        return $this->enqueuePendingRevisions($queue);
+
+      default:
+    }
+  }
+
+}
diff --git a/civicrm/ext/search/README.md b/civicrm/ext/search/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..456e59f4f8e17d5dd01f5ac4e5549c124e2f6303
--- /dev/null
+++ b/civicrm/ext/search/README.md
@@ -0,0 +1,7 @@
+# org.civicrm.search
+
+A core extension to create advanced searches.
+
+## Usage
+
+Once enabled, navigate to **Search > Create Search...** in the menu.
diff --git a/civicrm/ext/search/ang/search.ang.php b/civicrm/ext/search/ang/search.ang.php
new file mode 100644
index 0000000000000000000000000000000000000000..67275088f1f9520b0320ea0f89665c2224f0f026
--- /dev/null
+++ b/civicrm/ext/search/ang/search.ang.php
@@ -0,0 +1,17 @@
+<?php
+// Autoloader data for search builder.
+return [
+  'js' => [
+    'ang/*.js',
+    'ang/search/*.js',
+    'ang/search/*/*.js',
+  ],
+  'css' => [
+    'css/*.css',
+  ],
+  'partials' => [
+    'ang/search',
+  ],
+  'basePages' => [],
+  'requires' => ['crmUi', 'crmUtil', 'ngRoute', 'crmRouteBinder', 'ui.sortable', 'ui.bootstrap', 'dialogService', 'api4'],
+];
diff --git a/civicrm/ext/search/ang/search.module.js b/civicrm/ext/search/ang/search.module.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b2eeb34ffe1545bd93824d8613bc54faaa0e7cc
--- /dev/null
+++ b/civicrm/ext/search/ang/search.module.js
@@ -0,0 +1,95 @@
+(function(angular, $, _) {
+  "use strict";
+
+  // Shared between router and searchMeta service
+  var searchEntity;
+
+  // Declare module and route/controller/services
+  angular.module('search', CRM.angRequires('search'))
+
+    .config(function($routeProvider) {
+      $routeProvider.when('/:entity', {
+        controller: 'searchRoute',
+        template: '<div id="bootstrap-theme" class="crm-search"><crm-search entity="entity"></crm-search></div>',
+        reloadOnSearch: false
+      });
+    })
+
+    // Controller binds entity to route
+    .controller('searchRoute', function($scope, $routeParams, $location) {
+      searchEntity = $scope.entity = $routeParams.entity;
+
+      // Changing entity will refresh the angular page
+      $scope.$watch('entity', function(newEntity, oldEntity) {
+        if (newEntity && oldEntity && newEntity !== oldEntity) {
+          $location.url('/' + newEntity);
+        }
+      });
+    })
+
+    .factory('searchMeta', function() {
+      function getEntity(entityName) {
+        if (entityName) {
+          entityName = entityName === true ? searchEntity : entityName;
+          return _.find(CRM.vars.search.schema, {name: entityName});
+        }
+      }
+      function getField(name) {
+        var dotSplit = name.split('.'),
+          joinEntity = dotSplit.length > 1 ? dotSplit[0] : null,
+          fieldName = _.last(dotSplit).split(':')[0],
+          entityName = searchEntity;
+        // Custom fields contain a dot in their fieldname
+        // If 3 segments, the first is the joinEntity and the last 2 are the custom field
+        if (dotSplit.length === 3) {
+          fieldName = dotSplit[1] + '.' + fieldName;
+        }
+        // If 2 segments, it's ambiguous whether this is a custom field or joined field. Search the main entity first.
+        if (dotSplit.length === 2) {
+          var field = _.find(getEntity(true).fields, {name: dotSplit[0] + '.' + fieldName});
+          if (field) {
+            return field;
+          }
+        }
+        if (joinEntity) {
+          entityName = _.find(CRM.vars.search.links[entityName], {alias: joinEntity}).entity;
+        }
+        return _.find(getEntity(entityName).fields, {name: fieldName});
+      }
+      return {
+        getEntity: getEntity,
+        getField: getField,
+        parseExpr: function(expr) {
+          var result = {},
+            fieldName = expr,
+            bracketPos = expr.indexOf('(');
+          if (bracketPos >= 0) {
+            fieldName = expr.match(/[A-Z( _]*([\w.:]+)/)[1];
+            result.fn = _.find(CRM.vars.search.functions, {name: expr.substring(0, bracketPos)});
+          }
+          result.field = getField(fieldName);
+          var split = fieldName.split(':'),
+            prefixPos = split[0].lastIndexOf(result.field.name);
+          result.path = split[0];
+          result.prefix = prefixPos > 0 ? result.path.substring(0, prefixPos) : '';
+          result.suffix = !split[1] ? '' : ':' + split[1];
+          return result;
+        }
+      };
+    })
+
+    // Reformat an array of objects for compatibility with select2
+    // Todo this probably belongs in core
+    .factory('formatForSelect2', function() {
+      return function(input, key, label, extra) {
+        return _.transform(input, function(result, item) {
+          var formatted = {id: item[key], text: item[label]};
+          if (extra) {
+            _.merge(formatted, _.pick(item, extra));
+          }
+          result.push(formatted);
+        }, []);
+      };
+    });
+
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search/ang/search/SaveSmartGroup.ctrl.js b/civicrm/ext/search/ang/search/SaveSmartGroup.ctrl.js
new file mode 100644
index 0000000000000000000000000000000000000000..74401b984731c0a4b3d81a71216e28d0346c18ac
--- /dev/null
+++ b/civicrm/ext/search/ang/search/SaveSmartGroup.ctrl.js
@@ -0,0 +1,54 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('search').controller('SaveSmartGroup', function ($scope, crmApi4, dialogService) {
+    var ts = $scope.ts = CRM.ts(),
+      model = $scope.model;
+    $scope.groupEntityRefParams = {
+      entity: 'Group',
+      api: {
+        params: {is_hidden: 0, is_active: 1, 'saved_search_id.api_entity': model.entity},
+        extra: ['saved_search_id', 'description', 'visibility', 'group_type']
+      },
+      select: {
+        allowClear: true,
+        minimumInputLength: 0,
+        placeholder: ts('Select existing group')
+      }
+    };
+    if (!CRM.checkPerm('administer reserved groups')) {
+      $scope.groupEntityRefParams.api.params.is_reserved = 0;
+    }
+    $scope.perm = {
+      administerReservedGroups: CRM.checkPerm('administer reserved groups')
+    };
+    $scope.groupFields = _.indexBy(_.find(CRM.vars.search.schema, {name: 'Group'}).fields, 'name');
+    $scope.$watch('model.id', function (id) {
+      if (id) {
+        _.assign(model, $('#api-save-search-select-group').select2('data').extra);
+      }
+    });
+    $scope.cancel = function () {
+      dialogService.cancel('saveSearchDialog');
+    };
+    $scope.save = function () {
+      $('.ui-dialog:visible').block();
+      var group = model.id ? {id: model.id} : {title: model.title};
+      group.description = model.description;
+      group.visibility = model.visibility;
+      group.group_type = model.group_type;
+      group.saved_search_id = '$id';
+      var savedSearch = {
+        api_entity: model.entity,
+        api_params: model.params
+      };
+      if (group.id) {
+        savedSearch.id = model.saved_search_id;
+      }
+      crmApi4('SavedSearch', 'save', {records: [savedSearch], chain: {group: ['Group', 'save', {'records': [group]}]}})
+        .then(function (result) {
+          dialogService.close('saveSearchDialog', result[0]);
+        });
+    };
+  });
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search/ang/search/crmSearch.component.js b/civicrm/ext/search/ang/search/crmSearch.component.js
new file mode 100644
index 0000000000000000000000000000000000000000..5eaaee5ababb199f1894377a02c94cf093956f3b
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearch.component.js
@@ -0,0 +1,580 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('search').component('crmSearch', {
+    bindings: {
+      entity: '='
+    },
+    templateUrl: '~/search/crmSearch.html',
+    controller: function($scope, $element, $timeout, crmApi4, dialogService, searchMeta, formatForSelect2) {
+      var ts = $scope.ts = CRM.ts(),
+        ctrl = this;
+
+      this.DEFAULT_AGGREGATE_FN = 'GROUP_CONCAT';
+
+      this.selectedRows = [];
+      this.limit = CRM.cache.get('searchPageSize', 30);
+      this.page = 1;
+      this.params = {};
+      // After a search this.results is an object of result arrays keyed by page,
+      // Prior to searching it's an empty string because 1: falsey and 2: doesn't throw an error if you try to access undefined properties
+      this.results = '';
+      this.rowCount = false;
+      // Have the filters (WHERE, HAVING, GROUP BY, JOIN) changed?
+      this.stale = true;
+      this.allRowsSelected = false;
+
+      $scope.controls = {};
+      $scope.joinTypes = [{k: false, v: ts('Optional')}, {k: true, v: ts('Required')}];
+      $scope.entities = formatForSelect2(CRM.vars.search.schema, 'name', 'title', ['description', 'icon']);
+      this.perm = {
+        editGroups: CRM.checkPerm('edit groups')
+      };
+
+      this.getEntity = searchMeta.getEntity;
+
+      this.paramExists = function(param) {
+        return _.includes(searchMeta.getEntity(ctrl.entity).params, param);
+      };
+
+      $scope.getJoinEntities = function() {
+        var joinEntities = _.transform(CRM.vars.search.links[ctrl.entity], function(joinEntities, link) {
+          var entity = searchMeta.getEntity(link.entity);
+          if (entity) {
+            joinEntities.push({
+              id: link.entity + ' AS ' + link.alias,
+              text: entity.title,
+              description: '(' + link.alias + ')',
+              icon: entity.icon
+            });
+          }
+        }, []);
+        return {results: joinEntities};
+      };
+
+      $scope.addJoin = function() {
+        // Debounce the onchange event using timeout
+        $timeout(function() {
+          if ($scope.controls.join) {
+            ctrl.params.join = ctrl.params.join || [];
+            ctrl.params.join.push([$scope.controls.join, false]);
+            loadFieldOptions();
+          }
+          $scope.controls.join = '';
+        });
+      };
+
+      $scope.changeJoin = function(idx) {
+        if (ctrl.params.join[idx][0]) {
+          ctrl.params.join[idx].length = 2;
+          loadFieldOptions();
+        } else {
+          ctrl.clearParam('join', idx);
+        }
+      };
+
+      $scope.changeGroupBy = function(idx) {
+        if (!ctrl.params.groupBy[idx]) {
+          ctrl.clearParam('groupBy', idx);
+        }
+        // Remove aggregate functions when no grouping
+        if (!ctrl.params.groupBy.length) {
+          _.each(ctrl.params.select, function(col, pos) {
+            if (_.contains(col, '(')) {
+              var info = searchMeta.parseExpr(col);
+              if (info.fn.category === 'aggregate') {
+                ctrl.params.select[pos] = info.path + info.suffix;
+              }
+            }
+          });
+        }
+      };
+
+      /**
+       * Called when clicking on a column header
+       * @param col
+       * @param $event
+       */
+      $scope.setOrderBy = function(col, $event) {
+        var dir = $scope.getOrderBy(col) === 'fa-sort-asc' ? 'DESC' : 'ASC';
+        if (!$event.shiftKey) {
+          ctrl.params.orderBy = {};
+        }
+        ctrl.params.orderBy[col] = dir;
+        if (ctrl.results) {
+          ctrl.refreshPage();
+        }
+      };
+
+      /**
+       * Returns crm-i icon class for a sortable column
+       * @param col
+       * @returns {string}
+       */
+      $scope.getOrderBy = function(col) {
+        var dir = ctrl.params.orderBy && ctrl.params.orderBy[col];
+        if (dir) {
+          return 'fa-sort-' + dir.toLowerCase();
+        }
+        return 'fa-sort disabled';
+      };
+
+      $scope.addParam = function(name) {
+        if ($scope.controls[name] && !_.contains(ctrl.params[name], $scope.controls[name])) {
+          ctrl.params[name].push($scope.controls[name]);
+          if (name === 'groupBy') {
+            // Expand the aggregate block
+            $timeout(function() {
+              $('#crm-search-build-group-aggregate.collapsed .collapsible-title').click();
+            }, 10);
+          }
+        }
+        $scope.controls[name] = '';
+      };
+
+      // Deletes an item from an array param
+      this.clearParam = function(name, idx) {
+        ctrl.params[name].splice(idx, 1);
+      };
+
+      // Prevent visual jumps in results table height during loading
+      function lockTableHeight() {
+        var $table = $('.crm-search-results', $element);
+        $table.css('height', $table.height());
+      }
+
+      function unlockTableHeight() {
+        $('.crm-search-results', $element).css('height', '');
+      }
+
+      // Ensure all non-grouped columns are aggregated if using GROUP BY
+      function aggregateGroupByColumns() {
+        if (ctrl.params.groupBy.length) {
+          _.each(ctrl.params.select, function(col, pos) {
+            if (!_.contains(col, '(') && ctrl.canAggregate(col)) {
+              ctrl.params.select[pos] = ctrl.DEFAULT_AGGREGATE_FN + '(' + col + ')';
+            }
+          });
+        }
+      }
+
+      // Debounced callback for loadResults
+      function _loadResultsCallback() {
+        // Multiply limit to read 2 pages at once & save ajax requests
+        var params = angular.merge({debug: true, limit: ctrl.limit * 2}, ctrl.params);
+        lockTableHeight();
+        $scope.error = false;
+        if (ctrl.stale) {
+          ctrl.page = 1;
+          ctrl.rowCount = false;
+        }
+        if (ctrl.rowCount === false) {
+          params.select.push('row_count');
+        }
+        params.offset = ctrl.limit * (ctrl.page - 1);
+        crmApi4(ctrl.entity, 'get', params).then(function(success) {
+          if (ctrl.stale) {
+            ctrl.results = {};
+          }
+          if (ctrl.rowCount === false) {
+            ctrl.rowCount = success.count;
+          }
+          ctrl.debug = success.debug;
+          // populate this page & the next
+          ctrl.results[ctrl.page] = success.slice(0, ctrl.limit);
+          if (success.length > ctrl.limit) {
+            ctrl.results[ctrl.page + 1] = success.slice(ctrl.limit);
+          }
+          $scope.loading = false;
+          ctrl.stale = false;
+          unlockTableHeight();
+        }, function(error) {
+          $scope.loading = false;
+          ctrl.results = {};
+          ctrl.stale = true;
+          ctrl.debug = error.debug;
+          $scope.error = errorMsg(error);
+        })
+          .finally(function() {
+            if (ctrl.debug) {
+              ctrl.debug.params = JSON.stringify(ctrl.params, null, 2);
+            }
+          });
+      }
+
+      var _loadResults = _.debounce(_loadResultsCallback, 250);
+
+      function loadResults() {
+        $scope.loading = true;
+        aggregateGroupByColumns();
+        _loadResults();
+      }
+
+      // What to tell the user when search returns an error from the server
+      // Todo: parse error codes and give helpful feedback.
+      function errorMsg(error) {
+        return ts('Ensure all search critera are set correctly and try again.');
+      }
+
+      this.changePage = function() {
+        if (ctrl.stale || !ctrl.results[ctrl.page]) {
+          lockTableHeight();
+          loadResults();
+        }
+      };
+
+      this.refreshAll = function() {
+        ctrl.stale = true;
+        ctrl.selectedRows.length = 0;
+        loadResults();
+      };
+
+      // Refresh results while staying on current page.
+      this.refreshPage = function() {
+        lockTableHeight();
+        ctrl.results = {};
+        loadResults();
+      };
+
+      $scope.onClickSearch = function() {
+        if (ctrl.autoSearch) {
+          ctrl.autoSearch = false;
+        } else {
+          ctrl.refreshAll();
+        }
+      };
+
+      $scope.onClickAuto = function() {
+        ctrl.autoSearch = !ctrl.autoSearch;
+        if (ctrl.autoSearch && ctrl.stale) {
+          ctrl.refreshAll();
+        }
+        $('.crm-search-auto-toggle').blur();
+      };
+
+      $scope.onChangeLimit = function() {
+        // Refresh only if search has already been run
+        if (ctrl.autoSearch || ctrl.results) {
+          // Save page size in localStorage
+          CRM.cache.set('searchPageSize', ctrl.limit);
+          ctrl.refreshAll();
+        }
+      };
+
+      function onChangeSelect(newSelect, oldSelect) {
+        // When removing a column from SELECT, also remove from ORDER BY
+        _.each(_.difference(_.keys(ctrl.params.orderBy), newSelect), function(col) {
+          delete ctrl.params.orderBy[col];
+        });
+        // Re-arranging or removing columns doesn't merit a refresh, only adding columns does
+        if (!oldSelect || _.difference(newSelect, oldSelect).length) {
+          if (ctrl.autoSearch) {
+            ctrl.refreshPage();
+          } else {
+            ctrl.stale = true;
+          }
+        }
+      }
+
+      function onChangeFilters() {
+        ctrl.stale = true;
+        ctrl.selectedRows.length = 0;
+        if (ctrl.autoSearch) {
+          ctrl.refreshAll();
+        }
+      }
+
+      $scope.selectAllRows = function() {
+        // Deselect all
+        if (ctrl.allRowsSelected) {
+          ctrl.allRowsSelected = false;
+          ctrl.selectedRows.length = 0;
+          return;
+        }
+        // Select all
+        ctrl.allRowsSelected = true;
+        if (ctrl.page === 1 && ctrl.results[1].length < ctrl.limit) {
+          ctrl.selectedRows = _.pluck(ctrl.results[1], 'id');
+          return;
+        }
+        // If more than one page of results, use ajax to fetch all ids
+        $scope.loadingAllRows = true;
+        var params = _.cloneDeep(ctrl.params);
+        params.select = ['id'];
+        crmApi4(ctrl.entity, 'get', params, ['id']).then(function(ids) {
+          $scope.loadingAllRows = false;
+          ctrl.selectedRows = _.toArray(ids);
+        });
+      };
+
+      $scope.selectRow = function(row) {
+        var index = ctrl.selectedRows.indexOf(row.id);
+        if (index < 0) {
+          ctrl.selectedRows.push(row.id);
+          ctrl.allRowsSelected = (ctrl.rowCount === ctrl.selectedRows.length);
+        } else {
+          ctrl.allRowsSelected = false;
+          ctrl.selectedRows.splice(index, 1);
+        }
+      };
+
+      $scope.isRowSelected = function(row) {
+        return ctrl.allRowsSelected || _.includes(ctrl.selectedRows, row.id);
+      };
+
+      this.getFieldLabel = function(col) {
+        var info = searchMeta.parseExpr(col),
+          label = info.field.label;
+        if (info.fn) {
+          label = '(' + info.fn.title + ') ' + label;
+        }
+        return label;
+      };
+
+      // Is a column eligible to use an aggregate function?
+      this.canAggregate = function(col) {
+        // If the column is used for a groupBy, no
+        if (ctrl.params.groupBy.indexOf(col) > -1) {
+          return false;
+        }
+        // If the entity this column belongs to is being grouped by id, then also no
+        var info = searchMeta.parseExpr(col);
+        return ctrl.params.groupBy.indexOf(info.prefix + 'id') < 0;
+      };
+
+      $scope.formatResult = function formatResult(row, col) {
+        var info = searchMeta.parseExpr(col),
+          key = info.fn ? (info.fn.name + ':' + info.path + info.suffix) : col,
+          value = row[key];
+        // Handle grouped results
+        if (info.fn && info.fn.name === 'GROUP_CONCAT' && value) {
+          return formatGroupConcatValues(info, value);
+        }
+        else if (info.fn && info.fn.name === 'COUNT') {
+          return value;
+        }
+        return formatFieldValue(info.field, value);
+      };
+
+      function formatFieldValue(field, value) {
+        var type = field.data_type;
+        if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) {
+          return CRM.utils.formatDate(value, null, type === 'Timestamp');
+        }
+        else if (type === 'Boolean' && typeof value === 'boolean') {
+          return value ? ts('Yes') : ts('No');
+        }
+        else if (type === 'Money') {
+          return CRM.formatMoney(value);
+        }
+        if (_.isArray(value)) {
+          return value.join(', ');
+        }
+        return value;
+      }
+
+      function formatGroupConcatValues(info, values) {
+        return _.transform(values.split(','), function(result, val) {
+          if (info.field.options && !info.suffix) {
+            result.push(_.result(getOption(info.field, val), 'label'));
+          } else {
+            result.push(formatFieldValue(info.field, val));
+          }
+        }).join(', ');
+      }
+
+      function getOption(field, value) {
+        return _.find(field.options, function(option) {
+          // Type coersion is intentional
+          return option.id == value;
+        });
+      }
+
+      $scope.fieldsForGroupBy = function() {
+        return {results: getAllFields('', function(key) {
+            return _.contains(ctrl.params.groupBy, key);
+          })
+        };
+      };
+
+      $scope.fieldsForSelect = function() {
+        return {results: getAllFields(':label', function(key) {
+            return _.contains(ctrl.params.select, key);
+          })
+        };
+      };
+
+      $scope.fieldsForWhere = function() {
+        return {results: getAllFields(':name', _.noop)};
+      };
+
+      $scope.fieldsForHaving = function() {
+        return {results: _.transform(ctrl.params.select, function(fields, name) {
+          fields.push({id: name, text: ctrl.getFieldLabel(name)});
+        })};
+      };
+
+      function getDefaultSelect() {
+        return _.filter(['id', 'display_name', 'label', 'title', 'location_type_id:label'], searchMeta.getField);
+      }
+
+      function getAllFields(suffix, disabledIf) {
+        function formatFields(entityName, prefix) {
+          return _.transform(searchMeta.getEntity(entityName).fields, function(result, field) {
+            var item = {
+              id: prefix + field.name + (field.options ? suffix : ''),
+              text: field.label,
+              description: field.description
+            };
+            if (disabledIf(item.id)) {
+              item.disabled = true;
+            }
+            result.push(item);
+          }, []);
+        }
+
+        var mainEntity = searchMeta.getEntity(ctrl.entity),
+          result = [{
+            text: mainEntity.title,
+            icon: mainEntity.icon,
+            children: formatFields(ctrl.entity, '')
+          }];
+        _.each(ctrl.params.join, function(join) {
+          var joinName = join[0].split(' AS '),
+            joinEntity = searchMeta.getEntity(joinName[0]);
+          result.push({
+            text: joinEntity.title + ' (' + joinName[1] + ')',
+            icon: joinEntity.icon,
+            children: formatFields(joinEntity.name, joinName[1] + '.')
+          });
+        });
+        return result;
+      }
+
+      /**
+       * Fetch pseudoconstants for main entity + joined entities
+       *
+       * Sets an optionsLoaded property on each entity to avoid duplicate requests
+       */
+      function loadFieldOptions() {
+        var mainEntity = searchMeta.getEntity(ctrl.entity),
+          entities = {};
+
+        function enqueue(entity) {
+          entity.optionsLoaded = false;
+          entities[entity.name] = [entity.name, 'getFields', {
+            loadOptions: CRM.vars.search.loadOptions,
+            where: [['options', '!=', false]],
+            select: ['options']
+          }, {name: 'options'}];
+        }
+
+        if (typeof mainEntity.optionsLoaded === 'undefined') {
+          enqueue(mainEntity);
+        }
+        _.each(ctrl.params.join, function(join) {
+          var joinName = join[0].split(' AS '),
+            joinEntity = searchMeta.getEntity(joinName[0]);
+          if (typeof joinEntity.optionsLoaded === 'undefined') {
+            enqueue(joinEntity);
+          }
+        });
+        if (!_.isEmpty(entities)) {
+          crmApi4(entities).then(function(results) {
+            _.each(results, function(fields, entityName) {
+              var entity = searchMeta.getEntity(entityName);
+              _.each(fields, function(options, fieldName) {
+                _.find(entity.fields, {name: fieldName}).options = options;
+              });
+              entity.optionsLoaded = true;
+            });
+          });
+        }
+      }
+
+      this.$onInit = function() {
+        $scope.$bindToRoute({
+          expr: '$ctrl.params.select',
+          param: 'select',
+          format: 'json',
+          default: getDefaultSelect()
+        });
+        $scope.$watchCollection('$ctrl.params.select', onChangeSelect);
+
+        $scope.$bindToRoute({
+          expr: '$ctrl.params.orderBy',
+          param: 'orderBy',
+          format: 'json',
+          default: {}
+        });
+
+        $scope.$bindToRoute({
+          expr: '$ctrl.params.where',
+          param: 'where',
+          format: 'json',
+          default: [],
+          deep: true
+        });
+        $scope.$watch('$ctrl.params.where', onChangeFilters, true);
+
+        if (this.paramExists('groupBy')) {
+          $scope.$bindToRoute({
+            expr: '$ctrl.params.groupBy',
+            param: 'groupBy',
+            format: 'json',
+            default: []
+          });
+        }
+        $scope.$watchCollection('$ctrl.params.groupBy', onChangeFilters);
+
+        if (this.paramExists('join')) {
+          $scope.$bindToRoute({
+            expr: '$ctrl.params.join',
+            param: 'join',
+            format: 'json',
+            default: [],
+            deep: true
+          });
+        }
+        $scope.$watch('$ctrl.params.join', onChangeFilters, true);
+
+        if (this.paramExists('having')) {
+          $scope.$bindToRoute({
+            expr: '$ctrl.params.having',
+            param: 'having',
+            format: 'json',
+            default: [],
+            deep: true
+          });
+        }
+        $scope.$watch('$ctrl.params.having', onChangeFilters, true);
+
+        loadFieldOptions();
+      };
+
+      $scope.saveGroup = function() {
+        var selectField = ctrl.entity === 'Contact' ? 'id' : 'contact_id';
+        if (ctrl.entity !== 'Contact' && !searchMeta.getField('contact_id')) {
+          CRM.alert(ts('Cannot create smart group from %1.', {1: searchMeta.getEntity(true).title}), ts('Missing contact_id'), 'error', {expires: 5000});
+          return;
+        }
+        var model = {
+          title: '',
+          description: '',
+          visibility: 'User and User Admin Only',
+          group_type: [],
+          id: null,
+          entity: ctrl.entity,
+          params: angular.extend({}, ctrl.params, {version: 4, select: [selectField]})
+        };
+        delete model.params.orderBy;
+        var options = CRM.utils.adjustDialogDefaults({
+          autoOpen: false,
+          title: ts('Save smart group')
+        });
+        dialogService.open('saveSearchDialog', '~/search/saveSmartGroup.html', model, options);
+      };
+    }
+  });
+
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search/ang/search/crmSearch.html b/civicrm/ext/search/ang/search/crmSearch.html
new file mode 100644
index 0000000000000000000000000000000000000000..bd0feabcba10f045de80214f40fd56b556c5133d
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearch.html
@@ -0,0 +1,20 @@
+<div id="bootstrap-theme" class="crm-search">
+  <h1 crm-page-title>{{:: ts('Create Search for %1', {1: $ctrl.getEntity($ctrl.entity).title}) }}</h1>
+
+  <!--This warning will show if bootstrap is unavailable. Normally it will be hidden by the bootstrap .collapse class.-->
+  <div class="messages warning no-popup collapse">
+    <p>
+      <i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i>
+      <strong>{{:: ts('Bootstrap theme not found.') }}</strong>
+    </p>
+    <p>{{:: ts('This screen may not work correctly without a bootstrap-based theme such as Shoreditch installed.') }}</p>
+  </div>
+
+  <form>
+    <div ng-include="'~/search/crmSearch/criteria.html'"></div>
+    <div ng-include="'~/search/crmSearch/controls.html'"></div>
+    <div ng-include="'~/search/crmSearch/debug.html'" ng-if="$ctrl.debug"></div>
+    <div ng-include="'~/search/crmSearch/results.html'" class="crm-search-results"></div>
+    <div ng-include="'~/search/crmSearch/pager.html'"></div>
+  </form>
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearch/controls.html b/civicrm/ext/search/ang/search/crmSearch/controls.html
new file mode 100644
index 0000000000000000000000000000000000000000..c17724ba312736ae0c2b3439c17d96723b7d5c21
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearch/controls.html
@@ -0,0 +1,25 @@
+<hr>
+<div class="form-inline">
+  <div class="btn-group" role="group">
+    <button class="btn btn-primary{{ $ctrl.autoSearch ? '-outline' : '' }}" ng-click="onClickSearch()" ng-disabled="loading || (!$ctrl.autoSearch && !$ctrl.stale)">
+      <i class="crm-i {{ loading ? 'fa-spin fa-spinner' : 'fa-search' }}"></i>
+      {{:: ts('Search') }}
+    </button>
+    <button class="btn crm-search-auto-toggle btn-primary{{ $ctrl.autoSearch ? '' : '-outline' }}" ng-click="onClickAuto()">
+      <i class="crm-i fa-toggle-{{ $ctrl.autoSearch ? 'on' : 'off' }}"></i>
+      {{:: ts('Auto') }}
+    </button>
+  </div>
+  <crm-search-actions entity="$ctrl.entity" ids="$ctrl.selectedRows"></crm-search-actions>
+  <div class="btn-group pull-right">
+    <button type="button" class="btn form-control dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+      <i class="crm-i fa-save"></i> {{:: ts('Create')}}
+      <span class="caret"></span>
+    </button>
+    <ul class="dropdown-menu">
+      <li ng-if=":: $ctrl.perm.editGroups">
+        <a href ng-click="saveGroup()">{{:: ts('Smart Group') }}</a>
+      </li>
+    </ul>
+  </div>
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearch/criteria.html b/civicrm/ext/search/ang/search/crmSearch/criteria.html
new file mode 100644
index 0000000000000000000000000000000000000000..6bb500a7419fd798e69da25ab599c162deea0206
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearch/criteria.html
@@ -0,0 +1,48 @@
+<div class="crm-flex-box">
+  <div>
+    <div class="form-inline">
+      <label for="crm-search-main-entity">{{:: ts('Search for') }}</label>
+      <input id="crm-search-main-entity" class="form-control" ng-model="$ctrl.entity" crm-ui-select="::{allowClear: false, data: entities}" ng-change="changeEntity()" />
+    </div>
+    <div ng-if=":: $ctrl.paramExists('join')">
+      <fieldset ng-repeat="join in $ctrl.params.join">
+        <div class="form-inline">
+          <label for="crm-search-join-{{ $index }}">{{:: ts('With') }}</label>
+          <input id="crm-search-join-{{ $index }}" class="form-control" ng-model="join[0]" crm-ui-select="{placeholder: ' ', data: getJoinEntities}" ng-change="changeJoin($index)" />
+          <select class="form-control" ng-model="join[1]" ng-options="o.k as o.v for o in ::joinTypes" ></select>
+        </div>
+        <fieldset class="api4-clause-fieldset" crm-search-clause="{format: 'json', clauses: join, skip: 2, op: 'AND', label: ts('If'), fields: fieldsForWhere}">
+        </fieldset>
+      </fieldset>
+      <fieldset>
+        <div class="form-inline">
+          <input id="crm-search-add-join" class="form-control crm-action-menu fa-plus" ng-model="controls.join" crm-ui-select="{placeholder: ts('With'), data: getJoinEntities}" ng-change="addJoin()"/>
+        </div>
+      </fieldset>
+    </div>
+    <fieldset ng-if=":: $ctrl.paramExists('groupBy')">
+      <div class="form-inline" ng-repeat="groupBy in $ctrl.params.groupBy">
+        <label for="crm-search-groupBy-{{ $index }}">{{:: ts('Group By') }}</label>
+        <input id="crm-search-groupBy-{{ $index }}" class="form-control" ng-model="$ctrl.params.groupBy[$index]" crm-ui-select="{placeholder: ' ', data: fieldsForGroupBy}" ng-change="changeGroupBy($index)" />
+        <hr>
+      </div>
+      <div class="form-inline">
+        <input id="crm-search-add-groupBy" class="form-control crm-action-menu fa-plus" ng-model="controls.groupBy" crm-ui-select="{placeholder: ts('Group By'), data: fieldsForGroupBy}" ng-change="addParam('groupBy')"/>
+      </div>
+      <fieldset id="crm-search-build-group-aggregate" ng-if="$ctrl.params.groupBy.length" class="crm-collapsible collapsed">
+        <legend class="collapsible-title">{{:: ts('Aggregate fields') }}</legend>
+        <div>
+          <fieldset ng-repeat="col in $ctrl.params.select" ng-if="$ctrl.canAggregate(col)">
+            <crm-search-function expr="$ctrl.params.select[$index]" cat="'aggregate'"></crm-search-function>
+          </fieldset>
+        </div>
+      </fieldset>
+    </fieldset>
+  </div>
+  <div>
+    <fieldset class="api4-clause-fieldset" crm-search-clause="{format: 'string', clauses: $ctrl.params.where, op: 'AND', label: ts('Where'), fields: fieldsForWhere}">
+    </fieldset>
+    <fieldset ng-if="$ctrl.paramExists('having') && $ctrl.params.groupBy.length" class="api4-clause-fieldset" crm-search-clause="{format: 'string', clauses: $ctrl.params.having, op: 'AND', label: ts('Filter'), fields: fieldsForHaving}">
+    </fieldset>
+  </div>
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearch/debug.html b/civicrm/ext/search/ang/search/crmSearch/debug.html
new file mode 100644
index 0000000000000000000000000000000000000000..4bb483d1af9e43adbed8b77fde498a0dc777547a
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearch/debug.html
@@ -0,0 +1,10 @@
+<fieldset class="crm-collapsible collapsed">
+  <legend class="collapsible-title">{{:: ts('Query Info') }}</legend>
+  <div>
+    <pre ng-if="$ctrl.debug.timeIndex">{{ ts('Request took %1 seconds.', {1: $ctrl.debug.timeIndex}) }}</pre>
+    <div><strong>API:</strong></div>
+    <pre>{{ $ctrl.debug.params }}</pre>
+    <div><strong>SQL:</strong></div>
+    <pre ng-repeat="query in $ctrl.debug.sql">{{ query }}</pre>
+  </div>
+</fieldset>
diff --git a/civicrm/ext/search/ang/search/crmSearch/pager.html b/civicrm/ext/search/ang/search/crmSearch/pager.html
new file mode 100644
index 0000000000000000000000000000000000000000..2c4a2b4ba1ba1e54710278d9096f99326c70c45f
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearch/pager.html
@@ -0,0 +1,35 @@
+<div class="crm-flex-box">
+  <div>
+    <div class="form-inline">
+      <label ng-if="loading && $ctrl.rowCount === false"><i class="crm-i fa-spin fa-spinner"></i></label>
+      <label ng-if="$ctrl.rowCount === 1">
+        {{ $ctrl.selectedRows.length ? ts('%1 selected of 1 result', {1: $ctrl.selectedRows.length}) : ts('1 result') }}
+      </label>
+      <label ng-if="$ctrl.rowCount === 0 || $ctrl.rowCount > 1">
+        {{ $ctrl.selectedRows.length ? ts('%1 selected of %2 results', {1: $ctrl.selectedRows.length, 2: $ctrl.rowCount}) : ts('%1 results', {1: $ctrl.rowCount}) }}
+      </label>
+    </div>
+  </div>
+  <div class="text-center crm-flex-2">
+    <ul uib-pagination ng-if="$ctrl.rowCount && !$ctrl.stale"
+        class="pagination"
+        boundary-links="true"
+        total-items="$ctrl.rowCount"
+        ng-model="$ctrl.page"
+        ng-change="$ctrl.changePage()"
+        items-per-page="$ctrl.limit"
+        max-size="6"
+        force-ellipses="true"
+        previous-text="&lsaquo;"
+        next-text="&rsaquo;"
+        first-text="&laquo;"
+        last-text="&raquo;"
+    ></ul>
+  </div>
+  <div class="form-inline text-right">
+    <label for="crm-search-results-page-size" >
+      {{:: ts('Page size:') }}
+    </label>
+    <input class="form-control" id="crm-search-results-page-size" type="number" ng-model="$ctrl.limit" min="10" step="10" ng-change="onChangeLimit()">
+  </div>
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearch/results.html b/civicrm/ext/search/ang/search/crmSearch/results.html
new file mode 100644
index 0000000000000000000000000000000000000000..01b200207ccbcbad97ec25bbd6e4291d8dc56005
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearch/results.html
@@ -0,0 +1,32 @@
+<table>
+  <thead>
+    <tr ng-model="$ctrl.params.select" ui-sortable="{axis: 'x'}">
+      <th class="crm-search-result-select">
+        <input type="checkbox" ng-checked="$ctrl.allRowsSelected" ng-click="selectAllRows()" ng-disabled="!(loading === false && !loadingAllRows && $ctrl.results[$ctrl.page] && $ctrl.results[$ctrl.page][0].id)">
+      </th>
+      <th ng-repeat="col in $ctrl.params.select" ng-click="setOrderBy(col, $event)" title="{{:: ts('Drag to reorder columns, click to sort results (shift-click to sort by multiple).')}}">
+        <i class="crm-i {{ getOrderBy(col) }}"></i>
+        <span>{{ $ctrl.getFieldLabel(col) }}</span>
+        <a href class="crm-hover-button" title="{{:: ts('Clear') }}" ng-click="$ctrl.clearParam('select', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
+      </th>
+      <th class="form-inline">
+        <input class="form-control crm-action-menu fa-plus" ng-model="controls.select" crm-ui-select="::{data: fieldsForSelect, placeholder: ts('Add')}" ng-change="addParam('select')">
+      </th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr ng-repeat="row in $ctrl.results[$ctrl.page]">
+      <td>
+        <input type="checkbox" ng-checked="isRowSelected(row)" ng-click="selectRow(row)" ng-disabled="!(loading === false && !loadingAllRows && row.id)">
+      </td>
+      <td ng-repeat="col in $ctrl.params.select">
+        {{ formatResult(row, col) }}
+      </td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+<div class="messages warning no-popup" ng-if="error">
+  <h4>{{:: ts('An error occurred') }}</h4>
+  <p>{{ error }}</p>
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearchActions.component.js b/civicrm/ext/search/ang/search/crmSearchActions.component.js
new file mode 100644
index 0000000000000000000000000000000000000000..138cb089a9251a02a325c60ee5c8e6689fe34d96
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchActions.component.js
@@ -0,0 +1,62 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('search').component('crmSearchActions', {
+    bindings: {
+      entity: '<',
+      ids: '<'
+    },
+    require: {
+      search: '^crmSearch'
+    },
+    templateUrl: '~/search/crmSearchActions.html',
+    controller: function($scope, crmApi4, dialogService, searchMeta) {
+      var ts = $scope.ts = CRM.ts(),
+        entityTitle = searchMeta.getEntity(this.entity).title,
+        ctrl = this;
+
+      this.init = function() {
+        if (!ctrl.actions) {
+          var actions = _.transform(_.cloneDeep(CRM.vars.search.actions), function (actions, action) {
+            if (_.includes(action.entities, ctrl.entity)) {
+              action.title = action.title.replace('%1', entityTitle);
+              actions.push(action);
+            }
+          }, []);
+          ctrl.actions = _.sortBy(actions, 'title');
+        }
+      };
+
+      this.isActionAllowed = function(action) {
+        return !action.number || $scope.eval('' + $ctrl.ids.length + action.number);
+      };
+
+      this.doAction = function(action) {
+        if (!ctrl.isActionAllowed(action) || !ctrl.ids.length) {
+          return;
+        }
+        var data = {
+          ids: ctrl.ids,
+          entity: ctrl.entity
+        };
+        // If action uses a crmPopup form
+        if (action.crmPopup) {
+          var path = $scope.$eval(action.crmPopup.path, data),
+            query = action.crmPopup.query && $scope.$eval(action.crmPopup.query, data);
+          CRM.loadForm(CRM.url(path, query))
+            .on('crmFormSuccess', ctrl.search.refreshPage);
+        }
+        // If action uses dialogService
+        else if (action.uiDialog) {
+          var options = CRM.utils.adjustDialogDefaults({
+            autoOpen: false,
+            title: action.title
+          });
+          dialogService.open('crmSearchAction', action.uiDialog.templateUrl, data, options)
+            .then(ctrl.search.refreshPage);
+        }
+      };
+    }
+  });
+
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search/ang/search/crmSearchActions.html b/civicrm/ext/search/ang/search/crmSearchActions.html
new file mode 100644
index 0000000000000000000000000000000000000000..dbeae2c84c5eda08603315db2b667d15531583d0
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchActions.html
@@ -0,0 +1,10 @@
+<div class="btn-group" title="{{:: ts('Perform action on selected items.') }}">
+  <button type="button" ng-disabled="!$ctrl.ids.length" ng-click="$ctrl.init()" class="btn form-control dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+    {{:: ts('Action') }} <span class="caret"></span>
+  </button>
+  <ul class="dropdown-menu" ng-if=":: $ctrl.actions">
+    <li ng-disabled="!$ctrl.isActionAllowed(action)" ng-repeat="action in $ctrl.actions">
+      <a href ng-click="$ctrl.doAction(action)"><i class="fa {{action.icon}}"></i> {{ action.title }}</a>
+    </li>
+  </ul>
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.ctrl.js b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.ctrl.js
new file mode 100644
index 0000000000000000000000000000000000000000..28a401e65258d488bf47dabeadfac36db9d509ab
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.ctrl.js
@@ -0,0 +1,24 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('search').controller('crmSearchActionDelete', function($scope, crmApi4, dialogService, searchMeta) {
+    var ts = $scope.ts = CRM.ts(),
+      model = $scope.model,
+      ctrl = $scope.$ctrl = this;
+
+    this.entity = searchMeta.getEntity(model.entity);
+
+    this.cancel = function() {
+      dialogService.cancel('crmSearchAction');
+    };
+
+    this.delete = function() {
+      crmApi4(model.entity, 'Delete', {
+        where: [['id', 'IN', model.ids]],
+      }).then(function() {
+        dialogService.close('crmSearchAction');
+      });
+    };
+
+  });
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.html b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.html
new file mode 100644
index 0000000000000000000000000000000000000000..a8c95de8a207b9ed3d441c532e552589f52d7f69
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.html
@@ -0,0 +1,10 @@
+<div id="bootstrap-theme">
+  <div ng-controller="crmSearchActionDelete">
+    <p>{{:: ts('Are you sure you want to delete %1 %2?', {1: model.ids.length, 2: $ctrl.entity.title}) }}</p>
+    <hr />
+    <div class="buttons pull-right">
+      <button type="button" ng-click="$ctrl.cancel()" class="btn btn-danger">{{:: ts('Cancel') }}</button>
+      <button ng-click="$ctrl.delete()" class="btn btn-primary">{{:: ts('Delete %1 %2', {1: model.ids.length, 2: $ctrl.entity.title}) }}</button>
+    </div>
+  </div>
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.ctrl.js b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.ctrl.js
new file mode 100644
index 0000000000000000000000000000000000000000..63394a2427f6746e338a7f0e4393c1636cc7dacd
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.ctrl.js
@@ -0,0 +1,63 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('search').controller('crmSearchActionUpdate', function ($scope, $timeout, crmApi4, dialogService, searchMeta) {
+    var ts = $scope.ts = CRM.ts(),
+      model = $scope.model,
+      ctrl = $scope.$ctrl = this;
+
+    this.entity = searchMeta.getEntity(model.entity);
+    this.values = [];
+    this.add = null;
+
+    function fieldInUse(fieldName) {
+      return _.includes(_.collect(ctrl.values, 0), fieldName);
+    }
+
+    this.updateField = function(index) {
+      // Debounce the onchange event using timeout
+      $timeout(function() {
+        if (!ctrl.values[index][0]) {
+          ctrl.values.splice(index, 1);
+        }
+      });
+    };
+
+    this.addField = function() {
+      // Debounce the onchange event using timeout
+      $timeout(function() {
+        if (ctrl.add) {
+          ctrl.values.push([ctrl.add, '']);
+        }
+        ctrl.add = null;
+      });
+    };
+
+    this.availableFields = function() {
+      var results = _.transform(ctrl.entity.fields, function(result, item) {
+        var formatted = {id: item.name, text: item.title, description: item.description};
+        if (fieldInUse(item.name)) {
+          formatted.disabled = true;
+        }
+        if (item.name !== 'id') {
+          result.push(formatted);
+        }
+      }, []);
+      return {results: results};
+    };
+
+    this.cancel = function() {
+      dialogService.cancel('crmSearchAction');
+    };
+
+    this.save = function() {
+      crmApi4(model.entity, 'Update', {
+        where: [['id', 'IN', model.ids]],
+        values: _.zipObject(ctrl.values)
+      }).then(function() {
+        dialogService.close('crmSearchAction');
+      });
+    };
+
+  });
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.html b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.html
new file mode 100644
index 0000000000000000000000000000000000000000..63178a0baed16f857e05bcda12abba5cf2ddc74a
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.html
@@ -0,0 +1,16 @@
+<div id="bootstrap-theme">
+  <div ng-controller="crmSearchActionUpdate">
+    <div class="form-inline" ng-repeat="clause in $ctrl.values" >
+      <input class="form-control" ng-change="$ctrl.updateField($index)" ng-model="clause[0]" crm-ui-select="{data: $ctrl.availableFields, allowClear: true, placeholder: 'Field'}" />
+      <input class="form-control" ng-model="clause[1]" crm-search-value="{field: clause[0]}" />
+    </div>
+    <div class="form-inline">
+      <input class="form-control twenty" style="width: 15em;" ng-model="$ctrl.add" ng-change="$ctrl.addField()" crm-ui-select="{data: $ctrl.availableFields, placeholder: ts('Add Value')}"/>
+    </div>
+    <hr />
+    <div class="buttons pull-right">
+      <button type="button" ng-click="$ctrl.cancel()" class="btn btn-danger">{{:: ts('Cancel') }}</button>
+      <button ng-click="$ctrl.save()" class="btn btn-primary" ng-disabled="!$ctrl.values.length">{{:: ts('Update %1 %2', {1: model.ids.length, 2: $ctrl.entity.title}) }}</button>
+    </div>
+  </div>
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearchClause.directive.js b/civicrm/ext/search/ang/search/crmSearchClause.directive.js
new file mode 100644
index 0000000000000000000000000000000000000000..ab59aff12212233cd43d9f2408dfd0a6a48976bd
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchClause.directive.js
@@ -0,0 +1,75 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('search').directive('crmSearchClause', function() {
+    return {
+      scope: {
+        data: '<crmSearchClause'
+      },
+      templateUrl: '~/search/crmSearchClause.html',
+      controller: function ($scope, $element, $timeout) {
+        var ts = $scope.ts = CRM.ts();
+        var ctrl = $scope.$ctrl = this;
+        this.conjunctions = {AND: ts('And'), OR: ts('Or'), NOT: ts('Not')};
+        this.operators = CRM.vars.search.operators;
+        this.sortOptions = {
+          axis: 'y',
+          connectWith: '.api4-clause-group-sortable',
+          containment: $element.closest('.api4-clause-fieldset'),
+          over: onSortOver,
+          start: onSort,
+          stop: onSort
+        };
+
+        this.addGroup = function(op) {
+          $scope.data.clauses.push([op, []]);
+        };
+
+        this.removeGroup = function() {
+          $scope.data.groupParent.splice($scope.data.groupIndex, 1);
+        };
+
+        function onSort(event, ui) {
+          $($element).closest('.api4-clause-fieldset').toggleClass('api4-sorting', event.type === 'sortstart');
+          $('.api4-input.form-inline').css('margin-left', '');
+        }
+
+        // Indent clause while dragging between nested groups
+        function onSortOver(event, ui) {
+          var offset = 0;
+          if (ui.sender) {
+            offset = $(ui.placeholder).offset().left - $(ui.sender).offset().left;
+          }
+          $('.api4-input.form-inline.ui-sortable-helper').css('margin-left', '' + offset + 'px');
+        }
+
+        this.addClause = function() {
+          $timeout(function() {
+            if (ctrl.newClause) {
+              $scope.data.clauses.push([ctrl.newClause, '=', '']);
+              ctrl.newClause = null;
+            }
+          });
+        };
+        $scope.$watch('data.clauses', function(values) {
+          // Iterate in reverse order so index doesn't get out-of-sync during splice
+          _.forEachRight(values, function(clause, index) {
+            // Remove empty values
+            if (index >= ($scope.data.skip  || 0)) {
+              if (typeof clause !== 'undefined' && !clause[0]) {
+                values.splice(index, 1);
+              }
+              // Add/remove value if operator allows for one
+              else if (typeof clause[1] === 'string' && _.contains(clause[1], 'NULL')) {
+                clause.length = 2;
+              } else if (typeof clause[1] === 'string' && clause.length === 2) {
+                clause.push('');
+              }
+            }
+          });
+        }, true);
+      }
+    };
+  });
+
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search/ang/search/crmSearchClause.html b/civicrm/ext/search/ang/search/crmSearchClause.html
new file mode 100644
index 0000000000000000000000000000000000000000..97ef49621fadd2d233c3f3ff17b788c26d44a0ca
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchClause.html
@@ -0,0 +1,41 @@
+<legend>{{ data.label || ts('%1 group', {1: $ctrl.conjunctions[data.op]}) }}</legend>
+<div class="btn-group btn-group-xs" ng-if="data.groupParent">
+  <button class="btn btn-danger-outline" ng-click="$ctrl.removeGroup()" title="{{:: ts('Remove group') }}">
+    <i class="crm-i fa-trash" aria-hidden="true"></i>
+  </button>
+</div>
+<div class="api4-clause-group-sortable" ng-model="data.clauses" ui-sortable="$ctrl.sortOptions">
+  <div class="api4-input form-inline clearfix" ng-repeat="(index, clause) in data.clauses" ng-class="{hiddenElement: index &lt; (data.skip || 0)}">
+    <div ng-if="index &gt;= (data.skip || 0)">
+      <div class="api4-clause-badge" title="{{:: ts('Drag to reposition') }}">
+        <span class="badge badge-info">
+          <span ng-if="index === (data.skip || 0) && !data.groupParent">{{ data.label }}</span>
+          <span ng-if="index &gt; (data.skip || 0) || data.groupParent">{{ $ctrl.conjunctions[data.op] }}</span>
+          <i class="crm-i fa-arrows" aria-hidden="true"></i>
+        </span>
+      </div>
+      <div ng-if="!$ctrl.conjunctions[clause[0]]" class="api4-input-group">
+        <input class="form-control" ng-model="clause[0]" crm-ui-select="{data: data.fields, allowClear: true, placeholder: 'Field'}" />
+        <select class="form-control api4-operator" ng-model="clause[1]" ng-options="o.key as o.value for o in $ctrl.operators" ></select>
+        <input class="form-control" ng-model="clause[2]" crm-search-value="{field: clause[0], op: clause[1], format: data.format}" />
+      </div>
+      <fieldset class="clearfix" ng-if="$ctrl.conjunctions[clause[0]]" crm-search-clause="{format: data.format, clauses: clause[1], op: clause[0], fields: data.fields, groupParent: data.clauses, groupIndex: index}">
+      </fieldset>
+    </div>
+  </div>
+</div>
+<div class="api4-input form-inline">
+  <div class="api4-clause-badge">
+    <div class="btn-group btn-group-xs" title="{{ data.groupParent ? ts('Add a subgroup of clauses') : ts('Add a group of clauses') }}">
+      <button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+        {{ $ctrl.conjunctions[data.op] }} <span class="caret"></span>
+      </button>
+      <ul class="dropdown-menu api4-add-where-group-menu">
+        <li ng-repeat="(con, label) in $ctrl.conjunctions" ng-show="data.op !== con">
+          <a href ng-click="$ctrl.addGroup(con)">{{ label }}</a>
+        </li>
+      </ul>
+    </div>
+  </div>
+  <input class="form-control" ng-model="$ctrl.newClause" ng-change="$ctrl.addClause()" crm-ui-select="{data: data.fields, placeholder: ts('Select field')}" />
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearchFunction.component.js b/civicrm/ext/search/ang/search/crmSearchFunction.component.js
new file mode 100644
index 0000000000000000000000000000000000000000..e75bac9571480ed04dbe726c292bc093b35e6a94
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchFunction.component.js
@@ -0,0 +1,28 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('search').component('crmSearchFunction', {
+    bindings: {
+      expr: '=',
+      cat: '<'
+    },
+    templateUrl: '~/search/crmSearchFunction.html',
+    controller: function($scope, formatForSelect2, searchMeta) {
+      var ts = $scope.ts = CRM.ts(),
+        ctrl = this;
+      this.functions = formatForSelect2(_.where(CRM.vars.search.functions, {category: this.cat}), 'name', 'title');
+
+      this.$onInit = function() {
+        var fieldInfo = searchMeta.parseExpr(ctrl.expr);
+        ctrl.path = fieldInfo.path;
+        ctrl.field = fieldInfo.field;
+        ctrl.fn = !fieldInfo.fn ? '' : fieldInfo.fn.name;
+      };
+
+      this.selectFunction = function() {
+        ctrl.expr = ctrl.fn ? (ctrl.fn + '(' + ctrl.path + ')') : ctrl.path;
+      };
+    }
+  });
+
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search/ang/search/crmSearchFunction.html b/civicrm/ext/search/ang/search/crmSearchFunction.html
new file mode 100644
index 0000000000000000000000000000000000000000..e0692e33d3e4f420634adfaeec7b1fc1b9ed9d8c
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchFunction.html
@@ -0,0 +1,4 @@
+<div class="form-inline">
+  <label>{{ $ctrl.field.label }}:</label>
+  <input class="form-control" style="width: 15em;" ng-model="$ctrl.fn" crm-ui-select="{data: $ctrl.functions, placeholder: ts('Select')}" ng-change="$ctrl.selectFunction()">
+</div>
diff --git a/civicrm/ext/search/ang/search/crmSearchValue.directive.js b/civicrm/ext/search/ang/search/crmSearchValue.directive.js
new file mode 100644
index 0000000000000000000000000000000000000000..9e39cf1585b50059c563229efc931ae6253892a5
--- /dev/null
+++ b/civicrm/ext/search/ang/search/crmSearchValue.directive.js
@@ -0,0 +1,115 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('search').directive('crmSearchValue', function($interval, searchMeta, formatForSelect2) {
+    return {
+      scope: {
+        data: '=crmSearchValue'
+      },
+      require: 'ngModel',
+      link: function (scope, element, attrs, ngModel) {
+        var ts = scope.ts = CRM.ts(),
+          multi = _.includes(['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'], scope.data.op),
+          format = scope.data.format;
+
+        function destroyWidget() {
+          var $el = $(element);
+          if ($el.is('.crm-form-date-wrapper .crm-hidden-date')) {
+            $el.crmDatepicker('destroy');
+          }
+          if ($el.is('.select2-container + input')) {
+            $el.crmEntityRef('destroy');
+          }
+          $(element).removeData().removeAttr('type').removeAttr('placeholder').show();
+        }
+
+        function makeWidget(field, op, optionKey) {
+          var $el = $(element),
+            inputType = field.input_type,
+            dataType = field.data_type;
+          if (!op) {
+            op = field.serialize || dataType === 'Array' ? 'IN' : '=';
+          }
+          multi = _.includes(['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'], op);
+          if (op === 'IS NULL' || op === 'IS NOT NULL') {
+            $el.hide();
+            return;
+          }
+          if (inputType === 'Date') {
+            if (_.includes(['=', '!=', '>', '>=', '<', '<='], op)) {
+              $el.crmDatepicker({time: (field.input_attrs && field.input_attrs.time) || false});
+            }
+          } else if (_.includes(['=', '!=', 'IN', 'NOT IN', 'CONTAINS'], op) && (field.fk_entity || field.options || dataType === 'Boolean')) {
+            if (field.options) {
+              if (field.options === true) {
+                $el.addClass('loading');
+                var waitForOptions = $interval(function() {
+                  if (field.options !== true) {
+                    $interval.cancel(waitForOptions);
+                    $el.removeClass('loading').crmSelect2({data: getFieldOptions, multiple: multi});
+                  }
+                }, 200);
+              }
+              $el.attr('placeholder', ts('select')).crmSelect2({data: getFieldOptions, multiple: multi});
+            } else if (field.fk_entity) {
+              $el.crmEntityRef({entity: field.fk_entity, select:{multiple: multi}});
+            } else if (dataType === 'Boolean') {
+              $el.attr('placeholder', ts('- select -')).crmSelect2({allowClear: false, multiple: multi, placeholder: ts('- select -'), data: [
+                // FIXME: it would be more correct to use real true/false booleans instead of numbers, but select2 doesn't seem to like them
+                {id: 1, text: ts('Yes')},
+                {id: 0, text: ts('No')}
+              ]});
+            }
+          } else if (dataType === 'Integer' && !multi) {
+            $el.attr('type', 'number');
+          }
+
+          function getFieldOptions() {
+            return {results: formatForSelect2(field.options, optionKey, 'label', ['description', 'color', 'icon'])};
+          }
+        }
+
+        // Copied from ng-list but applied conditionally if field is multi-valued
+        var parseList = function(viewValue) {
+          // If the viewValue is invalid (say required but empty) it will be `undefined`
+          if (_.isUndefined(viewValue)) return;
+
+          if (!multi) {
+            return format === 'json' ? JSON.stringify(viewValue) : viewValue;
+          }
+
+          var list = [];
+
+          if (viewValue) {
+            _.each(viewValue.split(','), function(value) {
+              if (value) list.push(_.trim(value));
+            });
+          }
+
+          return list;
+        };
+
+        // Copied from ng-list
+        ngModel.$parsers.push(parseList);
+        ngModel.$formatters.push(function(value) {
+          return _.isArray(value) ? value.join(', ') : (format === 'json' && value !== '' ? JSON.parse(value) : value);
+        });
+
+        // Copied from ng-list
+        ngModel.$isEmpty = function(value) {
+          return !value || !value.length;
+        };
+
+        scope.$watchCollection('data', function(data) {
+          destroyWidget();
+          var field = searchMeta.parseExpr(data.field).field;
+          if (field) {
+            var optionKey = data.field.split(':')[1] || 'id';
+            makeWidget(field, data.op, optionKey);
+          }
+        });
+      }
+    };
+  });
+
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search/ang/search/saveSmartGroup.html b/civicrm/ext/search/ang/search/saveSmartGroup.html
new file mode 100644
index 0000000000000000000000000000000000000000..a0caf4af2c1938f9450379b9892db59c61c8a328
--- /dev/null
+++ b/civicrm/ext/search/ang/search/saveSmartGroup.html
@@ -0,0 +1,26 @@
+<form id="bootstrap-theme">
+  <div ng-controller="SaveSmartGroup">
+    <input class="form-control" id="api-save-search-select-group" ng-model="model.id" crm-entityref="groupEntityRefParams" >
+    <label ng-show="!model.id">{{:: ts('Or') }}</label>
+    <input class="form-control" placeholder="Create new group" ng-model="model.title" ng-show="!model.id">
+    <hr />
+    <label>{{:: ts('Description:') }}</label>
+    <textarea class="form-control" ng-model="model.description"></textarea>
+    <label>{{:: ts('Group Type:') }}</label>
+    <div class="form-inline">
+      <div class="checkbox" ng-repeat="option in groupFields.group_type.options track by option.id">
+        <label>
+          <input type="checkbox" checklist-model="model.group_type" checklist-value="option.id">
+          {{ option.label }}
+        </label>
+      </div>
+    </div>
+    <label>{{:: ts('Visibility:') }}</label>
+    <select class="form-control" ng-model="model.visibility" ng-options="item.id as item.label for item in groupFields.visibility.options track by item.id"></select>
+    <hr />
+    <div class="buttons pull-right">
+      <button type="button" ng-click="cancel()" class="btn btn-danger">{{:: ts('Cancel') }}</button>
+      <button ng-click="save()" class="btn btn-primary" ng-disabled="!model.title && !model.id">{{:: ts('Save') }}</button>
+    </div>
+  </div>
+</form>
diff --git a/civicrm/ext/search/css/search.css b/civicrm/ext/search/css/search.css
new file mode 100644
index 0000000000000000000000000000000000000000..554f1f724b6e1baca41e8183571ea5ee35a3e850
--- /dev/null
+++ b/civicrm/ext/search/css/search.css
@@ -0,0 +1,153 @@
+.crm-flex-box {
+  display: flex;
+}
+.crm-flex-box > * {
+  flex: 1;
+}
+.crm-flex-box > .crm-flex-2 {
+  flex: 2;
+}
+.crm-flex-box > .crm-flex-3 {
+  flex: 3;
+}
+.crm-flex-box > .crm-flex-4 {
+  flex: 4;
+}
+#bootstrap-theme #crm-search-results-page-size {
+  width: 5em;
+}
+#bootstrap-theme .crm-search-results {
+  min-height: 200px;
+}
+.crm-search-results thead th[ng-repeat] {
+  cursor: pointer;
+}
+.crm-search-results thead th[ng-repeat] > span {
+  cursor: move;
+}
+
+#bootstrap-theme.crm-search fieldset {
+  padding: 6px;
+  border-top: 1px solid lightgrey;
+  margin-top: 10px;
+  margin-bottom: 10px;
+  position: relative;
+}
+
+#bootstrap-theme.crm-search fieldset fieldset {
+  padding-top: 0;
+  border-left: 1px solid lightgrey;
+  border-right: 1px solid lightgrey;
+  border-bottom: 1px solid lightgrey;
+}
+
+#bootstrap-theme.crm-search fieldset legend {
+  background-color: white;
+  font-size: 13px;
+  margin: 0;
+  width: auto;
+  border: 0 none;
+  padding: 2px 5px;
+  text-transform: capitalize;
+}
+#bootstrap-theme.crm-search fieldset > .btn-group {
+  position: absolute;
+  right: 0;
+  top: 11px;
+}
+#bootstrap-theme.crm-search fieldset > .btn-group .btn {
+  border: 0 none;
+}
+
+#bootstrap-theme.crm-search fieldset div.api4-input {
+  margin-bottom: 10px;
+}
+
+#bootstrap-theme.crm-search fieldset div.api4-input.ui-sortable-helper {
+  background-color: rgba(255, 255, 255, .9);
+}
+
+#bootstrap-theme.crm-search fieldset div.api4-input.ui-sortable-helper {
+  background-color: rgba(255, 255, 255, .9);
+}
+
+#bootstrap-theme.crm-search div.api4-input.form-inline label.form-control {
+  margin-right: 6px;
+}
+#bootstrap-theme.crm-search div.api4-input.form-inline label.form-control input[type=checkbox] {
+  margin: 0 2px 0 0;
+}
+
+#bootstrap-theme.crm-search div.api4-input.form-inline label.form-control:not(.api4-option-selected) {
+  transition: none;
+  box-shadow: none;
+  -webkit-box-shadow: none;
+  -moz-box-shadow: none;
+  font-weight: normal;
+}
+
+#bootstrap-theme.crm-search div.api4-input.form-inline .form-control label {
+  font-weight: normal;
+  position: relative;
+  top: -2px;
+}
+#bootstrap-theme.crm-search .api4-clause-fieldset fieldset {
+  float: right;
+  width: calc(100% - 58px);
+  margin-top: -8px;
+}
+
+#bootstrap-theme.crm-search .api4-clause-fieldset.api4-sorting fieldset .api4-clause-group-sortable {
+  min-height: 3.5em;
+}
+
+#bootstrap-theme.crm-search .api4-input-group {
+  display: inline-block;
+}
+
+#bootstrap-theme.crm-search i.fa-arrows {
+  cursor: move;
+}
+
+#bootstrap-theme.crm-search .api4-clause-badge {
+  width: 55px;
+  display: inline-block;
+  cursor: move;
+}
+#bootstrap-theme.crm-search .api4-clause-badge .badge {
+  opacity: .5;
+  position: relative;
+}
+#bootstrap-theme.crm-search .api4-clause-badge .caret {
+  margin: 0;
+}
+#bootstrap-theme.crm-search .api4-clause-badge .crm-i {
+  display: none;
+  padding: 0 6px;
+}
+#bootstrap-theme.crm-search .ui-sortable-helper .api4-clause-badge .badge span {
+  display: none;
+}
+#bootstrap-theme.crm-search .ui-sortable-helper .api4-clause-badge .crm-i {
+  display: inline-block;
+}
+
+#bootstrap-theme.crm-search .api4-operator {
+  width: 110px;
+}
+
+#bootstrap-theme.crm-search .api4-add-where-group-menu {
+  min-width: 80px;
+  background-color: rgba(186, 225, 251, 0.94);
+}
+#bootstrap-theme.crm-search .api4-add-where-group-menu a {
+  padding: 5px 10px;
+}
+
+#bootstrap-theme.crm-search .btn.form-control {
+  height: 36px;
+}
+
+#bootstrap-theme.crm-search th.crm-search-result-select {
+  padding-right: 10px;
+}
diff --git a/civicrm/ext/search/info.xml b/civicrm/ext/search/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3908cea9763af352ea0387c72c574270208f8f48
--- /dev/null
+++ b/civicrm/ext/search/info.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<extension key="org.civicrm.search" type="module">
+  <file>search</file>
+  <name>Search</name>
+  <description>Build searches for a wide variety of CiviCRM entities</description>
+  <license>AGPL-3.0</license>
+  <maintainer>
+    <author>Coleman Watts</author>
+    <email>coleman@civicrm.org</email>
+  </maintainer>
+  <urls>
+    <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
+  </urls>
+  <releaseDate>2020-07-08</releaseDate>
+  <version>1.0</version>
+  <tags>
+    <tag>mgmt:hidden</tag>
+  </tags>
+  <develStage>stable</develStage>
+  <compatibility>
+    <ver>5.28</ver>
+  </compatibility>
+  <comments>Distributed with CiviCRM core</comments>
+  <classloader>
+    <psr4 prefix="Civi\" path="Civi"/>
+  </classloader>
+  <civix>
+    <namespace>CRM/Search</namespace>
+  </civix>
+</extension>
diff --git a/civicrm/ext/search/search.civix.php b/civicrm/ext/search/search.civix.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a696e2cffa0848049994a544c3bcb8069308097
--- /dev/null
+++ b/civicrm/ext/search/search.civix.php
@@ -0,0 +1,477 @@
+<?php
+
+// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
+
+/**
+ * The ExtensionUtil class provides small stubs for accessing resources of this
+ * extension.
+ */
+class CRM_Search_ExtensionUtil {
+  const SHORT_NAME = "search";
+  const LONG_NAME = "org.civicrm.search";
+  const CLASS_PREFIX = "CRM_Search";
+
+  /**
+   * 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_Search_ExtensionUtil as E;
+
+/**
+ * (Delegated) Implements hook_civicrm_config().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
+ */
+function _search_civix_civicrm_config(&$config = NULL) {
+  static $configured = FALSE;
+  if ($configured) {
+    return;
+  }
+  $configured = TRUE;
+
+  $template =& CRM_Core_Smarty::singleton();
+
+  $extRoot = dirname(__FILE__) . DIRECTORY_SEPARATOR;
+  $extDir = $extRoot . 'templates';
+
+  if (is_array($template->template_dir)) {
+    array_unshift($template->template_dir, $extDir);
+  }
+  else {
+    $template->template_dir = [$extDir, $template->template_dir];
+  }
+
+  $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
+  set_include_path($include_path);
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_xmlMenu().
+ *
+ * @param $files array(string)
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu
+ */
+function _search_civix_civicrm_xmlMenu(&$files) {
+  foreach (_search_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) {
+    $files[] = $file;
+  }
+}
+
+/**
+ * Implements hook_civicrm_install().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install
+ */
+function _search_civix_civicrm_install() {
+  _search_civix_civicrm_config();
+  if ($upgrader = _search_civix_upgrader()) {
+    $upgrader->onInstall();
+  }
+}
+
+/**
+ * Implements hook_civicrm_postInstall().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
+ */
+function _search_civix_civicrm_postInstall() {
+  _search_civix_civicrm_config();
+  if ($upgrader = _search_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 _search_civix_civicrm_uninstall() {
+  _search_civix_civicrm_config();
+  if ($upgrader = _search_civix_upgrader()) {
+    $upgrader->onUninstall();
+  }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_enable().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable
+ */
+function _search_civix_civicrm_enable() {
+  _search_civix_civicrm_config();
+  if ($upgrader = _search_civix_upgrader()) {
+    if (is_callable([$upgrader, 'onEnable'])) {
+      $upgrader->onEnable();
+    }
+  }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_disable().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
+ * @return mixed
+ */
+function _search_civix_civicrm_disable() {
+  _search_civix_civicrm_config();
+  if ($upgrader = _search_civix_upgrader()) {
+    if (is_callable([$upgrader, 'onDisable'])) {
+      $upgrader->onDisable();
+    }
+  }
+}
+
+/**
+ * (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
+ *
+ * @return mixed
+ *   based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending)
+ *   for 'enqueue', returns void
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
+ */
+function _search_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
+  if ($upgrader = _search_civix_upgrader()) {
+    return $upgrader->onUpgrade($op, $queue);
+  }
+}
+
+/**
+ * @return CRM_Search_Upgrader
+ */
+function _search_civix_upgrader() {
+  if (!file_exists(__DIR__ . '/CRM/Search/Upgrader.php')) {
+    return NULL;
+  }
+  else {
+    return CRM_Search_Upgrader_Base::instance();
+  }
+}
+
+/**
+ * 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 string $dir base dir
+ * @param string $pattern , glob pattern, eg "*.txt"
+ *
+ * @return array
+ */
+function _search_civix_find_files($dir, $pattern) {
+  if (is_callable(['CRM_Utils_File', 'findFiles'])) {
+    return CRM_Utils_File::findFiles($dir, $pattern);
+  }
+
+  $todos = [$dir];
+  $result = [];
+  while (!empty($todos)) {
+    $subdir = array_shift($todos);
+    foreach (_search_civix_glob("$subdir/$pattern") as $match) {
+      if (!is_dir($match)) {
+        $result[] = $match;
+      }
+    }
+    if ($dh = opendir($subdir)) {
+      while (FALSE !== ($entry = readdir($dh))) {
+        $path = $subdir . DIRECTORY_SEPARATOR . $entry;
+        if ($entry[0] == '.') {
+        }
+        elseif (is_dir($path)) {
+          $todos[] = $path;
+        }
+      }
+      closedir($dh);
+    }
+  }
+  return $result;
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_managed().
+ *
+ * Find any *.mgd.php files, merge their content, and return.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed
+ */
+function _search_civix_civicrm_managed(&$entities) {
+  $mgdFiles = _search_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'] = E::LONG_NAME;
+      }
+      if (empty($e['params']['version'])) {
+        $e['params']['version'] = '3';
+      }
+      $entities[] = $e;
+    }
+  }
+}
+
+/**
+ * (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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_caseTypes
+ */
+function _search_civix_civicrm_caseTypes(&$caseTypes) {
+  if (!is_dir(__DIR__ . '/xml/case')) {
+    return;
+  }
+
+  foreach (_search_civix_glob(__DIR__ . '/xml/case/*.xml') as $file) {
+    $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));
+      throw new CRM_Core_Exception($errorMessage);
+    }
+    $caseTypes[$name] = [
+      '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 _search_civix_civicrm_angularModules(&$angularModules) {
+  if (!is_dir(__DIR__ . '/ang')) {
+    return;
+  }
+
+  $files = _search_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 _search_civix_civicrm_themes(&$themes) {
+  $files = _search_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.
+ *
+ * The documentation for glob() says, "On some systems it is impossible to
+ * distinguish between empty match and an error." Anecdotally, the return
+ * result for an empty match is sometimes array() and sometimes FALSE.
+ * This wrapper provides consistency.
+ *
+ * @link http://php.net/glob
+ * @param string $pattern
+ *
+ * @return array
+ */
+function _search_civix_glob($pattern) {
+  $result = glob($pattern);
+  return is_array($result) ? $result : [];
+}
+
+/**
+ * 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)
+ *
+ * @return bool
+ */
+function _search_civix_insert_navigation_menu(&$menu, $path, $item) {
+  // If we are done going down the path, insert menu
+  if (empty($path)) {
+    $menu[] = [
+      'attributes' => array_merge([
+        'label'      => CRM_Utils_Array::value('name', $item),
+        'active'     => 1,
+      ], $item),
+    ];
+    return TRUE;
+  }
+  else {
+    // Find an recurse into the next level down
+    $found = FALSE;
+    $path = explode('/', $path);
+    $first = array_shift($path);
+    foreach ($menu as $key => &$entry) {
+      if ($entry['attributes']['name'] == $first) {
+        if (!isset($entry['child'])) {
+          $entry['child'] = [];
+        }
+        $found = _search_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item);
+      }
+    }
+    return $found;
+  }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_navigationMenu().
+ */
+function _search_civix_navigationMenu(&$nodes) {
+  if (!is_callable(['CRM_Core_BAO_Navigation', 'fixNavigationMenu'])) {
+    _search_civix_fixNavigationMenu($nodes);
+  }
+}
+
+/**
+ * Given a navigation menu, generate navIDs for any items which are
+ * missing them.
+ */
+function _search_civix_fixNavigationMenu(&$nodes) {
+  $maxNavID = 1;
+  array_walk_recursive($nodes, function($item, $key) use (&$maxNavID) {
+    if ($key === 'navID') {
+      $maxNavID = max($maxNavID, $item);
+    }
+  });
+  _search_civix_fixNavigationMenuItems($nodes, $maxNavID, NULL);
+}
+
+function _search_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'])) {
+      _search_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 _search_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
+  $settingsDir = __DIR__ . DIRECTORY_SEPARATOR . 'settings';
+  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 _search_civix_civicrm_entityTypes(&$entityTypes) {
+  $entityTypes = array_merge($entityTypes, []);
+}
diff --git a/civicrm/ext/search/search.php b/civicrm/ext/search/search.php
new file mode 100644
index 0000000000000000000000000000000000000000..7c7f9e1a83eac1135efa45bcabc5d0656253956c
--- /dev/null
+++ b/civicrm/ext/search/search.php
@@ -0,0 +1,128 @@
+<?php
+
+require_once 'search.civix.php';
+
+/**
+ * Implements hook_civicrm_config().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/
+ */
+function search_civicrm_config(&$config) {
+  _search_civix_civicrm_config($config);
+}
+
+/**
+ * Implements hook_civicrm_xmlMenu().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu
+ */
+function search_civicrm_xmlMenu(&$files) {
+  _search_civix_civicrm_xmlMenu($files);
+}
+
+/**
+ * Implements hook_civicrm_install().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install
+ */
+function search_civicrm_install() {
+  _search_civix_civicrm_install();
+}
+
+/**
+ * Implements hook_civicrm_postInstall().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
+ */
+function search_civicrm_postInstall() {
+  _search_civix_civicrm_postInstall();
+}
+
+/**
+ * Implements hook_civicrm_uninstall().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
+ */
+function search_civicrm_uninstall() {
+  _search_civix_civicrm_uninstall();
+}
+
+/**
+ * Implements hook_civicrm_enable().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable
+ */
+function search_civicrm_enable() {
+  _search_civix_civicrm_enable();
+}
+
+/**
+ * Implements hook_civicrm_disable().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
+ */
+function search_civicrm_disable() {
+  _search_civix_civicrm_disable();
+}
+
+/**
+ * Implements hook_civicrm_upgrade().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
+ */
+function search_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
+  return _search_civix_civicrm_upgrade($op, $queue);
+}
+
+/**
+ * Implements hook_civicrm_managed().
+ *
+ * Generate a list of entities to create/deactivate/delete when this module
+ * is installed, disabled, uninstalled.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed
+ */
+function search_civicrm_managed(&$entities) {
+  _search_civix_civicrm_managed($entities);
+}
+
+/**
+ * Implements hook_civicrm_angularModules().
+ *
+ * Generate a list of Angular modules.
+ *
+ * Note: This hook only runs in CiviCRM 4.5+. It may
+ * use features only available in v4.6+.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules
+ */
+function search_civicrm_angularModules(&$angularModules) {
+  _search_civix_civicrm_angularModules($angularModules);
+}
+
+/**
+ * Implements hook_civicrm_alterSettingsFolders().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterSettingsFolders
+ */
+function search_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
+  _search_civix_civicrm_alterSettingsFolders($metaDataFolders);
+}
+
+/**
+ * Implements hook_civicrm_entityTypes().
+ *
+ * Declare entity types provided by this module.
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
+ */
+function search_civicrm_entityTypes(&$entityTypes) {
+  _search_civix_civicrm_entityTypes($entityTypes);
+}
+
+/**
+ * Implements hook_civicrm_thems().
+ */
+function search_civicrm_themes(&$themes) {
+  _search_civix_civicrm_themes($themes);
+}
diff --git a/civicrm/ext/search/templates/CRM/Search/Page/Ang.tpl b/civicrm/ext/search/templates/CRM/Search/Page/Ang.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/civicrm/ext/search/xml/Menu/search.xml b/civicrm/ext/search/xml/Menu/search.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d57a0c52ce6ec9ca04023c6cc3efd4e2ee8b2659
--- /dev/null
+++ b/civicrm/ext/search/xml/Menu/search.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<menu>
+  <item>
+    <path>civicrm/search</path>
+    <page_callback>CRM_Search_Page_Ang</page_callback>
+    <access_arguments>access CiviCRM</access_arguments>
+  </item>
+</menu>
diff --git a/civicrm/install/civicrm.php b/civicrm/install/civicrm.php
index e6886134039281a2c69874ea93a06aac6b3bad53..5b6dcb263a61896a7218c12981221f64e9167e31 100644
--- a/civicrm/install/civicrm.php
+++ b/civicrm/install/civicrm.php
@@ -131,8 +131,6 @@ function civicrm_main(&$config) {
 function civicrm_source($dsn, $fileName, $lineMode = FALSE) {
   global $crmPath;
 
-  require_once "$crmPath/packages/DB.php";
-
   // CRM-19699 See also CRM_Core_DAO for PHP7 mysqli compatiblity.
   // Duplicated here because this is not using CRM_Core_DAO directly
   // and this function may be called directly from Drush.
diff --git a/civicrm/js/model/crm.uf.js b/civicrm/js/model/crm.uf.js
index 4f198c12a9bbaebf69fdbe82c3e4eca78980fd36..636304bad7e6c5550ecb04efa8edfc5672f9980e 100644
--- a/civicrm/js/model/crm.uf.js
+++ b/civicrm/js/model/crm.uf.js
@@ -510,8 +510,8 @@
         options: YESNO
       },
       'add_to_group_id': {
-        title: ts('Add new contacts to a Group?'),
-        help: ts('Select a group if you are using this profile for adding new contacts, AND you want the new contacts to be automatically assigned to a group.'),
+        title: ts('Add contacts to a group?'),
+        help: ts('Select a group if you want contacts to be automatically added to that group when the profile is submitted.'),
         type: 'Number'
       },
       'cancel_URL': {
diff --git a/civicrm/packages/DB/DataObject.php b/civicrm/packages/DB/DataObject.php
index 1df37c89bbdc6f2bd61182d3794d7e1edcd525c1..2e996dd4022d772ec82f817e60b18067fe5ca4b0 100644
--- a/civicrm/packages/DB/DataObject.php
+++ b/civicrm/packages/DB/DataObject.php
@@ -2758,7 +2758,7 @@ class DB_DataObject extends DB_DataObject_Overload
         $action = strtolower(substr(trim($queryString),0,6));
         // CRM-20445 ends
 
-        if (!empty($_DB_DATAOBJECT['CONFIG']['debug']) || defined('CIVICRM_DEBUG_LOG_QUERY')) {
+        if (!empty($_DB_DATAOBJECT['CONFIG']['debug']) || (defined('CIVICRM_DEBUG_LOG_QUERY') && CIVICRM_DEBUG_LOG_QUERY)) {
           $timeTaken = sprintf("%0.6f", microtime(TRUE) - $time);
           $alertLevel = $this->getAlertLevel($timeTaken);
           $message = "$alertLevel QUERY DONE IN $timeTaken  seconds.";
@@ -2774,7 +2774,7 @@ class DB_DataObject extends DB_DataObject_Overload
           else {
             echo $message .= " not quite sure why this query does not have more info";
           }
-          if (defined('CIVICRM_DEBUG_LOG_QUERY')) {
+          if ((defined('CIVICRM_DEBUG_LOG_QUERY') && CIVICRM_DEBUG_LOG_QUERY)) {
             CRM_Core_Error::debug_log_message($message, FALSE, 'sql_log');
           }
           else {
diff --git a/civicrm/packages/VERSIONS.php b/civicrm/packages/VERSIONS.php
index 13510eb9e3728fa9869ece7f4a33edd09dbc42ae..f1d52e0518b43f0c429dd5cb989350517cad9e74 100644
--- a/civicrm/packages/VERSIONS.php
+++ b/civicrm/packages/VERSIONS.php
@@ -107,7 +107,6 @@
  * Contact_Vcard_Build           1.1.2      PHP 3          local changes
  * Contact_Vcard_Parse           1.32.0     PHP 3.0
  * Date                          1.4.7      BSD 3-cl.
- * DB                            1.9.3      PHP 3.0
  * DB_DataObject                 1.11.3     PHP 3          local changes
  * HTML_Common                   1.2.5      PHP 3
  * HTML_QuickForm                3.2.16     PHP 3          local changes, hierselect.php from a very old version (PHP 2)
diff --git a/civicrm/release-notes.md b/civicrm/release-notes.md
index b12dfb5a91c9903239383fbb9fa77ebbbdd3d6f4..970335ce2d46916a877c4c1ce7ae3bfc3e86e99e 100644
--- a/civicrm/release-notes.md
+++ b/civicrm/release-notes.md
@@ -15,6 +15,17 @@ Other resources for identifying changes are:
     * https://github.com/civicrm/civicrm-joomla
     * https://github.com/civicrm/civicrm-wordpress
 
+## CiviCRM 5.30.0
+
+Released October 7, 2020
+
+- **[Synopsis](release-notes/5.30.0.md#synopsis)**
+- **[Features](release-notes/5.30.0.md#features)**
+- **[Bugs resolved](release-notes/5.30.0.md#bugs)**
+- **[Miscellany](release-notes/5.30.0.md#misc)**
+- **[Credits](release-notes/5.30.0.md#credits)**
+- **[Feedback](release-notes/5.30.0.md#feedback)**
+
 ## CiviCRM 5.29.1
 
 Released September 15, 2020
diff --git a/civicrm/release-notes/5.30.0.md b/civicrm/release-notes/5.30.0.md
new file mode 100644
index 0000000000000000000000000000000000000000..a1086516cea7b24b625e783011093eb5448e9f85
--- /dev/null
+++ b/civicrm/release-notes/5.30.0.md
@@ -0,0 +1,579 @@
+# CiviCRM 5.30.0
+
+Released October 7, 2020
+
+- **[Synopsis](#synopsis)**
+- **[Features](#features)**
+- **[Bugs resolved](#bugs)**
+- **[Miscellany](#misc)**
+- **[Credits](#credits)**
+- **[Feedback](#feedback)**
+
+## <a name="synopsis"></a>Synopsis
+
+| *Does this version...?*                                         |         |
+|:--------------------------------------------------------------- |:-------:|
+| Fix security vulnerabilities?                                   |   no    |
+| **Change the database schema?**                                 | **yes** |
+| **Alter the API?**                                              | **yes** |
+| Require attention to configuration options?                     |   no    |
+| **Fix problems installing or upgrading to a previous version?** | **yes** |
+| **Introduce features?**                                         | **yes** |
+| **Fix bugs?**                                                   | **yes** |
+
+## <a name="features"></a>Features
+
+### Core CiviCRM
+
+- **APIv4 Explorer: Generate short cv code
+  ([18089](https://github.com/civicrm/civicrm-core/pull/18089))**
+
+  Improves the APIv4 Explorer user interface by including cv code to run the API
+  from CLI.
+
+- **Optimise proximity custom search, by reducing addGeocodingData fn call
+  ([18248](https://github.com/civicrm/civicrm-core/pull/18248))**
+
+  Improves performance of the proximity custom search.
+
+- **Support more table names for utf8mb4 conversions and database name
+  ([18249](https://github.com/civicrm/civicrm-core/pull/18249))**
+
+  Improves the System.utf8conversion API so that one can specify table patterns
+  and databases.
+
+- **distmaker - Autogenerate civicrm-*-patchset.tar.gz
+  ([18229](https://github.com/civicrm/civicrm-core/pull/18229))**
+
+  Adds a script to autogenerate `civicrm-*-patchset.tar.gz` instead of having to
+  manually generate it every release.
+
+- **Finish allowing use of SSL to connect to database
+  (Work Towards [dev/core#1926](https://lab.civicrm.org/dev/core/-/issues/1926):
+  [18095](https://github.com/civicrm/civicrm-core/pull/18095))**
+
+  Code cleanup towards the goal of allowing SSL database connections.
+
+- **Profile settings - Add new contacts to a Group? is misleading
+  ([dev/core#1950](https://lab.civicrm.org/dev/core/-/issues/1950):
+  [18153](https://github.com/civicrm/civicrm-core/pull/18153))**
+
+  Improves user experience by editing the help text and description for the "Add
+  new contacts to a Group?" setting.
+
+- **API4: Inconsistent behavior on Role ID
+  ([dev/event#37](https://lab.civicrm.org/dev/event/-/issues/37):
+  [18285](https://github.com/civicrm/civicrm-core/pull/18285))**
+
+  Allows searching within arrays or serialized strings via APIv4 (and the new
+  Search extension).
+
+- **APIv4 - Fix output of CustomValue create/save/update
+  ([18195](https://github.com/civicrm/civicrm-core/pull/18195))**
+
+  Improves APIv4 output when saving custom pseudo-entities so that it outputs
+  the values saved instead of `[is_error => 0, result => true]`.
+
+- **Add ability to enable query logging per process
+  ([18335](https://github.com/civicrm/civicrm-core/pull/18335))**
+
+  Makes it possible to enable logging by process as opposed to all processes or
+  no processes.
+
+- **Expose field label to APIv4 and Search creator
+  ([18255](https://github.com/civicrm/civicrm-core/pull/18255) and
+  [18256](https://github.com/civicrm/civicrm-core/pull/18256))**
+
+  Progress towards codifying the difference between a field's title and
+  label in the schema.
+
+  These changes:
+   - expose labels to APIv4
+   - switch to using labels in the new search extension.
+   - separates titles from labels in exportable fields.
+   - attempts to clarify the difference between titles and labels in the
+   getfields description as:
+     - title: "Technical name of field, shown in API and exports"
+     - label: "User-facing label, shown on most forms and displays"
+
+- **Metadata fix - phone_type_id, location_type_id, gender_id
+  ([18114](https://github.com/civicrm/civicrm-core/pull/18114))**
+
+  Improves metadata for various fields to align with new title and label
+  standards.
+
+- **CRM_Utils_VersionCheck - respect force param
+  ([18183](https://github.com/civicrm/civicrm-core/pull/18183))**
+
+  Makes the versionCheck job more consistent with other checks by respecting
+  APIv4's "force" setting to run even if disabled.
+
+- **Add Serialize key to payment_processor field for Event & Contribution Page
+  ([18110](https://github.com/civicrm/civicrm-core/pull/18110))**
+
+  Use metatdata rather than code handling to save array of processors. This
+  makes it so that the DAO can handle arrays if it knows the format.
+
+### CiviContribute
+
+- **Shell Financial ACLs extension
+  ([18269](https://github.com/civicrm/civicrm-core/pull/18269))**
+
+  Begins work to move Financial ACL code into an extension.
+
+- **Add Line Item v4 API (Work Towards
+  [dev/core#1980](https://lab.civicrm.org/dev/core/-/issues/1980):
+  [18275](https://github.com/civicrm/civicrm-core/pull/18275) and
+  [18293](https://github.com/civicrm/civicrm-core/pull/18293))**
+
+  Extends permission handling for LineItem delete and LineItem ACLs from APIv3 to
+  to APIv4 by moving it to the `financialacls` extension.
+
+- **Book keeping Report : Add Time field for Transaction Date and Receive Date
+  field ([dev/report#47](https://lab.civicrm.org/dev/report/-/issues/47):
+  [18268](https://github.com/civicrm/civicrm-core/pull/18268))**
+
+  Adds a time field to the "Transaction Date" and "Receive Date" filters on the
+  Bookkeeping report.
+
+### CiviEvent
+
+- **Wrap "not you" message in crmRegion
+  ([18236](https://github.com/civicrm/civicrm-core/pull/18236))**
+
+  Wraps the "Not you, do you want to register for someone else" message in a
+  crmRegion so that it can be removed/replaced by the buildForm hook.
+
+### CiviMail
+
+- **Add new columns to mailing summary report
+  ([dev/core#1944](https://lab.civicrm.org/dev/core/-/issues/1944):
+  [18132](https://github.com/civicrm/civicrm-core/pull/18132))**
+
+  Adds two new columns to the mailing summary report: "Sender Name" and "Sender
+  Email".
+
+- **Add disable_smarty option to MessageTemplate.send API
+  ([18118](https://github.com/civicrm/civicrm-core/pull/18118))**
+
+  Improves the `MessageTemplate.send` API by adding a new optional parameter
+  "disable_smarty", when "disable_smarty" is set to true smarty is not applied
+  to the contents of the message.
+
+### WordPress Integration
+
+- **Switch WP over to new installer
+  ([dev/wordpress#37](https://lab.civicrm.org/dev/wordpress/-/issues/37):
+  [215](https://github.com/civicrm/civicrm-wordpress/pull/215))**
+
+  Switch to new CiviCRM Installer.
+
+### Drupal Integration
+
+- **Allow for the setting of an additional constant CIVICRM_DRUSH_DSN
+  ([613](https://github.com/civicrm/civicrm-drupal/pull/613))**
+
+  Allows sites to configure CIVICRM_DRUSH_DSN for drush to use to set up
+  database connections.
+
+## <a name="bugs"></a>Bugs resolved
+
+### Core CiviCRM
+
+- **Increase size of mysql field in export table for column with
+  CRM_Utils_Type::T_INT type
+  ([dev/core#1661](https://lab.civicrm.org/dev/core/-/issues/1661):
+  [17956](https://github.com/civicrm/civicrm-core/pull/17956))**
+
+  Fixes some data too long for column errors when exporting.
+
+- **Improve robustness of api/class.api.php
+  ([18283](https://github.com/civicrm/civicrm-core/pull/18283))**
+
+  Ensures that the API mode is set to "local" when an empty string is passed
+  as the server.
+
+- **add serialize data to actionschedule
+  ([18289](https://github.com/civicrm/civicrm-core/pull/18289))**
+
+  Ensures one can create a scheduled reminder via the API that triggers on two
+  different events.
+
+- **API - Add upgrade-safe checks to ensure table exists before reading
+  ([18135](https://github.com/civicrm/civicrm-core/pull/18135))**
+
+  Adds efficient checks into the API (v3 and v4) to ensure a DAO table
+  exists before trying to read from it. This prevents hard-crashes in the UI
+  when there are pending updates (or during the upgrade process itself),
+
+- **DB_DataObject - Read CIVICRM_DEBUG_LOG_QUERY correctly
+  ([305](https://github.com/civicrm/civicrm-packages/pull/305))**
+
+  Makes The value of `CIVICRM_DEBUG_LOG_QUERY` TRUE-ish or FALSE-ish instead of
+  considering any value true.
+
+- **APIv4 Explorer - Fix possible undefined index
+  ([18093](https://github.com/civicrm/civicrm-core/pull/18093))**
+
+- **Option value cache key missing domain ID can result in wrong value retrieved
+  for domain ([dev/core#1971](https://lab.civicrm.org/dev/core/-/issues/1971):
+  [18252](https://github.com/civicrm/civicrm-core/pull/18252))**
+
+- **Upgrade screen - show success instead of error if already upgraded
+  ([18181](https://github.com/civicrm/civicrm-core/pull/18181))**
+
+- **trim white space when checking dedupe
+  ([18234](https://github.com/civicrm/civicrm-core/pull/18234))**
+
+- **Fix search extension excluded from tarball
+  ([18402](https://github.com/civicrm/civicrm-core/pull/18402))**
+
+- **Permissions problem on add or edit template
+  ([dev/core#2008](https://lab.civicrm.org/dev/core/-/issues/2008):
+  [18437](https://github.com/civicrm/civicrm-core/pull/18437))**
+
+- **Search ext: Aggregate field fixes
+  ([18520](https://github.com/civicrm/civicrm-core/pull/18520))**
+
+- **Some datepicker fields only allow choosing dates far in the future
+  ([dev/core#2052](https://lab.civicrm.org/dev/core/-/issues/2052):
+  [18524](https://github.com/civicrm/civicrm-core/pull/18524))**
+
+### CiviCase
+
+- **Fix js for case type status change on load
+  ([18254](https://github.com/civicrm/civicrm-core/pull/18254))**
+
+### CiviContribute
+
+- **Clean money for non-deductible amount
+  ([18300](https://github.com/civicrm/civicrm-core/pull/18300))**
+
+  Ensures that the Contribution API cleans data entered into the "non-deductible
+  amount" field the same way it cleans the other money fields.
+
+- **Processors - remove gross_amount param from processors
+  ([18177](https://github.com/civicrm/civicrm-core/pull/18177))**
+
+  The gross_amount param is returned by a bunch of processors but is not used so
+  this removes it.
+
+- **{contribution.receipt_date} token does not use any CiviCRM date formatter,
+  output in YYYY-MM-DD HH:MM:SS format and {contribution.receive_date} also uses
+  a non-standard format
+  ([18176](https://github.com/civicrm/civicrm-core/pull/18176))**
+
+- **Fix PHP notice on contribution page
+  ([18116](https://github.com/civicrm/civicrm-core/pull/18116))**
+
+- **E_NOTICE on contribution page widget tab
+  ([18189](https://github.com/civicrm/civicrm-core/pull/18189))**
+
+- **change payment instrument when pending payment paid through credit card
+  ([dev/core#912](https://lab.civicrm.org/dev/core/-/issues/912):
+  [18266](https://github.com/civicrm/civicrm-core/pull/18266))**
+
+- **PayPal Standard redirect fails on WordPress
+  ([dev/core#1997](https://lab.civicrm.org/dev/core/-/issues/1997):
+  [18525](https://github.com/civicrm/civicrm-core/pull/18525) and
+  [18534](https://github.com/civicrm/civicrm-core/pull/18534))**
+
+- **Invoice no longer displays amount paid/due
+  ([dev/core#2035](https://lab.civicrm.org/dev/core/-/issues/2035):
+  [18602](https://github.com/civicrm/civicrm-core/pull/18602) and
+  [18628](https://github.com/civicrm/civicrm-core/pull/18628))**
+
+### CiviEvent
+
+- **Allow the "Cancellation or transfer time limit (hours)" to be negative
+  ([dev/event#34](https://lab.civicrm.org/dev/event/-/issues/34):
+  [18067](https://github.com/civicrm/civicrm-core/pull/18067))**
+
+  Ensures that entering a negative value into "Cancellation or transfer time
+  limit (hours)" saves correctly and works as expected.
+
+- **Batch Update via Profile does not supply data for editing when custom fields
+  created after participant added to event
+  ([18235](https://github.com/civicrm/civicrm-core/pull/18235))**
+
+- **E_WARNING when saving event fees admin page if there's no discounts set
+  ([18169](https://github.com/civicrm/civicrm-core/pull/18169))**
+
+### CiviMail
+
+- **Unexpected behavior from api.MailingEventSubscribe.create
+  (Work Towards [dev/mail#24](https://lab.civicrm.org/dev/mail/-/issues/24):
+  [18325](https://github.com/civicrm/civicrm-core/pull/18325))**
+
+  Fixes labels for APIv3 `MailingEventSubscribe.create` so that they accurately
+  represent the behavior (unsubscribe v subscribe).
+
+- **Mass SMS: Limit group selection to mailing groups
+  ([18154](https://github.com/civicrm/civicrm-core/pull/18154))**
+
+  Ensures that the fields "Include Groups" and "Exclude Groups" on the "New SMS" form
+  only list groups of the type "Mailing List".
+
+- **Help text bubble is empty and smarty errors when .hlp file is missing for
+  settings forms
+  ([dev/core#1920](https://lab.civicrm.org/dev/core/-/issues/1920):
+  [18648](https://github.com/civicrm/civicrm-core/pull/18648))**
+
+- **Fix error when creating new AB test mailing because domain_id is a required
+  field ([18685](https://github.com/civicrm/civicrm-core/pull/18685))**
+
+### CiviMember
+
+- **Fix regression on back-office membership renewal by credit card
+  ([18615](https://github.com/civicrm/civicrm-core/pull/18615))**
+
+### Drupal Integration
+
+- **Fix : Enable Drupal Watchdog Logging for Drupal 8
+  ([dev/core#1943](https://lab.civicrm.org/dev/core/-/issues/1943):
+  [18115](https://github.com/civicrm/civicrm-core/pull/18115))**
+
+  Ensure the setting `Enable Drupal Watchdog Logging` works on Drupal 8 sites.
+
+### WordPress Integration
+
+- **Check if $post defined before trying to use its field values.
+  ([18168](https://github.com/civicrm/civicrm-core/pull/18168))**
+
+  This fixes php notices like `PHP Notice:  Trying to get property 'post_name'
+  of non-object in <webroot
+  path>\wp-content\plugins\civicrm\civicrm\CRM\Utils\System\WordPress.php on
+  line 288`
+
+## <a name="misc"></a>Miscellany
+
+- **Fix regen and update civicrm_generated
+  ([18273](https://github.com/civicrm/civicrm-core/pull/18273))**
+
+- **Do not pass by reference for createRelatedMemberships
+  ([18243](https://github.com/civicrm/civicrm-core/pull/18243))**
+
+- **Typo in groups dropdown on scheduled reminders admin form
+  ([dev/core#1956](https://lab.civicrm.org/dev/core/-/issues/1956):
+  [18323](https://github.com/civicrm/civicrm-core/pull/18323))**
+
+- **Replace CRM_Utils_Array::value in contribution/confirm and bao/membership
+  ([18157](https://github.com/civicrm/civicrm-core/pull/18157))**
+
+- **Cleanup contactID variables in event/contribution register buildQuickForm
+  ([18208](https://github.com/civicrm/civicrm-core/pull/18208))**
+
+- **Use already determined value for contributionRecurID
+  ([18265](https://github.com/civicrm/civicrm-core/pull/18265))**
+
+- **Minor code cleanup to Order API
+  ([18217](https://github.com/civicrm/civicrm-core/pull/18217))**
+
+- **CodeGen - Add use statement to extensions DAO files
+  ([18094](https://github.com/civicrm/civicrm-core/pull/18094))**
+
+- **Use more appropriate money format function
+  ([18151](https://github.com/civicrm/civicrm-core/pull/18151))**
+
+- **Metadata fix in activity search
+  ([18216](https://github.com/civicrm/civicrm-core/pull/18216))**
+
+- **Replace deprecated Drupal 8 constants
+  ([18240](https://github.com/civicrm/civicrm-core/pull/18240))**
+
+- **Spelling mistake "separator" not "seperator"
+  ([18238](https://github.com/civicrm/civicrm-core/pull/18238))**
+
+- **Remove unused variables from repeatTransaction
+  ([18209](https://github.com/civicrm/civicrm-core/pull/18209))**
+
+- **Remove first_contribution key from repeattransaction
+  ([18197](https://github.com/civicrm/civicrm-core/pull/18197))**
+
+- **Remove unnecessary transformation of upgrade msg severity
+  ([18182](https://github.com/civicrm/civicrm-core/pull/18182))**
+
+- **Remove old dropped items from the schema xml
+  ([18244](https://github.com/civicrm/civicrm-core/pull/18244))**
+
+- **Remove 1 of 2 remaining places where relatedObjects is called in
+  completeOrder ([18257](https://github.com/civicrm/civicrm-core/pull/18257))**
+
+- **Remove last pass-by-reference in completeOrder signature
+  ([18258](https://github.com/civicrm/civicrm-core/pull/18258))**
+
+- **Remove legacy handling for 'fixing' line_item.entity_id
+  ([18155](https://github.com/civicrm/civicrm-core/pull/18155))**
+
+- **Remove deprecated code ids['userId']
+  ([18156](https://github.com/civicrm/civicrm-core/pull/18156))**
+
+- **Remove 'hack' that overwrites result of searchColumns hook in mailings list
+  ([18237](https://github.com/civicrm/civicrm-core/pull/18237))**
+
+- **Remove unused variable
+  ([18108](https://github.com/civicrm/civicrm-core/pull/18108))**
+
+- **Remove handling for 4.2 DBs
+  ([612](https://github.com/civicrm/civicrm-drupal/pull/612))**
+
+- **Remove ref to logging time
+  ([18221](https://github.com/civicrm/civicrm-core/pull/18221))**
+
+- **Remove 2019 deprecated function
+  ([18242](https://github.com/civicrm/civicrm-core/pull/18242))**
+
+- **remove dropped tables for activity target and assignment
+  ([18280](https://github.com/civicrm/civicrm-core/pull/18280))**
+
+- **Deprecate 'trapException' in DAO::executeQuery
+  ([18138](https://github.com/civicrm/civicrm-core/pull/18138))**
+
+- **Deprecate/remove unused checkVersion() functions
+  ([18134](https://github.com/civicrm/civicrm-core/pull/18134))**
+
+- **[REF] Ensure that the form param _id is set when adding a new group w…
+  ([18250](https://github.com/civicrm/civicrm-core/pull/18250))**
+
+- **[REF] Update recent replacement for CRM_Utils_Array::value
+  ([18172](https://github.com/civicrm/civicrm-core/pull/18172) and
+  [18453](https://github.com/civicrm/civicrm-core/pull/18453))**
+
+- **[REF] Remove usages of CRM_Utils_Money::format that pass in a blank s…
+  ([18142](https://github.com/civicrm/civicrm-core/pull/18142))**
+
+- **[REF] Move isSSLDSN() function to avoid potential problems
+  ([18131](https://github.com/civicrm/civicrm-core/pull/18131))**
+
+- **(REF) CRM_Core_Region - Remove unused bits
+  ([18139](https://github.com/civicrm/civicrm-core/pull/18139))**
+
+- **[REF] Move Auto DSN Switching into a core function
+  ([18270](https://github.com/civicrm/civicrm-core/pull/18270))**
+
+- **[REF] Remove DB Package as it is now deployed using composer
+  ([302](https://github.com/civicrm/civicrm-packages/pull/302))**
+
+- **[REF] Deploy Pear DB package using composer
+  ([18027](https://github.com/civicrm/civicrm-core/pull/18027) and
+  [18473](https://github.com/civicrm/civicrm-core/pull/18473))**
+
+- **[REF] Update subtypeInfo function to leverage getAllContactTypes
+  ([17934](https://github.com/civicrm/civicrm-core/pull/17934))**
+
+- **[REF] Fix metadata label for contribution_page_id
+  ([18047](https://github.com/civicrm/civicrm-core/pull/18047))**
+
+- **REF Remove unused passbyreference and var from ipn_process_transaction
+  ([18311](https://github.com/civicrm/civicrm-core/pull/18311))**
+
+- **[REF] Clean up return variables on updateContributionStatus,
+  updatePendingOnlineContribution
+  ([18303](https://github.com/civicrm/civicrm-core/pull/18303))**
+
+- **[REF] Move function to delete merged contacts to the Merger class and
+  include all instances of the pair
+  ([17980](https://github.com/civicrm/civicrm-core/pull/17980))**
+
+- **[REF] Remove most interaction with $objects in completeOrder
+  ([18315](https://github.com/civicrm/civicrm-core/pull/18315))**
+
+- **[REF] Fix financial item allocation of negative payments against completed
+  payments ([17810](https://github.com/civicrm/civicrm-core/pull/17810))**
+
+- **[REF] Separate and move line-item specific portion of checkTaxAmount to
+  LineItem api ([18294](https://github.com/civicrm/civicrm-core/pull/18294))**
+
+- **[REF] [test] Slight increase in test cover & make functions more re-usable
+  ([18291](https://github.com/civicrm/civicrm-core/pull/18291))**
+
+- **[REF] simplify retrieval of existing membership on membership renewal form
+  ([18296](https://github.com/civicrm/civicrm-core/pull/18296))**
+
+- **[REF] Extract function to get locations to merge, rename 'operation' …
+  ([17991](https://github.com/civicrm/civicrm-core/pull/17991))**
+
+- **[REF] Deprecate unused SQL temptable functions
+  ([18171](https://github.com/civicrm/civicrm-core/pull/18171))**
+
+- **Minor cleanup in test class
+  ([18170](https://github.com/civicrm/civicrm-core/pull/18170))**
+
+- **Add line item acl tests
+  ([18274](https://github.com/civicrm/civicrm-core/pull/18274))**
+
+- **Fix test set up to call Order.create to create the correct line items
+  ([18279](https://github.com/civicrm/civicrm-core/pull/18279))**
+
+- **Test & remove handling for max_related in renewal form
+  ([18295](https://github.com/civicrm/civicrm-core/pull/18295))**
+
+- **Add test to check, remove unnecessary lines
+  ([18292](https://github.com/civicrm/civicrm-core/pull/18292))**
+
+- **[Test framework] - Remove test for deprecated way of calling hook invoke
+  ([18136](https://github.com/civicrm/civicrm-core/pull/18136))**
+
+- **(NFC) Update various DAO checksums
+  ([18184](https://github.com/civicrm/civicrm-core/pull/18184))**
+
+- **[NFC] Add code comments about hidden but still used buttons
+  ([18119](https://github.com/civicrm/civicrm-core/pull/18119))**
+
+- **[NFC] [Test] Initial testing on event payment forms.
+  ([18112](https://github.com/civicrm/civicrm-core/pull/18112))**
+
+- **[NFC] Update locale over-ride documentation to mention that it is mos…
+  ([17919](https://github.com/civicrm/civicrm-core/pull/17919))**
+
+- **(NFC) Add some tests to the `resources` group
+  ([18211](https://github.com/civicrm/civicrm-core/pull/18211))**
+
+- **[NFC] Some DAO checksums not updated
+  ([18239](https://github.com/civicrm/civicrm-core/pull/18239))**
+
+- **[NFC] Array formatting
+  ([18109](https://github.com/civicrm/civicrm-core/pull/18109))**
+
+- **[NFC] IDE cleanup - arrays, single quotes
+  ([614](https://github.com/civicrm/civicrm-drupal/pull/614))**
+
+- **[NFC] Update Event DAO Checksum
+  ([18361](https://github.com/civicrm/civicrm-core/pull/18361))**
+
+- **[NFC] Fix dates in RelationshipTest
+  ([18530](https://github.com/civicrm/civicrm-core/pull/18530))**
+
+- **api_v3_MultilingualTest - Skip Cxn entities
+  ([18547](https://github.com/civicrm/civicrm-core/pull/18547))**
+
+- **[NFC] Update Checksum on CustomField DAO
+  ([18689](https://github.com/civicrm/civicrm-core/pull/18689))**
+
+## <a name="credits"></a>Credits
+
+This release was developed by the following code authors:
+
+AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman, Pengyi
+Zhang; Circle Interactive - Pradeep Nayak; CiviCRM - Coleman Watts, Tim Otten;
+CiviDesk - Sunil Pawar, Yashodha Chaku; Dave D; Flinders University of South
+Australia - Tom Anderson; JMA Consulting - Monish Deb, Seamus Lee; John
+Kingsnorth; Lemniscus - Noah Miller; Lighthouse Consulting and Design - Brian
+Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - Chamil
+Wijesooriya; MJW Consulting - Matthew Wire; Squiffle Consulting - Aidan
+Saunders; Wikimedia Foundation - Eileen McNaughton, Maggie Epps; Wildsight -
+Lars Sanders-Green
+
+Most authors also reviewed code for this release; in addition, the following
+reviewers contributed their comments:
+
+Artful Robot - Rich Lott; Christian Wach; Circle Interactive - Martin Castle;
+Dave D; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe -
+Patrick Figel; MJCO - Mikey O'Toole; Semper IT - Karin Gerritsen; Squiffle
+Consulting - Aidan Saunders; Tadpole Collective - Kevin Cristiano
+
+## <a name="feedback"></a>Feedback
+
+These release notes are edited by Alice Frumin and Andrew Hunt.  If you'd like
+to provide feedback on them, please log in to https://chat.civicrm.org/civicrm
+and contact `@agh1`.
diff --git a/civicrm/settings/Mailing.setting.php b/civicrm/settings/Mailing.setting.php
index cb0b5ec0b6950d6cdba16c44f0aa004ba4bcea16..bfcc3580741d860005c0c75da8f86c3b91e78f4f 100644
--- a/civicrm/settings/Mailing.setting.php
+++ b/civicrm/settings/Mailing.setting.php
@@ -386,6 +386,6 @@ return [
     'is_domain' => 1,
     'is_contact' => 0,
     'description' => ts('The frequency that CiviMail updates its sent mail database.'),
-    'help_text' => 'CiviMail records email sent at the frequency you specify.  If you set it to 1, it will update the database every time it sends an email.  This ensures that emails are not resent if the batch job fails, but this may cause a performance hit, particularly for large jobs.',
+    'help_text' => NULL,
   ],
 ];
diff --git a/civicrm/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php b/civicrm/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php
index d48925c3de924d84e839e9bf02bc1550a5b037f2..4f09d7c8328a5b2755b042340ac1628b0c1d9d0e 100644
--- a/civicrm/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php
+++ b/civicrm/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php
@@ -23,19 +23,24 @@ if (!defined('CIVI_SETUP')) {
       $expectedKeys = array('server', 'username', 'password', 'database');
       sort($expectedKeys);
       if ($keys !== $expectedKeys) {
-        $e->addError('database', $dbField, sprintf("The database credentials for \"%s\" should be specified as (%s) not (%s)",
-          $dbField,
-          implode(',', $expectedKeys),
-          implode(',', $keys)
-        ));
-        $errors++;
+        // if it failed it might be because of the optional ssl parameters
+        $expectedKeys[] = 'ssl_params';
+        sort($expectedKeys);
+        if ($keys !== $expectedKeys) {
+          $e->addError('database', $dbField, sprintf("The database credentials for \"%s\" should be specified as (%s) not (%s)",
+            $dbField,
+            implode(',', $expectedKeys),
+            implode(',', $keys)
+          ));
+          $errors++;
+        }
       }
 
       foreach ($db as $k => $v) {
         if ($k === 'password' && empty($v)) {
           $e->addWarning('database', "$dbField.$k", "The property \"$dbField.$k\" is blank. This may be correct in some controlled environments; it could also be a mistake or a symptom of an insecure configuration.");
         }
-        elseif (!is_scalar($v)) {
+        elseif ($k !== 'ssl_params' && !is_scalar($v)) {
           $e->addError('database', "$dbField.$k", "The property \"$dbField.$k\" is not well-formed.");
           $errors++;
         }
diff --git a/civicrm/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php b/civicrm/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php
index f0d15507c3fe5585fcd12aa16d62f7fdd53b2f03..4e4e051f31a759dafe229fea743fba5dedf557de 100644
--- a/civicrm/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php
+++ b/civicrm/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php
@@ -29,6 +29,7 @@ if (!defined('CIVI_SETUP')) {
       'username' => $model->db['username'],
       'password' => $model->db['password'],
       'database' => $model->db['database'],
+      'ssl_params' => $model->db['ssl_params'] ?? NULL,
     ));
     _corereqadapter_addMessages($e, 'database', $dbMsgs);
   });
diff --git a/civicrm/setup/plugins/installDatabase/FlushDrupal8.civi-setup.php b/civicrm/setup/plugins/installDatabase/FlushDrupal8.civi-setup.php
index c1d57e2f4930a5e3a2606dc2c7a1c3778d584442..0b25fd3120cb59a5152d3a7547eeb43abe404a80 100644
--- a/civicrm/setup/plugins/installDatabase/FlushDrupal8.civi-setup.php
+++ b/civicrm/setup/plugins/installDatabase/FlushDrupal8.civi-setup.php
@@ -51,6 +51,6 @@ function civicrm_install_set_drupal8_perms() {
     ]);
   }
   $perms = array_intersect($perms, $allPerms);
-  user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, $perms);
-  user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, $perms);
+  user_role_grant_permissions(\Drupal\user\RoleInterface::AUTHENTICATED_ID, $perms);
+  user_role_grant_permissions(\Drupal\user\RoleInterface::ANONYMOUS_ID, $perms);
 }
diff --git a/civicrm/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php b/civicrm/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php
index 3581eb3bed8e5778c37f927fb3f7122f4e731fc1..29aedb2b073b2e66a765f4f6116555b5738c7591 100644
--- a/civicrm/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php
+++ b/civicrm/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php
@@ -59,6 +59,13 @@ if (!defined('CIVI_SETUP')) {
     $params['dbPass'] = addslashes($m->db['password']);
     $params['dbHost'] = addslashes($m->db['server']);
     $params['dbName'] = addslashes($m->db['database']);
+    // The '&' prefix is awkward, but we don't know what's already in the file.
+    // At the time of writing, it has ?new_link=true. If that is removed,
+    // then need to update this.
+    // The PHP_QUERY_RFC3986 is important because PEAR::DB will interpret plus
+    // signs as a reference to its old DSN format and mangle the DSN, so we
+    // need to use %20 for spaces.
+    $params['dbSSL'] = empty($m->db['ssl_params']) ? '' : addslashes('&' . http_build_query($m->db['ssl_params'], '', '&', PHP_QUERY_RFC3986));
     $params['cms'] = addslashes($m->cms);
     $params['CMSdbUser'] = addslashes($m->cmsDb['username']);
     $params['CMSdbPass'] = addslashes($m->cmsDb['password']);
diff --git a/civicrm/setup/src/Setup/DbUtil.php b/civicrm/setup/src/Setup/DbUtil.php
index 054fe8d731b08754f5c27be202fd7ac7ce61b62d..87849057c6d896932e818dc87d7a01a38f104f90 100644
--- a/civicrm/setup/src/Setup/DbUtil.php
+++ b/civicrm/setup/src/Setup/DbUtil.php
@@ -12,14 +12,16 @@ class DbUtil {
   public static function parseDsn($dsn) {
     $parsed = parse_url($dsn);
     return array(
-      'server' => self::encodeHostPort($parsed['host'], $parsed['port']),
+      'server' => self::encodeHostPort($parsed['host'], $parsed['port'] ?? NULL),
       'username' => $parsed['user'] ?: NULL,
       'password' => $parsed['pass'] ?: NULL,
       'database' => $parsed['path'] ? ltrim($parsed['path'], '/') : NULL,
+      'ssl_params' => self::parseSSL($parsed['query'] ?? NULL),
     );
   }
 
   /**
+   * @todo Is this used anywhere? It doesn't support SSL as-is.
    * Convert an datasource from array notation to URL notation.
    *
    * @param array $db
@@ -40,7 +42,25 @@ class DbUtil {
    */
   public static function softConnect($db) {
     list($host, $port) = self::decodeHostPort($db['server']);
-    $conn = @mysqli_connect($host, $db['username'], $db['password'], $db['database'], $port);
+    if (empty($db['ssl_params'])) {
+      $conn = @mysqli_connect($host, $db['username'], $db['password'], $db['database'], $port);
+    }
+    else {
+      $conn = NULL;
+      $init = mysqli_init();
+      mysqli_ssl_set(
+        $init,
+        $db['ssl_params']['key'] ?? NULL,
+        $db['ssl_params']['cert'] ?? NULL,
+        $db['ssl_params']['ca'] ?? NULL,
+        $db['ssl_params']['capath'] ?? NULL,
+        $db['ssl_params']['cipher'] ?? NULL
+      );
+      // @todo socket parameter, but if you're using sockets do you need SSL?
+      if (@mysqli_real_connect($init, $host, $db['username'], $db['password'], $db['database'], $port, NULL, MYSQLI_CLIENT_SSL)) {
+        $conn = $init;
+      }
+    }
     return $conn;
   }
 
@@ -94,6 +114,33 @@ class DbUtil {
     return $host . ($port ? (':' . $port) : '');
   }
 
+  /**
+   * For SSL you can have client certificates, which has some required and
+   * optional parameters, or you can have anonymous SSL, which just requires
+   * some indication that you want that.
+   *
+   * @param string $query_string
+   * @return array
+   */
+  public static function parseSSL($query_string) {
+    if (empty($query_string)) {
+      return [];
+    }
+    parse_str($query_string, $parsed_query);
+    $sensible_parameters = [
+      // ssl=1 alone means no client certificate - it's not a real mysqli option
+      'ssl' => NULL,
+      'key' => NULL,
+      'cert' => NULL,
+      'ca' => NULL,
+      'capath' => NULL,
+      'cipher' => NULL,
+    ];
+    // Only want to include a param if it's in our list of sensibles, e.g.
+    // we don't want new_link=true.
+    return array_intersect_key($parsed_query, $sensible_parameters);
+  }
+
   /**
    * @param array $db
    * @param string $SQLcontent
diff --git a/civicrm/sql/civicrm.mysql b/civicrm/sql/civicrm.mysql
index cbcb27da703cd2c8ccb281cfee537d74949ba9ed..e4aebd60919280b122599a7adcfbfc9aee955b5e 100644
--- a/civicrm/sql/civicrm.mysql
+++ b/civicrm/sql/civicrm.mysql
@@ -4808,7 +4808,7 @@ CREATE TABLE `civicrm_event` (
      `requires_approval` tinyint    COMMENT 'Whether participants require approval before they can finish registering.',
      `expiration_time` int unsigned    COMMENT 'Expire pending but unconfirmed registrations after this many hours.',
      `allow_selfcancelxfer` tinyint   DEFAULT 0 COMMENT 'Allow self service cancellation or transfer for event?',
-     `selfcancelxfer_time` int unsigned   DEFAULT 0 COMMENT 'Number of hours prior to event start date to allow self-service cancellation or transfer.',
+     `selfcancelxfer_time` int   DEFAULT 0 COMMENT 'Number of hours prior to event start date to allow self-service cancellation or transfer.',
      `waitlist_text` text    COMMENT 'Text to display when the event is full, but participants can signup for a waitlist.',
      `approval_req_text` text    COMMENT 'Text to display when the approval is required to complete registration for an event.',
      `is_template` tinyint NOT NULL  DEFAULT 0 COMMENT 'whether the event has template',
diff --git a/civicrm/sql/civicrm_data.mysql b/civicrm/sql/civicrm_data.mysql
index 1aab8338dd3b368e346898af56e1c3e67a7f7bc8..b565cd5f8bc0f185829dddad09ef40638c982d70 100644
--- a/civicrm/sql/civicrm_data.mysql
+++ b/civicrm/sql/civicrm_data.mysql
@@ -9408,29 +9408,28 @@ INSERT INTO civicrm_msg_template
                 <td style="text-align:right;white-space: nowrap"><b><font size="1">{ts 1=$defaultCurrency}TOTAL %1{/ts}</font></b></td>
                 <td style="text-align:right;"><font size="1">{$amount|crmMoney:$currency}</font></td>
               </tr>
-             {if $amountDue != 0}
-                <tr>
-                  <td colspan="3"></td>
-                  <td style="text-align:right;white-space: nowrap"><font size="1">
-                    {if $contribution_status_id == $refundedStatusId}
-                      {ts}Amount Credited{/ts}
-                    {else}
-                      {ts}Amount Paid{/ts}
-                    {/if}
-                   </font>
-                  </td>
-                  <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td>
-                </tr>
-                <tr>
-                  <td colspan="3"></td>
-                  <td colspan="2"><hr></hr></td>
-                </tr>
-                <tr>
-                  <td colspan="3"></td>
-                  <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td>
-                  <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td>
-                </tr>
-              {/if}
+              <tr>
+                <td colspan="3"></td>
+                <td style="text-align:right;white-space: nowrap"><font size="1">
+                  {if $contribution_status_id == $refundedStatusId}
+                    {ts}Amount Credited{/ts}
+                  {else}
+                    {ts}Amount Paid{/ts}
+                  {/if}
+                 </font>
+                </td>
+                <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td>
+              </tr>
+              <tr>
+                <td colspan="3"></td>
+                <td colspan="2"><hr></hr></td>
+              </tr>
+              <tr>
+                <td colspan="3"></td>
+                <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td>
+                <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td>
+              </tr>
+
               <br/><br/><br/>
               <tr>
                 <td colspan="5"></td>
@@ -9852,29 +9851,28 @@ INSERT INTO civicrm_msg_template
                 <td style="text-align:right;white-space: nowrap"><b><font size="1">{ts 1=$defaultCurrency}TOTAL %1{/ts}</font></b></td>
                 <td style="text-align:right;"><font size="1">{$amount|crmMoney:$currency}</font></td>
               </tr>
-             {if $amountDue != 0}
-                <tr>
-                  <td colspan="3"></td>
-                  <td style="text-align:right;white-space: nowrap"><font size="1">
-                    {if $contribution_status_id == $refundedStatusId}
-                      {ts}Amount Credited{/ts}
-                    {else}
-                      {ts}Amount Paid{/ts}
-                    {/if}
-                   </font>
-                  </td>
-                  <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td>
-                </tr>
-                <tr>
-                  <td colspan="3"></td>
-                  <td colspan="2"><hr></hr></td>
-                </tr>
-                <tr>
-                  <td colspan="3"></td>
-                  <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td>
-                  <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td>
-                </tr>
-              {/if}
+              <tr>
+                <td colspan="3"></td>
+                <td style="text-align:right;white-space: nowrap"><font size="1">
+                  {if $contribution_status_id == $refundedStatusId}
+                    {ts}Amount Credited{/ts}
+                  {else}
+                    {ts}Amount Paid{/ts}
+                  {/if}
+                 </font>
+                </td>
+                <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td>
+              </tr>
+              <tr>
+                <td colspan="3"></td>
+                <td colspan="2"><hr></hr></td>
+              </tr>
+              <tr>
+                <td colspan="3"></td>
+                <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td>
+                <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td>
+              </tr>
+
               <br/><br/><br/>
               <tr>
                 <td colspan="5"></td>
@@ -14393,7 +14391,7 @@ You were registered by: {$payer.name}
 {/if}
 
 {if $event.allow_selfcancelxfer }
-{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
+{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
    {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
 {ts}Transfer or cancel your registration:{/ts} {$selfService}
 {/if}
@@ -14896,7 +14894,7 @@ You were registered by: {$payer.name}
     {if $event.allow_selfcancelxfer }
      <tr>
       <td colspan="2" {$valueStyle}>
-        {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
+        {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
         {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
         <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a>
       </td>
@@ -15202,7 +15200,7 @@ You were registered by: {$payer.name}
 {/if}
 
 {if $event.allow_selfcancelxfer }
-{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
+{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
    {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
 {ts}Transfer or cancel your registration:{/ts} {$selfService}
 {/if}
@@ -15705,7 +15703,7 @@ You were registered by: {$payer.name}
     {if $event.allow_selfcancelxfer }
      <tr>
       <td colspan="2" {$valueStyle}>
-        {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
+        {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
         {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
         <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a>
       </td>
@@ -16609,7 +16607,7 @@ Click this link to go to a web page where you can confirm your registration onli
 {$confirmUrl}
 {/if}
 {if $event.allow_selfcancelxfer }
-{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
+{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
    {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
 {ts}Transfer or cancel your registration:{/ts} {$selfService}
 {/if}
@@ -16845,7 +16843,7 @@ Click this link to go to a web page where you can confirm your registration onli
   {if $event.allow_selfcancelxfer }
    <tr>
      <td colspan="2" {$valueStyle}>
-       {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
+       {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
          {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
        <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a>
      </td>
@@ -16879,7 +16877,7 @@ Click this link to go to a web page where you can confirm your registration onli
 {$confirmUrl}
 {/if}
 {if $event.allow_selfcancelxfer }
-{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
+{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
    {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
 {ts}Transfer or cancel your registration:{/ts} {$selfService}
 {/if}
@@ -17115,7 +17113,7 @@ Click this link to go to a web page where you can confirm your registration onli
   {if $event.allow_selfcancelxfer }
    <tr>
      <td colspan="2" {$valueStyle}>
-       {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
+       {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
          {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
        <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a>
      </td>
@@ -23093,6 +23091,7 @@ VALUES
 -- do not try this at home folks.
 INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'sequentialcreditnotes', 'Sequential credit notes', 'Sequential credit notes', 'sequentialcreditnotes', 1);
 INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'eventcart', 'Event cart', 'Event cart', 'eventcart', 1);
+INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'financialacls', 'Financial ACLs', 'Financial ACLs', 'financialacls', 1);
 -- +--------------------------------------------------------------------+
 -- | Copyright CiviCRM LLC. All rights reserved.                        |
 -- |                                                                    |
@@ -23897,4 +23896,4 @@ INSERT INTO `civicrm_report_instance`
     ( `domain_id`, `title`, `report_id`, `description`, `permission`, `form_values`)
 VALUES
     (  @domainID, 'Survey Details', 'survey/detail', 'Detailed report for canvassing, phone-banking, walk lists or other surveys.', 'access CiviReport', 'a:39:{s:6:"fields";a:2:{s:9:"sort_name";s:1:"1";s:6:"result";s:1:"1";}s:22:"assignee_contact_id_op";s:2:"eq";s:25:"assignee_contact_id_value";s:0:"";s:12:"sort_name_op";s:3:"has";s:15:"sort_name_value";s:0:"";s:17:"street_number_min";s:0:"";s:17:"street_number_max";s:0:"";s:16:"street_number_op";s:3:"lte";s:19:"street_number_value";s:0:"";s:14:"street_name_op";s:3:"has";s:17:"street_name_value";s:0:"";s:15:"postal_code_min";s:0:"";s:15:"postal_code_max";s:0:"";s:14:"postal_code_op";s:3:"lte";s:17:"postal_code_value";s:0:"";s:7:"city_op";s:3:"has";s:10:"city_value";s:0:"";s:20:"state_province_id_op";s:2:"in";s:23:"state_province_id_value";a:0:{}s:13:"country_id_op";s:2:"in";s:16:"country_id_value";a:0:{}s:12:"survey_id_op";s:2:"in";s:15:"survey_id_value";a:0:{}s:12:"status_id_op";s:2:"eq";s:15:"status_id_value";s:1:"1";s:11:"custom_1_op";s:2:"in";s:14:"custom_1_value";a:0:{}s:11:"custom_2_op";s:2:"in";s:14:"custom_2_value";a:0:{}s:17:"custom_3_relative";s:1:"0";s:13:"custom_3_from";s:0:"";s:11:"custom_3_to";s:0:"";s:11:"description";s:75:"Detailed report for canvassing, phone-banking, walk lists or other surveys.";s:13:"email_subject";s:0:"";s:8:"email_to";s:0:"";s:8:"email_cc";s:0:"";s:10:"permission";s:17:"access CiviReport";s:6:"groups";s:0:"";s:9:"domain_id";i:1;}');
-UPDATE civicrm_domain SET version = '5.29.1';
+UPDATE civicrm_domain SET version = '5.30.0';
diff --git a/civicrm/sql/civicrm_generated.mysql b/civicrm/sql/civicrm_generated.mysql
index c1fafd9745bf0bf58293a3f540fd32ad0081b59e..7bea0df4a5d0e25048d6c2a96c95fb0d4a062248 100644
--- a/civicrm/sql/civicrm_generated.mysql
+++ b/civicrm/sql/civicrm_generated.mysql
@@ -399,7 +399,7 @@ UNLOCK TABLES;
 
 LOCK TABLES `civicrm_domain` WRITE;
 /*!40000 ALTER TABLE `civicrm_domain` DISABLE KEYS */;
-INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.29.1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
+INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.30.0',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
 /*!40000 ALTER TABLE `civicrm_domain` ENABLE KEYS */;
 UNLOCK TABLES;
 
@@ -495,7 +495,7 @@ UNLOCK TABLES;
 
 LOCK TABLES `civicrm_extension` WRITE;
 /*!40000 ALTER TABLE `civicrm_extension` DISABLE KEYS */;
-INSERT INTO `civicrm_extension` (`id`, `type`, `full_name`, `name`, `label`, `file`, `schema_version`, `is_active`) VALUES (1,'module','sequentialcreditnotes','Sequential credit notes','Sequential credit notes','sequentialcreditnotes',NULL,1),(2,'module','eventcart','Event cart','Event cart','eventcart',NULL,1);
+INSERT INTO `civicrm_extension` (`id`, `type`, `full_name`, `name`, `label`, `file`, `schema_version`, `is_active`) VALUES (1,'module','sequentialcreditnotes','Sequential credit notes','Sequential credit notes','sequentialcreditnotes',NULL,1),(2,'module','eventcart','Event cart','Event cart','eventcart',NULL,1),(3,'module','financialacls','Financial ACLs','Financial ACLs','financialacls',NULL,1);
 /*!40000 ALTER TABLE `civicrm_extension` ENABLE KEYS */;
 UNLOCK TABLES;
 
diff --git a/civicrm/templates/CRM/Admin/Form/Setting/Mail.hlp b/civicrm/templates/CRM/Admin/Form/Setting/Mail.hlp
new file mode 100644
index 0000000000000000000000000000000000000000..e33c7912c59fb8ebdd07d9e47b702a0dcc443e49
--- /dev/null
+++ b/civicrm/templates/CRM/Admin/Form/Setting/Mail.hlp
@@ -0,0 +1,14 @@
+{*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+*}
+{htxt id="civimail-sync-interval-id"}
+  <p>
+    {ts}CiviMail records email sent at the frequency you specify. If you set it to 1, it will update the database every time it sends an email. This ensures that emails are not resent if the batch job fails, but this may cause a performance hit, particularly for large jobs.{/ts}
+  </p>
+{/htxt}
diff --git a/civicrm/templates/CRM/Case/Form/Activity/OpenCase.tpl b/civicrm/templates/CRM/Case/Form/Activity/OpenCase.tpl
index 4627f81de3a269d470fcaf5fdf5dedffd34b71a7..c1a955771e230fa65dee5d38d5003d8ab0c57332 100644
--- a/civicrm/templates/CRM/Case/Form/Activity/OpenCase.tpl
+++ b/civicrm/templates/CRM/Case/Form/Activity/OpenCase.tpl
@@ -42,15 +42,17 @@
       var caseStatusLabels = {/literal}{$caseStatusLabels.values|@json_encode}{literal};
       var caseStatusNames = {/literal}{$caseStatusNames.values|@json_encode}{literal};
       if ($('#case_type_id, #status_id', $form).length === 2) {
-        $('#case_type_id', $form).change(function() {
-          if ($(this).val()) {
-            var definition = caseTypes[$(this).val()].definition;
+        updateCaseStatusOptions();
+        $('#case_type_id', $form).change(updateCaseStatusOptions);
+        function updateCaseStatusOptions() {
+          if ($('#case_type_id', $form).val()) {
+            var definition = caseTypes[$('#case_type_id', $form).val()].definition;
             var newOptions = CRM._.filter(caseStatusLabels, function(opt) {
               return !definition.statuses || !definition.statuses.length || definition.statuses.indexOf(caseStatusNames[opt.key]) > -1;
             });
             CRM.utils.setOptions($('#status_id', $form), newOptions);
           }
-        })
+        }
       }
     });
   </script>
diff --git a/civicrm/templates/CRM/Case/Form/CaseView.tpl b/civicrm/templates/CRM/Case/Form/CaseView.tpl
index 86e23f57af2caeba63596c64373341d1854304e8..1301ac5b36370e22d39aca16f7d7672f7f3b832b 100644
--- a/civicrm/templates/CRM/Case/Form/CaseView.tpl
+++ b/civicrm/templates/CRM/Case/Form/CaseView.tpl
@@ -95,7 +95,7 @@
       <p>
         {$form.add_activity_type_id.html}
         {if $hasAccessToAllCases} &nbsp;
-          {$form.timeline_id.html}{$form._qf_CaseView_next.html} &nbsp;
+          {$form.timeline_id.html}{*This CaseView_next button is hidden, but gets clicked by the onChange handler for timeline_id in CaseView.js*}{$form._qf_CaseView_next.html} &nbsp;
           {$form.report_id.html}
         {/if}
       </p>
@@ -112,7 +112,7 @@
 
         {if $mergeCases}
           <a href="#mergeCasesDialog" class="action-item no-popup crm-hover-button case-miniform"><i class="crm-i fa-compress" aria-hidden="true"></i> {ts}Merge Case{/ts}</a>
-          {$form._qf_CaseView_next_merge_case.html}
+          {*This CaseView_next_merge_case button is hidden, but gets clicked by javascript in CaseView.js when the mergeCasesDialog popup is saved.*}{$form._qf_CaseView_next_merge_case.html}
           <span id="mergeCasesDialog" class="hiddenElement">
             {$form.merge_case_id.html}
           </span>
diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl
index d6ef535aae41dce7ebae78138fd04839be917618..0e5a3c2c6126c8b7454f955ce481b8ac3775ff37 100644
--- a/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl
@@ -65,16 +65,19 @@
 
   <div class="crm-contribution-page-id-{$contributionPageID} crm-block crm-contribution-main-form-block">
 
+    {crmRegion name='contribution-main-not-you-block'}
     {if $contact_id && !$ccid}
       <div class="messages status no-popup crm-not-you-message">
         {ts 1=$display_name}Welcome %1{/ts}. (<a href="{crmURL p='civicrm/contribute/transact' q="cid=0&reset=1&id=`$contributionPageID`"}" title="{ts}Click here to do this for a different person.{/ts}">{ts 1=$display_name}Not %1, or want to do this for a different person{/ts}</a>?)
       </div>
     {/if}
+    {/crmRegion}
 
     <div id="intro_text" class="crm-public-form-item crm-section intro_text-section">
       {$intro_text}
     </div>
     {include file="CRM/common/cidzero.tpl"}
+
     {if $islifetime or $ispricelifetime}
       <div class="help">{ts}You have a current Lifetime Membership which does not need to be renewed.{/ts}</div>
     {/if}
diff --git a/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl b/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl
index 657fb77251ff6781a1414af5a2fa01055dd8bc88..5564d23fa07e73b6b9d0bf75eb91d7891af3f077 100644
--- a/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl
@@ -120,7 +120,7 @@
     {$form.financial_type_id.html|crmAddClass:twenty}
   </td>
   <td>
-    <label>{ts}Contribution Page{/ts}</label> <br />
+    <label>{$form.contribution_page_id.label}</label> <br />
     {$form.contribution_page_id.html|crmAddClass:twenty}
   </td>
 </tr>
diff --git a/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.hlp b/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.hlp
index 2aa4f62db7de9fa010f1db254a93da413d3ac6d0..8fba9c56a980bd8add2c3ec8ba9d9e795f34db3c 100644
--- a/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.hlp
+++ b/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.hlp
@@ -89,5 +89,5 @@
   {ts}Cancellation or Transfer Time Limit{/ts}
 {/htxt}
 {htxt id="id-selfcancelxfer_time"}
-  {ts}Number of hours prior to event start date to allow self-service cancellation or transfer. Enter 0 (or leave empty) to allow cancellation or transfer up until the event has started.{/ts}
+  {ts}Number of hours prior to event start date to allow self-service cancellation or transfer. Enter a negative number of hours to allow cancellation after the event starts. Enter 0 (or leave empty) to allow cancellation or transfer up until the event has started.{/ts}
 {/htxt}
diff --git a/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl b/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl
index 02246d54cba6681f43f14049d85aa8d8b2d3a687..16bb9a1938acff8dde23299d7b0ba41c1cb0a989 100644
--- a/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl
+++ b/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl
@@ -18,7 +18,7 @@ function display(totalfee) {
   // go as a float - CRM-13491
   totalfee = Math.round(totalfee*100)/100;
   // note : some variables used used here are global variables defined inside Calculate.tpl
-  var totalEventFee  = formatMoney( totalfee, 2, seperator, thousandMarker);
+  var totalEventFee  = formatMoney( totalfee, 2, separator, thousandMarker);
   cj('#pricevalue').html("<b>"+symbol+"</b> "+totalEventFee);
   scriptfee   = totalfee;
   scriptarray = price;
@@ -67,7 +67,7 @@ function populatebalanceFee(updatedAmt, onlyStatusUpdate) {
   }
 
   if (!onlyStatusUpdate) {
-    balanceAmt = formatMoney(balanceAmt, 2, seperator, thousandMarker);
+    balanceAmt = formatMoney(balanceAmt, 2, separator, thousandMarker);
     cj('#balance-fee').text(symbol+" "+balanceAmt);
   }
 }
diff --git a/civicrm/templates/CRM/Event/Form/Registration/Register.tpl b/civicrm/templates/CRM/Event/Form/Registration/Register.tpl
index cc4dedfac3bdbf272ab63416c0eae0b9c5c39561..a76a3c8dfe9ce87446b04359f930eb7b71da09d1 100644
--- a/civicrm/templates/CRM/Event/Form/Registration/Register.tpl
+++ b/civicrm/templates/CRM/Event/Form/Registration/Register.tpl
@@ -33,6 +33,7 @@
       </div>
     {/if}
 
+    {crmRegion name='event-register-not-you-block'}
     {if $contact_id}
       <div class="messages status no-popup crm-not-you-message" id="crm-event-register-different">
         {ts 1=$display_name}Welcome %1{/ts}. (<a
@@ -40,6 +41,8 @@
           title="{ts}Click here to register a different person for this event.{/ts}">{ts 1=$display_name}Not %1, or want to register a different person{/ts}</a>?)
       </div>
     {/if}
+    {/crmRegion}
+
     {if $event.intro_text}
       <div id="intro_text" class="crm-public-form-item crm-section intro_text-section">
         <p>{$event.intro_text}</p>
diff --git a/civicrm/templates/CRM/Mailing/Form/Settings.hlp b/civicrm/templates/CRM/Mailing/Form/Settings.hlp
deleted file mode 100644
index 99d515ee397dbe92bcbc577f0fb0d0bff1e517c6..0000000000000000000000000000000000000000
--- a/civicrm/templates/CRM/Mailing/Form/Settings.hlp
+++ /dev/null
@@ -1,28 +0,0 @@
-{*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved.                        |
- |                                                                    |
- | This work is published under the GNU AGPLv3 license with some      |
- | permitted exceptions and without any warranty. For full license    |
- | and copyright information, see https://civicrm.org/licensing       |
- +--------------------------------------------------------------------+
-*}
-{htxt id="mailing-visibility-title"}
-  {ts}Visibility{/ts}
-{/htxt}
-{htxt id="mailing-visibility"}
-<p>
-{assign var=hlpViewURL value="mailing.viewUrl"}
-{ts 1=$hlpViewURL}This option makes it possible to make the contents of the email accessible to users as a web page. This option is only relevant if you insert the Mailing permalink token %1 in your message in the next step. This token generates a URL on which the message can be viewed (remember to insert the code in the appropriate HTML tags).{/ts}
-</p><p>
-{ts}You have two options to specify the visibility of the mailing{/ts}:
-</p><p>
-{ts}"public pages" will make the content of this mailing be viewable as a web page by everyone who has the permission of "view public CiviMail content"{/ts}
-</p><p>
-{ts}"user and user admin" only means that only users that received the mailing or administrators can view the content of this email as a web page; the recipients will have to log in to be able to view the message{/ts}
-<p></p>
-{htxt id="mailing-sync-interval-title"}
-  {ts}Sync Interval{/ts}
-{ts}"civimail_sync_interval" specifies how frequently CiviMail records which emails it has sent.  A value of 1 means that it updates the database with every email sent, but this may have a performance hit.{/ts}:
-<p></p>
-{/htxt}
diff --git a/civicrm/templates/CRM/Pledge/Form/Pledge.tpl b/civicrm/templates/CRM/Pledge/Form/Pledge.tpl
index 82c7e770874325c0eede6fb4275da773cac8d8e1..fa6afe6e6ca386394d892d15bbcd44e140cf7393 100644
--- a/civicrm/templates/CRM/Pledge/Form/Pledge.tpl
+++ b/civicrm/templates/CRM/Pledge/Form/Pledge.tpl
@@ -217,14 +217,14 @@
 
      function calculatedPaymentAmount( ) {
        var thousandMarker = {/literal}{crmSetting name="monetaryThousandSeparator" group="CiviCRM Localization"}{literal};
-       var seperator      = '{/literal}{$config->monetaryDecimalPoint}{literal}';
+       var separator      = '{/literal}{$config->monetaryDecimalPoint}{literal}';
        var amount = document.getElementById("amount").value;
-       // replace all thousandMarker and change the seperator to a dot
-       amount = amount.replace(thousandMarker,'').replace(seperator,'.');
+       // replace all thousandMarker and change the separator to a dot
+       amount = amount.replace(thousandMarker,'').replace(separator,'.');
        var installments = document.getElementById("installments").value;
        if ( installments != '' && installments != NaN) {
             amount =  amount/installments;
-            var installmentAmount = formatMoney( amount, 2, seperator, thousandMarker );
+            var installmentAmount = formatMoney( amount, 2, separator, thousandMarker );
             document.getElementById("eachPaymentAmount").value = installmentAmount;
        }
      }
diff --git a/civicrm/templates/CRM/UF/Form/Group.hlp b/civicrm/templates/CRM/UF/Form/Group.hlp
index 2bb3fc4ca23f84c0950035e9c45bcc7a848fd84b..3a0a93d8f1e854ad608c045307a37c89b9be6482 100644
--- a/civicrm/templates/CRM/UF/Form/Group.hlp
+++ b/civicrm/templates/CRM/UF/Form/Group.hlp
@@ -63,7 +63,7 @@
   {ts}Add to Group{/ts}
 {/htxt}
 {htxt id='id-add_group'}
-{ts}Select a group if you are using this profile for adding new contacts, AND you want the new contacts to be automatically assigned to a group.{/ts}
+{ts}Select a group if you want contacts to be automatically added to that group when the profile is submitted.{/ts}
 {/htxt}
 
 {htxt id='id-notify_email-title'}
diff --git a/civicrm/templates/CRM/common/civicrm.settings.php.template b/civicrm/templates/CRM/common/civicrm.settings.php.template
index 46ed344835d7c5594701862e047c5dc55f7826e7..3d0575569fb8dbccc87ac797befb0fcc48ca85e1 100644
--- a/civicrm/templates/CRM/common/civicrm.settings.php.template
+++ b/civicrm/templates/CRM/common/civicrm.settings.php.template
@@ -106,7 +106,7 @@ if (!defined('CIVICRM_DSN')) {
     define('CIVICRM_DSN', $GLOBALS['_CV']['TEST_DB_DSN']);
   }
   else {
-    define('CIVICRM_DSN', 'mysql://%%dbUser%%:%%dbPass%%@%%dbHost%%/%%dbName%%?new_link=true');
+    define('CIVICRM_DSN', 'mysql://%%dbUser%%:%%dbPass%%@%%dbHost%%/%%dbName%%?new_link=true%%dbSSL%%');
   }
 }
 
@@ -432,6 +432,11 @@ if (!defined('CIVICRM_PSR16_STRICT')) {
  * configuration option, but wish to, for example, use fr_CA instead of the
  * default fr_FR (for French), set one or more of the constants below to an
  * appropriate regional value.
+ *
+ * Note that since 5.26.0 specifically https://github.com/civicrm/civicrm-core/pull/16700
+ * This generally doesn't get used by WordPress especially if using the Polylang plugin.
+ * The reason is that the WordPress implementation has been changed to get the full locale
+ * from the WordPress plugin rather than just the 2 string language code.
  */
 // define('CIVICRM_LANGUAGE_MAPPING_FR', 'fr_CA');
 // define('CIVICRM_LANGUAGE_MAPPING_EN', 'en_CA');
diff --git a/civicrm/vendor/autoload.php b/civicrm/vendor/autoload.php
index 5333a90fa06389962d06ec187892e4a70604f580..7a74b810e6251b1633490fb3350011c7803d22bf 100644
--- a/civicrm/vendor/autoload.php
+++ b/civicrm/vendor/autoload.php
@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96::getLoader();
+return ComposerAutoloaderInit157c8f429cb411f5002e91a721972659::getLoader();
diff --git a/civicrm/vendor/composer/autoload_namespaces.php b/civicrm/vendor/composer/autoload_namespaces.php
index 7ae201cfc1de7e83bbeb32dad0dfd6134707c1fc..fc4a46d05bf2bd09094d8fb67ea5467bf3404c77 100644
--- a/civicrm/vendor/composer/autoload_namespaces.php
+++ b/civicrm/vendor/composer/autoload_namespaces.php
@@ -17,6 +17,7 @@ return array(
     'Log' => array($vendorDir . '/pear/log'),
     'File' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
     'Dflydev\\ApacheMimeTypes' => array($vendorDir . '/dflydev/apache-mime-types/src'),
+    'DB' => array($vendorDir . '/pear/db'),
     'Crypt' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
     'Console' => array($vendorDir . '/pear/console_getopt'),
     'Civi\\' => array($baseDir . '/tests/phpunit'),
diff --git a/civicrm/vendor/composer/autoload_real.php b/civicrm/vendor/composer/autoload_real.php
index 8e5f2d37637bc8ff8f3e44c79e689d6446867c26..b76ea7c666459ff43c4330222beb188063e1131c 100644
--- a/civicrm/vendor/composer/autoload_real.php
+++ b/civicrm/vendor/composer/autoload_real.php
@@ -2,7 +2,7 @@
 
 // autoload_real.php @generated by Composer
 
-class ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96
+class ComposerAutoloaderInit157c8f429cb411f5002e91a721972659
 {
     private static $loader;
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96
             return self::$loader;
         }
 
-        spl_autoload_register(array('ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96', 'loadClassLoader'), true, true);
+        spl_autoload_register(array('ComposerAutoloaderInit157c8f429cb411f5002e91a721972659', 'loadClassLoader'), true, true);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
-        spl_autoload_unregister(array('ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInit157c8f429cb411f5002e91a721972659', 'loadClassLoader'));
 
         $includePaths = require __DIR__ . '/include_paths.php';
         $includePaths[] = get_include_path();
@@ -31,7 +31,7 @@ class ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96
         if ($useStaticLoader) {
             require_once __DIR__ . '/autoload_static.php';
 
-            call_user_func(\Composer\Autoload\ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::getInitializer($loader));
+            call_user_func(\Composer\Autoload\ComposerStaticInit157c8f429cb411f5002e91a721972659::getInitializer($loader));
         } else {
             $map = require __DIR__ . '/autoload_namespaces.php';
             foreach ($map as $namespace => $path) {
@@ -52,19 +52,19 @@ class ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96
         $loader->register(true);
 
         if ($useStaticLoader) {
-            $includeFiles = Composer\Autoload\ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$files;
+            $includeFiles = Composer\Autoload\ComposerStaticInit157c8f429cb411f5002e91a721972659::$files;
         } else {
             $includeFiles = require __DIR__ . '/autoload_files.php';
         }
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequirec0c306f7aec196654ddc587ff3d70d96($fileIdentifier, $file);
+            composerRequire157c8f429cb411f5002e91a721972659($fileIdentifier, $file);
         }
 
         return $loader;
     }
 }
 
-function composerRequirec0c306f7aec196654ddc587ff3d70d96($fileIdentifier, $file)
+function composerRequire157c8f429cb411f5002e91a721972659($fileIdentifier, $file)
 {
     if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
         require $file;
diff --git a/civicrm/vendor/composer/autoload_static.php b/civicrm/vendor/composer/autoload_static.php
index c31616f97c2c188b59b2adfe61647705331892e0..4ef4257dbf96cf18d58fb37809f854e09a616bab 100644
--- a/civicrm/vendor/composer/autoload_static.php
+++ b/civicrm/vendor/composer/autoload_static.php
@@ -4,7 +4,7 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96
+class ComposerStaticInit157c8f429cb411f5002e91a721972659
 {
     public static $files = array (
         '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
@@ -346,6 +346,10 @@ class ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96
             array (
                 0 => __DIR__ . '/..' . '/dflydev/apache-mime-types/src',
             ),
+            'DB' => 
+            array (
+                0 => __DIR__ . '/..' . '/pear/db',
+            ),
         ),
         'C' => 
         array (
@@ -531,11 +535,11 @@ class ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96
     public static function getInitializer(ClassLoader $loader)
     {
         return \Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$prefixDirsPsr4;
-            $loader->prefixesPsr0 = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$prefixesPsr0;
-            $loader->fallbackDirsPsr0 = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$fallbackDirsPsr0;
-            $loader->classMap = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInit157c8f429cb411f5002e91a721972659::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit157c8f429cb411f5002e91a721972659::$prefixDirsPsr4;
+            $loader->prefixesPsr0 = ComposerStaticInit157c8f429cb411f5002e91a721972659::$prefixesPsr0;
+            $loader->fallbackDirsPsr0 = ComposerStaticInit157c8f429cb411f5002e91a721972659::$fallbackDirsPsr0;
+            $loader->classMap = ComposerStaticInit157c8f429cb411f5002e91a721972659::$classMap;
 
         }, null, ClassLoader::class);
     }
diff --git a/civicrm/vendor/composer/include_paths.php b/civicrm/vendor/composer/include_paths.php
index 975aca43d6419a59bc93ddc76ac23ef9e94e23d5..4d88f9589bdbef68e1e18c5f438f080d56a3d1e6 100644
--- a/civicrm/vendor/composer/include_paths.php
+++ b/civicrm/vendor/composer/include_paths.php
@@ -10,9 +10,10 @@ return array(
     $vendorDir . '/phpseclib/phpseclib/phpseclib',
     $vendorDir . '/pear/pear_exception',
     $vendorDir . '/pear/auth_sasl',
-    $vendorDir . '/pear/log',
     $vendorDir . '/pear/console_getopt',
     $vendorDir . '/pear/pear-core-minimal/src',
+    $vendorDir . '/pear/db',
+    $vendorDir . '/pear/log',
     $vendorDir . '/pear/mail',
     $vendorDir . '/pear/mail_mime',
     $vendorDir . '/pear/net_socket',
diff --git a/civicrm/vendor/composer/installed.json b/civicrm/vendor/composer/installed.json
index 3c2cfe85bd84f58e6da16f62683e9d8ca2ebb613..3989c7f86ad1ac4ce138d20d54980b4256cdb521 100644
--- a/civicrm/vendor/composer/installed.json
+++ b/civicrm/vendor/composer/installed.json
@@ -1029,6 +1029,68 @@
         ],
         "description": "More info available on: http://pear.php.net/package/Console_Getopt"
     },
+    {
+        "name": "pear/db",
+        "version": "v1.10.0",
+        "version_normalized": "1.10.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/pear/DB.git",
+            "reference": "e158c3a48246b67cd8c95856ffbb93de4ef380fe"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/pear/DB/zipball/e158c3a48246b67cd8c95856ffbb93de4ef380fe",
+            "reference": "e158c3a48246b67cd8c95856ffbb93de4ef380fe",
+            "shasum": ""
+        },
+        "require": {
+            "pear/pear-core-minimal": "*"
+        },
+        "time": "2020-04-19T19:45:59+00:00",
+        "type": "library",
+        "extra": {
+            "patches_applied": {
+                "Apply CiviCRM Customisations for the pear:db package": "https://raw.githubusercontent.com/civicrm/civicrm-core/a48a43c2b5f6d694fff1cfb99d522c5d9e2459a0/tools/scripts/composer/pear_db_civicrm_changes.patch"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "DB": "./"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "include-path": [
+            "./"
+        ],
+        "license": [
+            "PHP License v3.01"
+        ],
+        "authors": [
+            {
+                "name": "Daniel Convissor",
+                "email": "danielc@php.net",
+                "role": "Lead"
+            },
+            {
+                "name": "Adam Harvey",
+                "email": "aharvey@php.net",
+                "role": "Lead"
+            },
+            {
+                "name": "Stig Bakken",
+                "email": "stig@php.net",
+                "role": "Developer"
+            },
+            {
+                "name": "Tomas V.V.Cox",
+                "email": "cox@idecnet.com",
+                "role": "Developer"
+            }
+        ],
+        "description": "More info available on: http://pear.php.net/package/DB"
+    },
     {
         "name": "pear/log",
         "version": "1.13.2",
diff --git a/civicrm/packages/DB.php b/civicrm/vendor/pear/db/DB.php
similarity index 99%
rename from civicrm/packages/DB.php
rename to civicrm/vendor/pear/db/DB.php
index 0612a1397d6afdac14f7e00cd31e1e28d75d24ca..cfac8ee2e6f662a38edd4f8359c2ca25d599c082 100644
--- a/civicrm/packages/DB.php
+++ b/civicrm/vendor/pear/db/DB.php
@@ -440,7 +440,7 @@ define('DB_PORTABILITY_ALL', 63);
  * @author     Daniel Convissor <danielc@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB
@@ -591,7 +591,7 @@ class DB
      */
     function apiVersion()
     {
-        return '1.9.3';
+        return '@package_version@';
     }
 
     // }}}
@@ -606,7 +606,7 @@ class DB
      */
     public static function isError($value)
     {
-        return is_object($value) && is_a($value, 'DB_Error');
+        return is_object($value) && is_a($value, 'DB_Error');		
     }
 
     // }}}
@@ -896,7 +896,7 @@ class DB
          * defined, and means that we deal with strings and array in the same
          * manner. */
         $dsnArray = DB::parseDSN($dsn);
-
+        
         if ($hidePassword) {
             $dsnArray['password'] = 'PASSWORD';
         }
@@ -906,7 +906,7 @@ class DB
         if (is_string($dsn) && strpos($dsn, 'tcp') === false && $dsnArray['protocol'] == 'tcp') {
             $dsnArray['protocol'] = false;
         }
-
+        
         // Now we just have to construct the actual string. This is ugly.
         $dsnString = $dsnArray['phptype'];
         if ($dsnArray['dbsyntax']) {
@@ -929,7 +929,7 @@ class DB
             $dsnString .= ':'.$dsnArray['port'];
         }
         $dsnString .= '/'.$dsnArray['database'];
-
+        
         /* Option handling. Unfortunately, parseDSN simply places options into
          * the top-level array, so we'll first get rid of the fields defined by
          * DB and see what's left. */
@@ -956,7 +956,7 @@ class DB
 
         return $dsnString;
     }
-
+    
     // }}}
 }
 
@@ -972,7 +972,7 @@ class DB
  * @author     Stig Bakken <ssb@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB_Error extends PEAR_Error
@@ -1033,7 +1033,7 @@ class DB_Error extends PEAR_Error
  * @author     Stig Bakken <ssb@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB_result
@@ -1498,7 +1498,7 @@ class DB_result
  * @author     Stig Bakken <ssb@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  * @see        DB_common::setFetchMode()
  */
diff --git a/civicrm/packages/DB/common.php b/civicrm/vendor/pear/db/DB/common.php
similarity index 99%
rename from civicrm/packages/DB/common.php
rename to civicrm/vendor/pear/db/DB/common.php
index 92a8143b2098e0154d7115fcdb578b9dedc516cf..b4112e513474cf0a1c5cf2992b3182643cca6f19 100644
--- a/civicrm/packages/DB/common.php
+++ b/civicrm/vendor/pear/db/DB/common.php
@@ -42,7 +42,7 @@ require_once 'PEAR.php';
  * @author     Daniel Convissor <danielc@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB_common extends PEAR
@@ -262,7 +262,7 @@ class DB_common extends PEAR
     function quoteString($string)
     {
         $string = $this->quoteSmart($string);
-        if ($string{0} == "'") {
+        if ($string[0] == "'") {
             return substr($string, 1, -1);
         }
         return $string;
@@ -471,7 +471,7 @@ class DB_common extends PEAR
     function quoteBoolean($boolean) {
         return $boolean ? '1' : '0';
     }
-
+     
     // }}}
     // {{{ quoteFloat()
 
@@ -487,7 +487,7 @@ class DB_common extends PEAR
     function quoteFloat($float) {
         return "'".$this->escapeSimple(str_replace(',', '.', strval(floatval($float))))."'";
     }
-
+     
     // }}}
     // {{{ escapeSimple()
 
@@ -885,6 +885,9 @@ class DB_common extends PEAR
     function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT,
                          $where = false)
     {
+        if ($where) {
+            $where = strtr($where, array('?' => '\?', '!' => '\!', '&' => '\&',));
+        }
         $sth = $this->autoPrepare($table, array_keys($fields_values), $mode,
                                   $where);
         if (DB::isError($sth)) {
diff --git a/civicrm/packages/DB/dbase.php b/civicrm/vendor/pear/db/DB/dbase.php
similarity index 99%
rename from civicrm/packages/DB/dbase.php
rename to civicrm/vendor/pear/db/DB/dbase.php
index 8324c86b7a07151214df3b28a00e14e078ca43b6..8368c3d736c1898cadf6a3bac7423f3b94c7b4ce 100644
--- a/civicrm/packages/DB/dbase.php
+++ b/civicrm/vendor/pear/db/DB/dbase.php
@@ -41,7 +41,7 @@ require_once 'DB/common.php';
  * @author     Daniel Convissor <danielc@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB_dbase extends DB_common
@@ -402,7 +402,7 @@ class DB_dbase extends DB_common
     function quoteBoolean($boolean) {
         return $boolean ? 'T' : 'F';
     }
-
+     
     // }}}
     // {{{ tableInfo()
 
diff --git a/civicrm/vendor/pear/db/DB/fbsql.php b/civicrm/vendor/pear/db/DB/fbsql.php
new file mode 100644
index 0000000000000000000000000000000000000000..62ae2b6a8f0df1abb104153245c4f4a0d3bcba6d
--- /dev/null
+++ b/civicrm/vendor/pear/db/DB/fbsql.php
@@ -0,0 +1,769 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's fbsql extension
+ * for interacting with FrontBase databases
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Frank M. Kromann <frank@frontbase.com>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's fbsql extension
+ * for interacting with FrontBase databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Frank M. Kromann <frank@frontbase.com>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/DB
+ * @since      Class functional since Release 1.7.0
+ */
+class DB_fbsql extends DB_common
+{
+    // {{{ properties
+
+    /**
+     * The DB driver type (mysql, oci8, odbc, etc.)
+     * @var string
+     */
+    var $phptype = 'fbsql';
+
+    /**
+     * The database syntax variant to be used (db2, access, etc.), if any
+     * @var string
+     */
+    var $dbsyntax = 'fbsql';
+
+    /**
+     * The capabilities of this DB implementation
+     *
+     * The 'new_link' element contains the PHP version that first provided
+     * new_link support for this DBMS.  Contains false if it's unsupported.
+     *
+     * Meaning of the 'limit' element:
+     *   + 'emulate' = emulate with fetch row by number
+     *   + 'alter'   = alter the query
+     *   + false     = skip rows
+     *
+     * @var array
+     */
+    var $features = array(
+        'limit'         => 'alter',
+        'new_link'      => false,
+        'numrows'       => true,
+        'pconnect'      => true,
+        'prepare'       => false,
+        'ssl'           => false,
+        'transactions'  => true,
+    );
+
+    /**
+     * A mapping of native error codes to DB error codes
+     * @var array
+     */
+    var $errorcode_map = array(
+         22 => DB_ERROR_SYNTAX,
+         85 => DB_ERROR_ALREADY_EXISTS,
+        108 => DB_ERROR_SYNTAX,
+        116 => DB_ERROR_NOSUCHTABLE,
+        124 => DB_ERROR_VALUE_COUNT_ON_ROW,
+        215 => DB_ERROR_NOSUCHFIELD,
+        217 => DB_ERROR_INVALID_NUMBER,
+        226 => DB_ERROR_NOSUCHFIELD,
+        231 => DB_ERROR_INVALID,
+        239 => DB_ERROR_TRUNCATED,
+        251 => DB_ERROR_SYNTAX,
+        266 => DB_ERROR_NOT_FOUND,
+        357 => DB_ERROR_CONSTRAINT_NOT_NULL,
+        358 => DB_ERROR_CONSTRAINT,
+        360 => DB_ERROR_CONSTRAINT,
+        361 => DB_ERROR_CONSTRAINT,
+    );
+
+    /**
+     * The raw database connection created by PHP
+     * @var resource
+     */
+    var $connection;
+
+    /**
+     * The DSN information for connecting to a database
+     * @var array
+     */
+    var $dsn = array();
+
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * This constructor calls <kbd>parent::__construct()</kbd>
+     *
+     * @return void
+     */
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database server, log in and open the database
+     *
+     * Don't call this method directly.  Use DB::connect() instead.
+     *
+     * @param array $dsn         the data source name
+     * @param bool  $persistent  should the connection be persistent?
+     *
+     * @return int  DB_OK on success. A DB_Error object on failure.
+     */
+    function connect($dsn, $persistent = false)
+    {
+        if (!PEAR::loadExtension('fbsql')) {
+            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+        }
+
+        $this->dsn = $dsn;
+        if ($dsn['dbsyntax']) {
+            $this->dbsyntax = $dsn['dbsyntax'];
+        }
+
+        $params = array(
+            $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
+            $dsn['username'] ? $dsn['username'] : null,
+            $dsn['password'] ? $dsn['password'] : null,
+        );
+
+        $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect';
+
+        $ini = ini_get('track_errors');
+        $php_errormsg = '';
+        if ($ini) {
+            $this->connection = @call_user_func_array($connect_function,
+                                                      $params);
+        } else {
+            @ini_set('track_errors', 1);
+            $this->connection = @call_user_func_array($connect_function,
+                                                      $params);
+            @ini_set('track_errors', $ini);
+        }
+
+        if (!$this->connection) {
+            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+                                     null, null, null,
+                                     $php_errormsg);
+        }
+
+        if ($dsn['database']) {
+            if (!@fbsql_select_db($dsn['database'], $this->connection)) {
+                return $this->fbsqlRaiseError();
+            }
+        }
+
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ disconnect()
+
+    /**
+     * Disconnects from the database server
+     *
+     * @return bool  TRUE on success, FALSE on failure
+     */
+    function disconnect()
+    {
+        $ret = @fbsql_close($this->connection);
+        $this->connection = null;
+        return $ret;
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * Sends a query to the database server
+     *
+     * @param string  the SQL query string
+     *
+     * @return mixed  + a PHP result resrouce for successful SELECT queries
+     *                + the DB_OK constant for other successful queries
+     *                + a DB_Error object on failure
+     */
+    function simpleQuery($query)
+    {
+        $this->last_query = $query;
+        $query = $this->modifyQuery($query);
+        $result = @fbsql_query("$query;", $this->connection);
+        if (!$result) {
+            return $this->fbsqlRaiseError();
+        }
+        // Determine which queries that should return data, and which
+        // should return an error code only.
+        if ($this->_checkManip($query)) {
+            return DB_OK;
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * Move the internal fbsql result pointer to the next available result
+     *
+     * @param a valid fbsql result resource
+     *
+     * @access public
+     *
+     * @return true if a result is available otherwise return false
+     */
+    function nextResult($result)
+    {
+        return @fbsql_next_result($result);
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * Places a row from the result set into the given array
+     *
+     * Formating of the array and the data therein are configurable.
+     * See DB_result::fetchInto() for more information.
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::fetchInto() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result    the query result resource
+     * @param array    $arr       the referenced array to put the data in
+     * @param int      $fetchmode how the resulting array should be indexed
+     * @param int      $rownum    the row number to fetch (0 = first row)
+     *
+     * @return mixed  DB_OK on success, NULL when the end of a result set is
+     *                 reached or on failure
+     *
+     * @see DB_result::fetchInto()
+     */
+    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+    {
+        if ($rownum !== null) {
+            if (!@fbsql_data_seek($result, $rownum)) {
+                return null;
+            }
+        }
+        if ($fetchmode & DB_FETCHMODE_ASSOC) {
+            $arr = @fbsql_fetch_array($result, FBSQL_ASSOC);
+            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+                $arr = array_change_key_case($arr, CASE_LOWER);
+            }
+        } else {
+            $arr = @fbsql_fetch_row($result);
+        }
+        if (!$arr) {
+            return null;
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+            $this->_rtrimArrayValues($arr);
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+            $this->_convertNullArrayValuesToEmpty($arr);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * Deletes the result set and frees the memory occupied by the result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::free() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return bool  TRUE on success, FALSE if $result is invalid
+     *
+     * @see DB_result::free()
+     */
+    function freeResult($result)
+    {
+        return is_resource($result) ? fbsql_free_result($result) : false;
+    }
+
+    // }}}
+    // {{{ autoCommit()
+
+    /**
+     * Enables or disables automatic commits
+     *
+     * @param bool $onoff  true turns it on, false turns it off
+     *
+     * @return int  DB_OK on success.  A DB_Error object if the driver
+     *               doesn't support auto-committing transactions.
+     */
+    function autoCommit($onoff=false)
+    {
+        if ($onoff) {
+            $this->query("SET COMMIT TRUE");
+        } else {
+            $this->query("SET COMMIT FALSE");
+        }
+    }
+
+    // }}}
+    // {{{ commit()
+
+    /**
+     * Commits the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function commit()
+    {
+        @fbsql_commit($this->connection);
+    }
+
+    // }}}
+    // {{{ rollback()
+
+    /**
+     * Reverts the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function rollback()
+    {
+        @fbsql_rollback($this->connection);
+    }
+
+    // }}}
+    // {{{ numCols()
+
+    /**
+     * Gets the number of columns in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numCols() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of columns.  A DB_Error object on failure.
+     *
+     * @see DB_result::numCols()
+     */
+    function numCols($result)
+    {
+        $cols = @fbsql_num_fields($result);
+        if (!$cols) {
+            return $this->fbsqlRaiseError();
+        }
+        return $cols;
+    }
+
+    // }}}
+    // {{{ numRows()
+
+    /**
+     * Gets the number of rows in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numRows() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     *
+     * @see DB_result::numRows()
+     */
+    function numRows($result)
+    {
+        $rows = @fbsql_num_rows($result);
+        if ($rows === null) {
+            return $this->fbsqlRaiseError();
+        }
+        return $rows;
+    }
+
+    // }}}
+    // {{{ affectedRows()
+
+    /**
+     * Determines the number of rows affected by a data maniuplation query
+     *
+     * 0 is returned for queries that don't manipulate data.
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     */
+    function affectedRows()
+    {
+        if ($this->_last_query_manip) {
+            $result = @fbsql_affected_rows($this->connection);
+        } else {
+            $result = 0;
+        }
+        return $result;
+     }
+
+    // }}}
+    // {{{ nextId()
+
+    /**
+     * Returns the next free id in a sequence
+     *
+     * @param string  $seq_name  name of the sequence
+     * @param boolean $ondemand  when true, the seqence is automatically
+     *                            created if it does not exist
+     *
+     * @return int  the next id number in the sequence.
+     *               A DB_Error object on failure.
+     *
+     * @see DB_common::nextID(), DB_common::getSequenceName(),
+     *      DB_fbsql::createSequence(), DB_fbsql::dropSequence()
+     */
+    function nextId($seq_name, $ondemand = true)
+    {
+        $seqname = $this->getSequenceName($seq_name);
+        do {
+            $repeat = 0;
+            $this->pushErrorHandling(PEAR_ERROR_RETURN);
+            $result = $this->query('SELECT UNIQUE FROM ' . $seqname);
+            $this->popErrorHandling();
+            if ($ondemand && DB::isError($result) &&
+                $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+                $repeat = 1;
+                $result = $this->createSequence($seq_name);
+                if (DB::isError($result)) {
+                    return $result;
+                }
+            } else {
+                $repeat = 0;
+            }
+        } while ($repeat);
+        if (DB::isError($result)) {
+            return $this->fbsqlRaiseError();
+        }
+        $result->fetchInto($tmp, DB_FETCHMODE_ORDERED);
+        return $tmp[0];
+    }
+
+    /**
+     * Creates a new sequence
+     *
+     * @param string $seq_name  name of the new sequence
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::createSequence(), DB_common::getSequenceName(),
+     *      DB_fbsql::nextID(), DB_fbsql::dropSequence()
+     */
+    function createSequence($seq_name)
+    {
+        $seqname = $this->getSequenceName($seq_name);
+        $res = $this->query('CREATE TABLE ' . $seqname
+                            . ' (id INTEGER NOT NULL,'
+                            . ' PRIMARY KEY(id))');
+        if ($res) {
+            $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname);
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ dropSequence()
+
+    /**
+     * Deletes a sequence
+     *
+     * @param string $seq_name  name of the sequence to be deleted
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+     *      DB_fbsql::nextID(), DB_fbsql::createSequence()
+     */
+    function dropSequence($seq_name)
+    {
+        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)
+                            . ' RESTRICT');
+    }
+
+    // }}}
+    // {{{ modifyLimitQuery()
+
+    /**
+     * Adds LIMIT clauses to a query string according to current DBMS standards
+     *
+     * @param string $query   the query to modify
+     * @param int    $from    the row to start to fetching (0 = the first row)
+     * @param int    $count   the numbers of rows to fetch
+     * @param mixed  $params  array, string or numeric data to be used in
+     *                         execution of the statement.  Quantity of items
+     *                         passed must match quantity of placeholders in
+     *                         query:  meaning 1 placeholder for non-array
+     *                         parameters or 1 placeholder per array element.
+     *
+     * @return string  the query string with LIMIT clauses added
+     *
+     * @access protected
+     */
+    function modifyLimitQuery($query, $from, $count, $params = array())
+    {
+        if (DB::isManip($query) || $this->_next_query_manip) {
+            return preg_replace('/^([\s(])*SELECT/i',
+                                "\\1SELECT TOP($count)", $query);
+        } else {
+            return preg_replace('/([\s(])*SELECT/i',
+                                "\\1SELECT TOP($from, $count)", $query);
+        }
+    }
+
+    // }}}
+    // {{{ quoteBoolean()
+
+    /**
+     * Formats a boolean value for use within a query in a locale-independent
+     * manner.
+     *
+     * @param boolean the boolean value to be quoted.
+     * @return string the quoted string.
+     * @see DB_common::quoteSmart()
+     * @since Method available since release 1.7.8.
+     */
+    function quoteBoolean($boolean) {
+        return $boolean ? 'TRUE' : 'FALSE';
+    }
+     
+    // }}}
+    // {{{ quoteFloat()
+
+    /**
+     * Formats a float value for use within a query in a locale-independent
+     * manner.
+     *
+     * @param float the float value to be quoted.
+     * @return string the quoted string.
+     * @see DB_common::quoteSmart()
+     * @since Method available since release 1.7.8.
+     */
+    function quoteFloat($float) {
+        return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
+    }
+     
+    // }}}
+    // {{{ fbsqlRaiseError()
+
+    /**
+     * Produces a DB_Error object regarding the current problem
+     *
+     * @param int $errno  if the error is being manually raised pass a
+     *                     DB_ERROR* constant here.  If this isn't passed
+     *                     the error information gathered from the DBMS.
+     *
+     * @return object  the DB_Error object
+     *
+     * @see DB_common::raiseError(),
+     *      DB_fbsql::errorNative(), DB_common::errorCode()
+     */
+    function fbsqlRaiseError($errno = null)
+    {
+        if ($errno === null) {
+            $errno = $this->errorCode(fbsql_errno($this->connection));
+        }
+        return $this->raiseError($errno, null, null, null,
+                                 @fbsql_error($this->connection));
+    }
+
+    // }}}
+    // {{{ errorNative()
+
+    /**
+     * Gets the DBMS' native error code produced by the last query
+     *
+     * @return int  the DBMS' error code
+     */
+    function errorNative()
+    {
+        return @fbsql_errno($this->connection);
+    }
+
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table or a result set
+     *
+     * @param object|string  $result  DB_result object from a query or a
+     *                                 string containing the name of a table.
+     *                                 While this also accepts a query result
+     *                                 resource identifier, this behavior is
+     *                                 deprecated.
+     * @param int            $mode    a valid tableInfo mode
+     *
+     * @return array  an associative array with the information requested.
+     *                 A DB_Error object on failure.
+     *
+     * @see DB_common::tableInfo()
+     */
+    function tableInfo($result, $mode = null)
+    {
+        if (is_string($result)) {
+            /*
+             * Probably received a table name.
+             * Create a result resource identifier.
+             */
+            $id = @fbsql_list_fields($this->dsn['database'],
+                                     $result, $this->connection);
+            $got_string = true;
+        } elseif (isset($result->result)) {
+            /*
+             * Probably received a result object.
+             * Extract the result resource identifier.
+             */
+            $id = $result->result;
+            $got_string = false;
+        } else {
+            /*
+             * Probably received a result resource identifier.
+             * Copy it.
+             * Deprecated.  Here for compatibility only.
+             */
+            $id = $result;
+            $got_string = false;
+        }
+
+        if (!is_resource($id)) {
+            return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+        }
+
+        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+            $case_func = 'strtolower';
+        } else {
+            $case_func = 'strval';
+        }
+
+        $count = @fbsql_num_fields($id);
+        $res   = array();
+
+        if ($mode) {
+            $res['num_fields'] = $count;
+        }
+
+        for ($i = 0; $i < $count; $i++) {
+            $res[$i] = array(
+                'table' => $case_func(@fbsql_field_table($id, $i)),
+                'name'  => $case_func(@fbsql_field_name($id, $i)),
+                'type'  => @fbsql_field_type($id, $i),
+                'len'   => @fbsql_field_len($id, $i),
+                'flags' => @fbsql_field_flags($id, $i),
+            );
+            if ($mode & DB_TABLEINFO_ORDER) {
+                $res['order'][$res[$i]['name']] = $i;
+            }
+            if ($mode & DB_TABLEINFO_ORDERTABLE) {
+                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+            }
+        }
+
+        // free the result only if we were called on a table
+        if ($got_string) {
+            @fbsql_free_result($id);
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ getSpecialQuery()
+
+    /**
+     * Obtains the query string needed for listing a given type of objects
+     *
+     * @param string $type  the kind of objects you want to retrieve
+     *
+     * @return string  the SQL query string or null if the driver doesn't
+     *                  support the object type requested
+     *
+     * @access protected
+     * @see DB_common::getListOf()
+     */
+    function getSpecialQuery($type)
+    {
+        switch ($type) {
+            case 'tables':
+                return 'SELECT "table_name" FROM information_schema.tables'
+                       . ' t0, information_schema.schemata t1'
+                       . ' WHERE t0.schema_pk=t1.schema_pk AND'
+                       . ' "table_type" = \'BASE TABLE\''
+                       . ' AND "schema_name" = current_schema';
+            case 'views':
+                return 'SELECT "table_name" FROM information_schema.tables'
+                       . ' t0, information_schema.schemata t1'
+                       . ' WHERE t0.schema_pk=t1.schema_pk AND'
+                       . ' "table_type" = \'VIEW\''
+                       . ' AND "schema_name" = current_schema';
+            case 'users':
+                return 'SELECT "user_name" from information_schema.users'; 
+            case 'functions':
+                return 'SELECT "routine_name" FROM'
+                       . ' information_schema.psm_routines'
+                       . ' t0, information_schema.schemata t1'
+                       . ' WHERE t0.schema_pk=t1.schema_pk'
+                       . ' AND "routine_kind"=\'FUNCTION\''
+                       . ' AND "schema_name" = current_schema';
+            case 'procedures':
+                return 'SELECT "routine_name" FROM'
+                       . ' information_schema.psm_routines'
+                       . ' t0, information_schema.schemata t1'
+                       . ' WHERE t0.schema_pk=t1.schema_pk'
+                       . ' AND "routine_kind"=\'PROCEDURE\''
+                       . ' AND "schema_name" = current_schema';
+            default:
+                return null;
+        }
+    }
+
+    // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/civicrm/vendor/pear/db/DB/ibase.php b/civicrm/vendor/pear/db/DB/ibase.php
new file mode 100644
index 0000000000000000000000000000000000000000..ca0c948bdb9998d7b839ea52a90a8277366e4cb7
--- /dev/null
+++ b/civicrm/vendor/pear/db/DB/ibase.php
@@ -0,0 +1,1082 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's interbase extension
+ * for interacting with Interbase and Firebird databases
+ *
+ * While this class works with PHP 4, PHP's InterBase extension is
+ * unstable in PHP 4.  Use PHP 5.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Sterling Hughes <sterling@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's interbase extension
+ * for interacting with Interbase and Firebird databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * While this class works with PHP 4, PHP's InterBase extension is
+ * unstable in PHP 4.  Use PHP 5.
+ *
+ * NOTICE:  limitQuery() only works for Firebird.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Sterling Hughes <sterling@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/DB
+ * @since      Class became stable in Release 1.7.0
+ */
+class DB_ibase extends DB_common
+{
+    // {{{ properties
+
+    /**
+     * The DB driver type (mysql, oci8, odbc, etc.)
+     * @var string
+     */
+    var $phptype = 'ibase';
+
+    /**
+     * The database syntax variant to be used (db2, access, etc.), if any
+     * @var string
+     */
+    var $dbsyntax = 'ibase';
+
+    /**
+     * The capabilities of this DB implementation
+     *
+     * The 'new_link' element contains the PHP version that first provided
+     * new_link support for this DBMS.  Contains false if it's unsupported.
+     *
+     * Meaning of the 'limit' element:
+     *   + 'emulate' = emulate with fetch row by number
+     *   + 'alter'   = alter the query
+     *   + false     = skip rows
+     *
+     * NOTE: only firebird supports limit.
+     *
+     * @var array
+     */
+    var $features = array(
+        'limit'         => false,
+        'new_link'      => false,
+        'numrows'       => 'emulate',
+        'pconnect'      => true,
+        'prepare'       => true,
+        'ssl'           => false,
+        'transactions'  => true,
+    );
+
+    /**
+     * A mapping of native error codes to DB error codes
+     * @var array
+     */
+    var $errorcode_map = array(
+        -104 => DB_ERROR_SYNTAX,
+        -150 => DB_ERROR_ACCESS_VIOLATION,
+        -151 => DB_ERROR_ACCESS_VIOLATION,
+        -155 => DB_ERROR_NOSUCHTABLE,
+        -157 => DB_ERROR_NOSUCHFIELD,
+        -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
+        -170 => DB_ERROR_MISMATCH,
+        -171 => DB_ERROR_MISMATCH,
+        -172 => DB_ERROR_INVALID,
+        // -204 =>  // Covers too many errors, need to use regex on msg
+        -205 => DB_ERROR_NOSUCHFIELD,
+        -206 => DB_ERROR_NOSUCHFIELD,
+        -208 => DB_ERROR_INVALID,
+        -219 => DB_ERROR_NOSUCHTABLE,
+        -297 => DB_ERROR_CONSTRAINT,
+        -303 => DB_ERROR_INVALID,
+        -413 => DB_ERROR_INVALID_NUMBER,
+        -530 => DB_ERROR_CONSTRAINT,
+        -551 => DB_ERROR_ACCESS_VIOLATION,
+        -552 => DB_ERROR_ACCESS_VIOLATION,
+        // -607 =>  // Covers too many errors, need to use regex on msg
+        -625 => DB_ERROR_CONSTRAINT_NOT_NULL,
+        -803 => DB_ERROR_CONSTRAINT,
+        -804 => DB_ERROR_VALUE_COUNT_ON_ROW,
+        // -902 =>  // Covers too many errors, need to use regex on msg
+        -904 => DB_ERROR_CONNECT_FAILED,
+        -922 => DB_ERROR_NOSUCHDB,
+        -923 => DB_ERROR_CONNECT_FAILED,
+        -924 => DB_ERROR_CONNECT_FAILED
+    );
+
+    /**
+     * The raw database connection created by PHP
+     * @var resource
+     */
+    var $connection;
+
+    /**
+     * The DSN information for connecting to a database
+     * @var array
+     */
+    var $dsn = array();
+
+
+    /**
+     * The number of rows affected by a data manipulation query
+     * @var integer
+     * @access private
+     */
+    var $affected = 0;
+
+    /**
+     * Should data manipulation queries be committed automatically?
+     * @var bool
+     * @access private
+     */
+    var $autocommit = true;
+
+    /**
+     * The prepared statement handle from the most recently executed statement
+     *
+     * {@internal  Mainly here because the InterBase/Firebird API is only
+     * able to retrieve data from result sets if the statemnt handle is
+     * still in scope.}}
+     *
+     * @var resource
+     */
+    var $last_stmt;
+
+    /**
+     * Is the given prepared statement a data manipulation query?
+     * @var array
+     * @access private
+     */
+    var $manip_query = array();
+
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * This constructor calls <kbd>parent::__construct()</kbd>
+     *
+     * @return void
+     */
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database server, log in and open the database
+     *
+     * Don't call this method directly.  Use DB::connect() instead.
+     *
+     * PEAR DB's ibase driver supports the following extra DSN options:
+     *   + buffers    The number of database buffers to allocate for the
+     *                 server-side cache.
+     *   + charset    The default character set for a database.
+     *   + dialect    The default SQL dialect for any statement
+     *                 executed within a connection.  Defaults to the
+     *                 highest one supported by client libraries.
+     *                 Functional only with InterBase 6 and up.
+     *   + role       Functional only with InterBase 5 and up.
+     *
+     * @param array $dsn         the data source name
+     * @param bool  $persistent  should the connection be persistent?
+     *
+     * @return int  DB_OK on success. A DB_Error object on failure.
+     */
+    function connect($dsn, $persistent = false)
+    {
+        if (!PEAR::loadExtension('interbase')) {
+            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+        }
+
+        $this->dsn = $dsn;
+        if ($dsn['dbsyntax']) {
+            $this->dbsyntax = $dsn['dbsyntax'];
+        }
+        if ($this->dbsyntax == 'firebird') {
+            $this->features['limit'] = 'alter';
+        }
+
+        $params = array(
+            $dsn['hostspec']
+                    ? ($dsn['hostspec'] . ':' . $dsn['database'])
+                    : $dsn['database'],
+            $dsn['username'] ? $dsn['username'] : null,
+            $dsn['password'] ? $dsn['password'] : null,
+            isset($dsn['charset']) ? $dsn['charset'] : null,
+            isset($dsn['buffers']) ? $dsn['buffers'] : null,
+            isset($dsn['dialect']) ? $dsn['dialect'] : null,
+            isset($dsn['role'])    ? $dsn['role'] : null,
+        );
+
+        $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
+
+        $this->connection = @call_user_func_array($connect_function, $params);
+        if (!$this->connection) {
+            return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ disconnect()
+
+    /**
+     * Disconnects from the database server
+     *
+     * @return bool  TRUE on success, FALSE on failure
+     */
+    function disconnect()
+    {
+        $ret = @ibase_close($this->connection);
+        $this->connection = null;
+        return $ret;
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * Sends a query to the database server
+     *
+     * @param string  the SQL query string
+     *
+     * @return mixed  + a PHP result resrouce for successful SELECT queries
+     *                + the DB_OK constant for other successful queries
+     *                + a DB_Error object on failure
+     */
+    function simpleQuery($query)
+    {
+        $ismanip = $this->_checkManip($query);
+        $this->last_query = $query;
+        $query = $this->modifyQuery($query);
+        $result = @ibase_query($this->connection, $query);
+
+        if (!$result) {
+            return $this->ibaseRaiseError();
+        }
+        if ($this->autocommit && $ismanip) {
+            @ibase_commit($this->connection);
+        }
+        if ($ismanip) {
+            $this->affected = $result;
+            return DB_OK;
+        } else {
+            $this->affected = 0;
+            return $result;
+        }
+    }
+
+    // }}}
+    // {{{ modifyLimitQuery()
+
+    /**
+     * Adds LIMIT clauses to a query string according to current DBMS standards
+     *
+     * Only works with Firebird.
+     *
+     * @param string $query   the query to modify
+     * @param int    $from    the row to start to fetching (0 = the first row)
+     * @param int    $count   the numbers of rows to fetch
+     * @param mixed  $params  array, string or numeric data to be used in
+     *                         execution of the statement.  Quantity of items
+     *                         passed must match quantity of placeholders in
+     *                         query:  meaning 1 placeholder for non-array
+     *                         parameters or 1 placeholder per array element.
+     *
+     * @return string  the query string with LIMIT clauses added
+     *
+     * @access protected
+     */
+    function modifyLimitQuery($query, $from, $count, $params = array())
+    {
+        if ($this->dsn['dbsyntax'] == 'firebird') {
+            $query = preg_replace('/^([\s(])*SELECT/i',
+                                  "SELECT FIRST $count SKIP $from", $query);
+        }
+        return $query;
+    }
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * Move the internal ibase result pointer to the next available result
+     *
+     * @param a valid fbsql result resource
+     *
+     * @access public
+     *
+     * @return true if a result is available otherwise return false
+     */
+    function nextResult($result)
+    {
+        return false;
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * Places a row from the result set into the given array
+     *
+     * Formating of the array and the data therein are configurable.
+     * See DB_result::fetchInto() for more information.
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::fetchInto() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result    the query result resource
+     * @param array    $arr       the referenced array to put the data in
+     * @param int      $fetchmode how the resulting array should be indexed
+     * @param int      $rownum    the row number to fetch (0 = first row)
+     *
+     * @return mixed  DB_OK on success, NULL when the end of a result set is
+     *                 reached or on failure
+     *
+     * @see DB_result::fetchInto()
+     */
+    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+    {
+        if ($rownum !== null) {
+            return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
+        }
+        if ($fetchmode & DB_FETCHMODE_ASSOC) {
+            if (function_exists('ibase_fetch_assoc')) {
+                $arr = @ibase_fetch_assoc($result);
+            } else {
+                $arr = get_object_vars(ibase_fetch_object($result));
+            }
+            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+                $arr = array_change_key_case($arr, CASE_LOWER);
+            }
+        } else {
+            $arr = @ibase_fetch_row($result);
+        }
+        if (!$arr) {
+            return null;
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+            $this->_rtrimArrayValues($arr);
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+            $this->_convertNullArrayValuesToEmpty($arr);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * Deletes the result set and frees the memory occupied by the result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::free() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return bool  TRUE on success, FALSE if $result is invalid
+     *
+     * @see DB_result::free()
+     */
+    function freeResult($result)
+    {
+        return is_resource($result) ? ibase_free_result($result) : false;
+    }
+
+    // }}}
+    // {{{ freeQuery()
+
+    function freeQuery($query)
+    {
+        return is_resource($query) ? ibase_free_query($query) : false;
+    }
+
+    // }}}
+    // {{{ affectedRows()
+
+    /**
+     * Determines the number of rows affected by a data maniuplation query
+     *
+     * 0 is returned for queries that don't manipulate data.
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     */
+    function affectedRows()
+    {
+        if (is_integer($this->affected)) {
+            return $this->affected;
+        }
+        return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
+    }
+
+    // }}}
+    // {{{ numCols()
+
+    /**
+     * Gets the number of columns in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numCols() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of columns.  A DB_Error object on failure.
+     *
+     * @see DB_result::numCols()
+     */
+    function numCols($result)
+    {
+        $cols = @ibase_num_fields($result);
+        if (!$cols) {
+            return $this->ibaseRaiseError();
+        }
+        return $cols;
+    }
+
+    // }}}
+    // {{{ prepare()
+
+    /**
+     * Prepares a query for multiple execution with execute().
+     *
+     * prepare() requires a generic query as string like <code>
+     *    INSERT INTO numbers VALUES (?, ?, ?)
+     * </code>.  The <kbd>?</kbd> characters are placeholders.
+     *
+     * Three types of placeholders can be used:
+     *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
+     *   + <kbd>!</kbd>  value is inserted 'as is'
+     *   + <kbd>&</kbd>  requires a file name.  The file's contents get
+     *                     inserted into the query (i.e. saving binary
+     *                     data in a db)
+     *
+     * Use backslashes to escape placeholder characters if you don't want
+     * them to be interpreted as placeholders.  Example: <code>
+     *    "UPDATE foo SET col=? WHERE col='over \& under'"
+     * </code>
+     *
+     * @param string $query query to be prepared
+     * @return mixed DB statement resource on success. DB_Error on failure.
+     */
+    function prepare($query)
+    {
+        $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
+                               PREG_SPLIT_DELIM_CAPTURE);
+        $token    = 0;
+        $types    = array();
+        $newquery = '';
+
+        foreach ($tokens as $key => $val) {
+            switch ($val) {
+                case '?':
+                    $types[$token++] = DB_PARAM_SCALAR;
+                    break;
+                case '&':
+                    $types[$token++] = DB_PARAM_OPAQUE;
+                    break;
+                case '!':
+                    $types[$token++] = DB_PARAM_MISC;
+                    break;
+                default:
+                    $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
+                    $newquery .= $tokens[$key] . '?';
+            }
+        }
+
+        $newquery = substr($newquery, 0, -1);
+        $this->last_query = $query;
+        $newquery = $this->modifyQuery($newquery);
+        $stmt = @ibase_prepare($this->connection, $newquery);
+
+        if ($stmt === false) {
+            $stmt = $this->ibaseRaiseError();
+        } else {
+            $this->prepare_types[(int)$stmt] = $types;
+            $this->manip_query[(int)$stmt]   = DB::isManip($query);
+        }
+
+        return $stmt;
+    }
+
+    // }}}
+    // {{{ execute()
+
+    /**
+     * Executes a DB statement prepared with prepare().
+     *
+     * @param resource  $stmt  a DB statement resource returned from prepare()
+     * @param mixed  $data  array, string or numeric data to be used in
+     *                      execution of the statement.  Quantity of items
+     *                      passed must match quantity of placeholders in
+     *                      query:  meaning 1 for non-array items or the
+     *                      quantity of elements in the array.
+     * @return object  a new DB_Result or a DB_Error when fail
+     * @see DB_ibase::prepare()
+     * @access public
+     */
+    function &execute($stmt, $data = array())
+    {
+        $data = (array)$data;
+        $this->last_parameters = $data;
+
+        $types = $this->prepare_types[(int)$stmt];
+        if (count($types) != count($data)) {
+            $tmp = $this->raiseError(DB_ERROR_MISMATCH);
+            return $tmp;
+        }
+
+        $i = 0;
+        foreach ($data as $key => $value) {
+            if ($types[$i] == DB_PARAM_MISC) {
+                /*
+                 * ibase doesn't seem to have the ability to pass a
+                 * parameter along unchanged, so strip off quotes from start
+                 * and end, plus turn two single quotes to one single quote,
+                 * in order to avoid the quotes getting escaped by
+                 * ibase and ending up in the database.
+                 */
+                $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
+                $data[$key] = str_replace("''", "'", $data[$key]);
+            } elseif ($types[$i] == DB_PARAM_OPAQUE) {
+                $fp = @fopen($data[$key], 'rb');
+                if (!$fp) {
+                    $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
+                    return $tmp;
+                }
+                $data[$key] = fread($fp, filesize($data[$key]));
+                fclose($fp);
+            }
+            $i++;
+        }
+
+        array_unshift($data, $stmt);
+
+        $res = call_user_func_array('ibase_execute', $data);
+        if (!$res) {
+            $tmp = $this->ibaseRaiseError();
+            return $tmp;
+        }
+        /* XXX need this?
+        if ($this->autocommit && $this->manip_query[(int)$stmt]) {
+            @ibase_commit($this->connection);
+        }*/
+        $this->last_stmt = $stmt;
+        if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
+            $this->_last_query_manip = true;
+            $this->_next_query_manip = false;
+            $tmp = DB_OK;
+        } else {
+            $this->_last_query_manip = false;
+            $tmp = new DB_result($this, $res);
+        }
+        return $tmp;
+    }
+
+    /**
+     * Frees the internal resources associated with a prepared query
+     *
+     * @param resource $stmt           the prepared statement's PHP resource
+     * @param bool     $free_resource  should the PHP resource be freed too?
+     *                                  Use false if you need to get data
+     *                                  from the result set later.
+     *
+     * @return bool  TRUE on success, FALSE if $result is invalid
+     *
+     * @see DB_ibase::prepare()
+     */
+    function freePrepared($stmt, $free_resource = true)
+    {
+        if (!is_resource($stmt)) {
+            return false;
+        }
+        if ($free_resource) {
+            @ibase_free_query($stmt);
+        }
+        unset($this->prepare_tokens[(int)$stmt]);
+        unset($this->prepare_types[(int)$stmt]);
+        unset($this->manip_query[(int)$stmt]);
+        return true;
+    }
+
+    // }}}
+    // {{{ autoCommit()
+
+    /**
+     * Enables or disables automatic commits
+     *
+     * @param bool $onoff  true turns it on, false turns it off
+     *
+     * @return int  DB_OK on success.  A DB_Error object if the driver
+     *               doesn't support auto-committing transactions.
+     */
+    function autoCommit($onoff = false)
+    {
+        $this->autocommit = $onoff ? 1 : 0;
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ commit()
+
+    /**
+     * Commits the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function commit()
+    {
+        return @ibase_commit($this->connection);
+    }
+
+    // }}}
+    // {{{ rollback()
+
+    /**
+     * Reverts the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function rollback()
+    {
+        return @ibase_rollback($this->connection);
+    }
+
+    // }}}
+    // {{{ transactionInit()
+
+    function transactionInit($trans_args = 0)
+    {
+        return $trans_args
+                ? @ibase_trans($trans_args, $this->connection)
+                : @ibase_trans();
+    }
+
+    // }}}
+    // {{{ nextId()
+
+    /**
+     * Returns the next free id in a sequence
+     *
+     * @param string  $seq_name  name of the sequence
+     * @param boolean $ondemand  when true, the seqence is automatically
+     *                            created if it does not exist
+     *
+     * @return int  the next id number in the sequence.
+     *               A DB_Error object on failure.
+     *
+     * @see DB_common::nextID(), DB_common::getSequenceName(),
+     *      DB_ibase::createSequence(), DB_ibase::dropSequence()
+     */
+    function nextId($seq_name, $ondemand = true)
+    {
+        $sqn = strtoupper($this->getSequenceName($seq_name));
+        $repeat = 0;
+        do {
+            $this->pushErrorHandling(PEAR_ERROR_RETURN);
+            $result = $this->query("SELECT GEN_ID(${sqn}, 1) "
+                                   . 'FROM RDB$GENERATORS '
+                                   . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
+            $this->popErrorHandling();
+            if ($ondemand && DB::isError($result)) {
+                $repeat = 1;
+                $result = $this->createSequence($seq_name);
+                if (DB::isError($result)) {
+                    return $result;
+                }
+            } else {
+                $repeat = 0;
+            }
+        } while ($repeat);
+        if (DB::isError($result)) {
+            return $this->raiseError($result);
+        }
+        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+        $result->free();
+        return $arr[0];
+    }
+
+    // }}}
+    // {{{ createSequence()
+
+    /**
+     * Creates a new sequence
+     *
+     * @param string $seq_name  name of the new sequence
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::createSequence(), DB_common::getSequenceName(),
+     *      DB_ibase::nextID(), DB_ibase::dropSequence()
+     */
+    function createSequence($seq_name)
+    {
+        $sqn = strtoupper($this->getSequenceName($seq_name));
+        $this->pushErrorHandling(PEAR_ERROR_RETURN);
+        $result = $this->query("CREATE GENERATOR ${sqn}");
+        $this->popErrorHandling();
+
+        return $result;
+    }
+
+    // }}}
+    // {{{ dropSequence()
+
+    /**
+     * Deletes a sequence
+     *
+     * @param string $seq_name  name of the sequence to be deleted
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+     *      DB_ibase::nextID(), DB_ibase::createSequence()
+     */
+    function dropSequence($seq_name)
+    {
+        return $this->query('DELETE FROM RDB$GENERATORS '
+                            . "WHERE RDB\$GENERATOR_NAME='"
+                            . strtoupper($this->getSequenceName($seq_name))
+                            . "'");
+    }
+
+    // }}}
+    // {{{ _ibaseFieldFlags()
+
+    /**
+     * Get the column's flags
+     *
+     * Supports "primary_key", "unique_key", "not_null", "default",
+     * "computed" and "blob".
+     *
+     * @param string $field_name  the name of the field
+     * @param string $table_name  the name of the table
+     *
+     * @return string  the flags
+     *
+     * @access private
+     */
+    function _ibaseFieldFlags($field_name, $table_name)
+    {
+        $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
+               .' FROM RDB$INDEX_SEGMENTS I'
+               .'  JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
+               .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
+               .'  AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
+
+        $result = @ibase_query($this->connection, $sql);
+        if (!$result) {
+            return $this->ibaseRaiseError();
+        }
+
+        $flags = '';
+        if ($obj = @ibase_fetch_object($result)) {
+            @ibase_free_result($result);
+            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'PRIMARY KEY') {
+                $flags .= 'primary_key ';
+            }
+            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'UNIQUE') {
+                $flags .= 'unique_key ';
+            }
+        }
+
+        $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
+               .'  R.RDB$DEFAULT_SOURCE AS DSOURCE,'
+               .'  F.RDB$FIELD_TYPE AS FTYPE,'
+               .'  F.RDB$COMPUTED_SOURCE AS CSOURCE'
+               .' FROM RDB$RELATION_FIELDS R '
+               .'  JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
+               .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
+               .'  AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
+
+        $result = @ibase_query($this->connection, $sql);
+        if (!$result) {
+            return $this->ibaseRaiseError();
+        }
+        if ($obj = @ibase_fetch_object($result)) {
+            @ibase_free_result($result);
+            if (isset($obj->NFLAG)) {
+                $flags .= 'not_null ';
+            }
+            if (isset($obj->DSOURCE)) {
+                $flags .= 'default ';
+            }
+            if (isset($obj->CSOURCE)) {
+                $flags .= 'computed ';
+            }
+            if (isset($obj->FTYPE)  && $obj->FTYPE == 261) {
+                $flags .= 'blob ';
+            }
+        }
+
+        return trim($flags);
+    }
+
+    // }}}
+    // {{{ ibaseRaiseError()
+
+    /**
+     * Produces a DB_Error object regarding the current problem
+     *
+     * @param int $errno  if the error is being manually raised pass a
+     *                     DB_ERROR* constant here.  If this isn't passed
+     *                     the error information gathered from the DBMS.
+     *
+     * @return object  the DB_Error object
+     *
+     * @see DB_common::raiseError(),
+     *      DB_ibase::errorNative(), DB_ibase::errorCode()
+     */
+    function &ibaseRaiseError($errno = null)
+    {
+        if ($errno === null) {
+            $errno = $this->errorCode($this->errorNative());
+        }
+        $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg());
+        return $tmp;
+    }
+
+    // }}}
+    // {{{ errorNative()
+
+    /**
+     * Gets the DBMS' native error code produced by the last query
+     *
+     * @return int  the DBMS' error code.  NULL if there is no error code.
+     *
+     * @since Method available since Release 1.7.0
+     */
+    function errorNative()
+    {
+        if (function_exists('ibase_errcode')) {
+            return @ibase_errcode();
+        }
+        if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i',
+                       @ibase_errmsg(), $m)) {
+            return (int)$m[1];
+        }
+        return null;
+    }
+
+    // }}}
+    // {{{ errorCode()
+
+    /**
+     * Maps native error codes to DB's portable ones
+     *
+     * @param int $nativecode  the error code returned by the DBMS
+     *
+     * @return int  the portable DB error code.  Return DB_ERROR if the
+     *               current driver doesn't have a mapping for the
+     *               $nativecode submitted.
+     *
+     * @since Method available since Release 1.7.0
+     */
+    function errorCode($nativecode = null)
+    {
+        if (isset($this->errorcode_map[$nativecode])) {
+            return $this->errorcode_map[$nativecode];
+        }
+
+        static $error_regexps;
+        if (!isset($error_regexps)) {
+            $error_regexps = array(
+                '/generator .* is not defined/'
+                    => DB_ERROR_SYNTAX,  // for compat. w ibase_errcode()
+                '/violation of [\w ]+ constraint/i'
+                    => DB_ERROR_CONSTRAINT,
+                '/table.*(not exist|not found|unknown)/i'
+                    => DB_ERROR_NOSUCHTABLE,
+                '/table .* already exists/i'
+                    => DB_ERROR_ALREADY_EXISTS,
+                '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
+                    => DB_ERROR_ALREADY_EXISTS,
+                '/unsuccessful metadata update .* not found/i'
+                    => DB_ERROR_NOT_FOUND,
+                '/validation error for column .* value "\*\*\* null/i'
+                    => DB_ERROR_CONSTRAINT_NOT_NULL,
+                '/conversion error from string/i'
+                    => DB_ERROR_INVALID_NUMBER,
+                '/no permission for/i'
+                    => DB_ERROR_ACCESS_VIOLATION,
+                '/arithmetic exception, numeric overflow, or string truncation/i'
+                    => DB_ERROR_INVALID,
+                '/feature is not supported/i'
+                    => DB_ERROR_NOT_CAPABLE,
+            );
+        }
+
+        $errormsg = @ibase_errmsg();
+        foreach ($error_regexps as $regexp => $code) {
+            if (preg_match($regexp, $errormsg)) {
+                return $code;
+            }
+        }
+        return DB_ERROR;
+    }
+
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table or a result set
+     *
+     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+     * is a table name.
+     *
+     * @param object|string  $result  DB_result object from a query or a
+     *                                 string containing the name of a table.
+     *                                 While this also accepts a query result
+     *                                 resource identifier, this behavior is
+     *                                 deprecated.
+     * @param int            $mode    a valid tableInfo mode
+     *
+     * @return array  an associative array with the information requested.
+     *                 A DB_Error object on failure.
+     *
+     * @see DB_common::tableInfo()
+     */
+    function tableInfo($result, $mode = null)
+    {
+        if (is_string($result)) {
+            /*
+             * Probably received a table name.
+             * Create a result resource identifier.
+             */
+            $id = @ibase_query($this->connection,
+                               "SELECT * FROM $result WHERE 1=0");
+            $got_string = true;
+        } elseif (isset($result->result)) {
+            /*
+             * Probably received a result object.
+             * Extract the result resource identifier.
+             */
+            $id = $result->result;
+            $got_string = false;
+        } else {
+            /*
+             * Probably received a result resource identifier.
+             * Copy it.
+             * Deprecated.  Here for compatibility only.
+             */
+            $id = $result;
+            $got_string = false;
+        }
+
+        if (!is_resource($id)) {
+            return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
+        }
+
+        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+            $case_func = 'strtolower';
+        } else {
+            $case_func = 'strval';
+        }
+
+        $count = @ibase_num_fields($id);
+        $res   = array();
+
+        if ($mode) {
+            $res['num_fields'] = $count;
+        }
+
+        for ($i = 0; $i < $count; $i++) {
+            $info = @ibase_field_info($id, $i);
+            $res[$i] = array(
+                'table' => $got_string ? $case_func($result) : '',
+                'name'  => $case_func($info['name']),
+                'type'  => $info['type'],
+                'len'   => $info['length'],
+                'flags' => ($got_string)
+                            ? $this->_ibaseFieldFlags($info['name'], $result)
+                            : '',
+            );
+            if ($mode & DB_TABLEINFO_ORDER) {
+                $res['order'][$res[$i]['name']] = $i;
+            }
+            if ($mode & DB_TABLEINFO_ORDERTABLE) {
+                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+            }
+        }
+
+        // free the result only if we were called on a table
+        if ($got_string) {
+            @ibase_free_result($id);
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ getSpecialQuery()
+
+    /**
+     * Obtains the query string needed for listing a given type of objects
+     *
+     * @param string $type  the kind of objects you want to retrieve
+     *
+     * @return string  the SQL query string or null if the driver doesn't
+     *                  support the object type requested
+     *
+     * @access protected
+     * @see DB_common::getListOf()
+     */
+    function getSpecialQuery($type)
+    {
+        switch ($type) {
+            case 'tables':
+                return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
+                       . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
+            case 'views':
+                return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
+            case 'users':
+                return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES';
+            default:
+                return null;
+        }
+    }
+
+    // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/civicrm/vendor/pear/db/DB/ifx.php b/civicrm/vendor/pear/db/DB/ifx.php
new file mode 100644
index 0000000000000000000000000000000000000000..54eacc45a7b08c9aa3c7e8155990d7db1103a2f1
--- /dev/null
+++ b/civicrm/vendor/pear/db/DB/ifx.php
@@ -0,0 +1,683 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's ifx extension
+ * for interacting with Informix databases
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's ifx extension
+ * for interacting with Informix databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * More info on Informix errors can be found at:
+ * http://www.informix.com/answers/english/ierrors.htm
+ *
+ * TODO:
+ *   - set needed env Informix vars on connect
+ *   - implement native prepare/execute
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/DB
+ */
+class DB_ifx extends DB_common
+{
+    // {{{ properties
+
+    /**
+     * The DB driver type (mysql, oci8, odbc, etc.)
+     * @var string
+     */
+    var $phptype = 'ifx';
+
+    /**
+     * The database syntax variant to be used (db2, access, etc.), if any
+     * @var string
+     */
+    var $dbsyntax = 'ifx';
+
+    /**
+     * The capabilities of this DB implementation
+     *
+     * The 'new_link' element contains the PHP version that first provided
+     * new_link support for this DBMS.  Contains false if it's unsupported.
+     *
+     * Meaning of the 'limit' element:
+     *   + 'emulate' = emulate with fetch row by number
+     *   + 'alter'   = alter the query
+     *   + false     = skip rows
+     *
+     * @var array
+     */
+    var $features = array(
+        'limit'         => 'emulate',
+        'new_link'      => false,
+        'numrows'       => 'emulate',
+        'pconnect'      => true,
+        'prepare'       => false,
+        'ssl'           => false,
+        'transactions'  => true,
+    );
+
+    /**
+     * A mapping of native error codes to DB error codes
+     * @var array
+     */
+    var $errorcode_map = array(
+        '-201'    => DB_ERROR_SYNTAX,
+        '-206'    => DB_ERROR_NOSUCHTABLE,
+        '-217'    => DB_ERROR_NOSUCHFIELD,
+        '-236'    => DB_ERROR_VALUE_COUNT_ON_ROW,
+        '-239'    => DB_ERROR_CONSTRAINT,
+        '-253'    => DB_ERROR_SYNTAX,
+        '-268'    => DB_ERROR_CONSTRAINT,
+        '-292'    => DB_ERROR_CONSTRAINT_NOT_NULL,
+        '-310'    => DB_ERROR_ALREADY_EXISTS,
+        '-316'    => DB_ERROR_ALREADY_EXISTS,
+        '-319'    => DB_ERROR_NOT_FOUND,
+        '-329'    => DB_ERROR_NODBSELECTED,
+        '-346'    => DB_ERROR_CONSTRAINT,
+        '-386'    => DB_ERROR_CONSTRAINT_NOT_NULL,
+        '-391'    => DB_ERROR_CONSTRAINT_NOT_NULL,
+        '-554'    => DB_ERROR_SYNTAX,
+        '-691'    => DB_ERROR_CONSTRAINT,
+        '-692'    => DB_ERROR_CONSTRAINT,
+        '-703'    => DB_ERROR_CONSTRAINT_NOT_NULL,
+        '-1202'   => DB_ERROR_DIVZERO,
+        '-1204'   => DB_ERROR_INVALID_DATE,
+        '-1205'   => DB_ERROR_INVALID_DATE,
+        '-1206'   => DB_ERROR_INVALID_DATE,
+        '-1209'   => DB_ERROR_INVALID_DATE,
+        '-1210'   => DB_ERROR_INVALID_DATE,
+        '-1212'   => DB_ERROR_INVALID_DATE,
+        '-1213'   => DB_ERROR_INVALID_NUMBER,
+    );
+
+    /**
+     * The raw database connection created by PHP
+     * @var resource
+     */
+    var $connection;
+
+    /**
+     * The DSN information for connecting to a database
+     * @var array
+     */
+    var $dsn = array();
+
+
+    /**
+     * Should data manipulation queries be committed automatically?
+     * @var bool
+     * @access private
+     */
+    var $autocommit = true;
+
+    /**
+     * The quantity of transactions begun
+     *
+     * {@internal  While this is private, it can't actually be designated
+     * private in PHP 5 because it is directly accessed in the test suite.}}
+     *
+     * @var integer
+     * @access private
+     */
+    var $transaction_opcount = 0;
+
+    /**
+     * The number of rows affected by a data manipulation query
+     * @var integer
+     * @access private
+     */
+    var $affected = 0;
+
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * This constructor calls <kbd>parent::__construct()</kbd>
+     *
+     * @return void
+     */
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database server, log in and open the database
+     *
+     * Don't call this method directly.  Use DB::connect() instead.
+     *
+     * @param array $dsn         the data source name
+     * @param bool  $persistent  should the connection be persistent?
+     *
+     * @return int  DB_OK on success. A DB_Error object on failure.
+     */
+    function connect($dsn, $persistent = false)
+    {
+        if (!PEAR::loadExtension('informix') &&
+            !PEAR::loadExtension('Informix'))
+        {
+            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+        }
+
+        $this->dsn = $dsn;
+        if ($dsn['dbsyntax']) {
+            $this->dbsyntax = $dsn['dbsyntax'];
+        }
+
+        $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : '';
+        $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : '';
+        $user = $dsn['username'] ? $dsn['username'] : '';
+        $pw = $dsn['password'] ? $dsn['password'] : '';
+
+        $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect';
+
+        $this->connection = @$connect_function($dbname, $user, $pw);
+        if (!is_resource($this->connection)) {
+            return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ disconnect()
+
+    /**
+     * Disconnects from the database server
+     *
+     * @return bool  TRUE on success, FALSE on failure
+     */
+    function disconnect()
+    {
+        $ret = @ifx_close($this->connection);
+        $this->connection = null;
+        return $ret;
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * Sends a query to the database server
+     *
+     * @param string  the SQL query string
+     *
+     * @return mixed  + a PHP result resrouce for successful SELECT queries
+     *                + the DB_OK constant for other successful queries
+     *                + a DB_Error object on failure
+     */
+    function simpleQuery($query)
+    {
+        $ismanip = $this->_checkManip($query);
+        $this->last_query = $query;
+        $this->affected   = null;
+        if (preg_match('/(SELECT|EXECUTE)/i', $query)) {    //TESTME: Use !DB::isManip()?
+            // the scroll is needed for fetching absolute row numbers
+            // in a select query result
+            $result = @ifx_query($query, $this->connection, IFX_SCROLL);
+        } else {
+            if (!$this->autocommit && $ismanip) {
+                if ($this->transaction_opcount == 0) {
+                    $result = @ifx_query('BEGIN WORK', $this->connection);
+                    if (!$result) {
+                        return $this->ifxRaiseError();
+                    }
+                }
+                $this->transaction_opcount++;
+            }
+            $result = @ifx_query($query, $this->connection);
+        }
+        if (!$result) {
+            return $this->ifxRaiseError();
+        }
+        $this->affected = @ifx_affected_rows($result);
+        // Determine which queries should return data, and which
+        // should return an error code only.
+        if (preg_match('/(SELECT|EXECUTE)/i', $query)) {
+            return $result;
+        }
+        // XXX Testme: free results inside a transaction
+        // may cause to stop it and commit the work?
+
+        // Result has to be freed even with a insert or update
+        @ifx_free_result($result);
+
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * Move the internal ifx result pointer to the next available result
+     *
+     * @param a valid fbsql result resource
+     *
+     * @access public
+     *
+     * @return true if a result is available otherwise return false
+     */
+    function nextResult($result)
+    {
+        return false;
+    }
+
+    // }}}
+    // {{{ affectedRows()
+
+    /**
+     * Determines the number of rows affected by a data maniuplation query
+     *
+     * 0 is returned for queries that don't manipulate data.
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     */
+    function affectedRows()
+    {
+        if ($this->_last_query_manip) {
+            return $this->affected;
+        } else {
+            return 0;
+        }
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * Places a row from the result set into the given array
+     *
+     * Formating of the array and the data therein are configurable.
+     * See DB_result::fetchInto() for more information.
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::fetchInto() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result    the query result resource
+     * @param array    $arr       the referenced array to put the data in
+     * @param int      $fetchmode how the resulting array should be indexed
+     * @param int      $rownum    the row number to fetch (0 = first row)
+     *
+     * @return mixed  DB_OK on success, NULL when the end of a result set is
+     *                 reached or on failure
+     *
+     * @see DB_result::fetchInto()
+     */
+    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+    {
+        if (($rownum !== null) && ($rownum < 0)) {
+            return null;
+        }
+        if ($rownum === null) {
+            /*
+             * Even though fetch_row() should return the next row  if
+             * $rownum is null, it doesn't in all cases.  Bug 598.
+             */
+            $rownum = 'NEXT';
+        } else {
+            // Index starts at row 1, unlike most DBMS's starting at 0.
+            $rownum++;
+        }
+        if (!$arr = @ifx_fetch_row($result, $rownum)) {
+            return null;
+        }
+        if ($fetchmode !== DB_FETCHMODE_ASSOC) {
+            $i=0;
+            $order = array();
+            foreach ($arr as $val) {
+                $order[$i++] = $val;
+            }
+            $arr = $order;
+        } elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
+                  $this->options['portability'] & DB_PORTABILITY_LOWERCASE)
+        {
+            $arr = array_change_key_case($arr, CASE_LOWER);
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+            $this->_rtrimArrayValues($arr);
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+            $this->_convertNullArrayValuesToEmpty($arr);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ numCols()
+
+    /**
+     * Gets the number of columns in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numCols() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of columns.  A DB_Error object on failure.
+     *
+     * @see DB_result::numCols()
+     */
+    function numCols($result)
+    {
+        if (!$cols = @ifx_num_fields($result)) {
+            return $this->ifxRaiseError();
+        }
+        return $cols;
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * Deletes the result set and frees the memory occupied by the result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::free() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return bool  TRUE on success, FALSE if $result is invalid
+     *
+     * @see DB_result::free()
+     */
+    function freeResult($result)
+    {
+        return is_resource($result) ? ifx_free_result($result) : false;
+    }
+
+    // }}}
+    // {{{ autoCommit()
+
+    /**
+     * Enables or disables automatic commits
+     *
+     * @param bool $onoff  true turns it on, false turns it off
+     *
+     * @return int  DB_OK on success.  A DB_Error object if the driver
+     *               doesn't support auto-committing transactions.
+     */
+    function autoCommit($onoff = true)
+    {
+        // XXX if $this->transaction_opcount > 0, we should probably
+        // issue a warning here.
+        $this->autocommit = $onoff ? true : false;
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ commit()
+
+    /**
+     * Commits the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function commit()
+    {
+        if ($this->transaction_opcount > 0) {
+            $result = @ifx_query('COMMIT WORK', $this->connection);
+            $this->transaction_opcount = 0;
+            if (!$result) {
+                return $this->ifxRaiseError();
+            }
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ rollback()
+
+    /**
+     * Reverts the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function rollback()
+    {
+        if ($this->transaction_opcount > 0) {
+            $result = @ifx_query('ROLLBACK WORK', $this->connection);
+            $this->transaction_opcount = 0;
+            if (!$result) {
+                return $this->ifxRaiseError();
+            }
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ ifxRaiseError()
+
+    /**
+     * Produces a DB_Error object regarding the current problem
+     *
+     * @param int $errno  if the error is being manually raised pass a
+     *                     DB_ERROR* constant here.  If this isn't passed
+     *                     the error information gathered from the DBMS.
+     *
+     * @return object  the DB_Error object
+     *
+     * @see DB_common::raiseError(),
+     *      DB_ifx::errorNative(), DB_ifx::errorCode()
+     */
+    function ifxRaiseError($errno = null)
+    {
+        if ($errno === null) {
+            $errno = $this->errorCode(ifx_error());
+        }
+        return $this->raiseError($errno, null, null, null,
+                                 $this->errorNative());
+    }
+
+    // }}}
+    // {{{ errorNative()
+
+    /**
+     * Gets the DBMS' native error code and message produced by the last query
+     *
+     * @return string  the DBMS' error code and message
+     */
+    function errorNative()
+    {
+        return @ifx_error() . ' ' . @ifx_errormsg();
+    }
+
+    // }}}
+    // {{{ errorCode()
+
+    /**
+     * Maps native error codes to DB's portable ones.
+     *
+     * Requires that the DB implementation's constructor fills
+     * in the <var>$errorcode_map</var> property.
+     *
+     * @param  string  $nativecode  error code returned by the database
+     * @return int a portable DB error code, or DB_ERROR if this DB
+     * implementation has no mapping for the given error code.
+     */
+    function errorCode($nativecode)
+    {
+        if (preg_match('/SQLCODE=(.*)]/', $nativecode, $match)) {
+            $code = $match[1];
+            if (isset($this->errorcode_map[$code])) {
+                return $this->errorcode_map[$code];
+            }
+        }
+        return DB_ERROR;
+    }
+
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table or a result set
+     *
+     * NOTE: only supports 'table' if <var>$result</var> is a table name.
+     *
+     * If analyzing a query result and the result has duplicate field names,
+     * an error will be raised saying
+     * <samp>can't distinguish duplicate field names</samp>.
+     *
+     * @param object|string  $result  DB_result object from a query or a
+     *                                 string containing the name of a table.
+     *                                 While this also accepts a query result
+     *                                 resource identifier, this behavior is
+     *                                 deprecated.
+     * @param int            $mode    a valid tableInfo mode
+     *
+     * @return array  an associative array with the information requested.
+     *                 A DB_Error object on failure.
+     *
+     * @see DB_common::tableInfo()
+     * @since Method available since Release 1.6.0
+     */
+    function tableInfo($result, $mode = null)
+    {
+        if (is_string($result)) {
+            /*
+             * Probably received a table name.
+             * Create a result resource identifier.
+             */
+            $id = @ifx_query("SELECT * FROM $result WHERE 1=0",
+                             $this->connection);
+            $got_string = true;
+        } elseif (isset($result->result)) {
+            /*
+             * Probably received a result object.
+             * Extract the result resource identifier.
+             */
+            $id = $result->result;
+            $got_string = false;
+        } else {
+            /*
+             * Probably received a result resource identifier.
+             * Copy it.
+             */
+            $id = $result;
+            $got_string = false;
+        }
+
+        if (!is_resource($id)) {
+            return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA);
+        }
+
+        $flds = @ifx_fieldproperties($id);
+        $count = @ifx_num_fields($id);
+
+        if (count($flds) != $count) {
+            return $this->raiseError("can't distinguish duplicate field names");
+        }
+
+        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+            $case_func = 'strtolower';
+        } else {
+            $case_func = 'strval';
+        }
+
+        $i   = 0;
+        $res = array();
+
+        if ($mode) {
+            $res['num_fields'] = $count;
+        }
+
+        foreach ($flds as $key => $value) {
+            $props = explode(';', $value);
+            $res[$i] = array(
+                'table' => $got_string ? $case_func($result) : '',
+                'name'  => $case_func($key),
+                'type'  => $props[0],
+                'len'   => $props[1],
+                'flags' => $props[4] == 'N' ? 'not_null' : '',
+            );
+            if ($mode & DB_TABLEINFO_ORDER) {
+                $res['order'][$res[$i]['name']] = $i;
+            }
+            if ($mode & DB_TABLEINFO_ORDERTABLE) {
+                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+            }
+            $i++;
+        }
+
+        // free the result only if we were called on a table
+        if ($got_string) {
+            @ifx_free_result($id);
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ getSpecialQuery()
+
+    /**
+     * Obtains the query string needed for listing a given type of objects
+     *
+     * @param string $type  the kind of objects you want to retrieve
+     *
+     * @return string  the SQL query string or null if the driver doesn't
+     *                  support the object type requested
+     *
+     * @access protected
+     * @see DB_common::getListOf()
+     */
+    function getSpecialQuery($type)
+    {
+        switch ($type) {
+            case 'tables':
+                return 'SELECT tabname FROM systables WHERE tabid >= 100';
+            default:
+                return null;
+        }
+    }
+
+    // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/civicrm/vendor/pear/db/DB/msql.php b/civicrm/vendor/pear/db/DB/msql.php
new file mode 100644
index 0000000000000000000000000000000000000000..202f93eded939ba5bb2cc8469dd8073906f33f35
--- /dev/null
+++ b/civicrm/vendor/pear/db/DB/msql.php
@@ -0,0 +1,831 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's msql extension
+ * for interacting with Mini SQL databases
+ *
+ * PHP's mSQL extension did weird things with NULL values prior to PHP
+ * 4.3.11 and 5.0.4.  Make sure your version of PHP meets or exceeds
+ * those versions.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's msql extension
+ * for interacting with Mini SQL databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * PHP's mSQL extension did weird things with NULL values prior to PHP
+ * 4.3.11 and 5.0.4.  Make sure your version of PHP meets or exceeds
+ * those versions.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/DB
+ * @since      Class not functional until Release 1.7.0
+ */
+class DB_msql extends DB_common
+{
+    // {{{ properties
+
+    /**
+     * The DB driver type (mysql, oci8, odbc, etc.)
+     * @var string
+     */
+    var $phptype = 'msql';
+
+    /**
+     * The database syntax variant to be used (db2, access, etc.), if any
+     * @var string
+     */
+    var $dbsyntax = 'msql';
+
+    /**
+     * The capabilities of this DB implementation
+     *
+     * The 'new_link' element contains the PHP version that first provided
+     * new_link support for this DBMS.  Contains false if it's unsupported.
+     *
+     * Meaning of the 'limit' element:
+     *   + 'emulate' = emulate with fetch row by number
+     *   + 'alter'   = alter the query
+     *   + false     = skip rows
+     *
+     * @var array
+     */
+    var $features = array(
+        'limit'         => 'emulate',
+        'new_link'      => false,
+        'numrows'       => true,
+        'pconnect'      => true,
+        'prepare'       => false,
+        'ssl'           => false,
+        'transactions'  => false,
+    );
+
+    /**
+     * A mapping of native error codes to DB error codes
+     * @var array
+     */
+    var $errorcode_map = array(
+    );
+
+    /**
+     * The raw database connection created by PHP
+     * @var resource
+     */
+    var $connection;
+
+    /**
+     * The DSN information for connecting to a database
+     * @var array
+     */
+    var $dsn = array();
+
+
+    /**
+     * The query result resource created by PHP
+     *
+     * Used to make affectedRows() work.  Only contains the result for
+     * data manipulation queries.  Contains false for other queries.
+     *
+     * @var resource
+     * @access private
+     */
+    var $_result;
+
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * This constructor calls <kbd>parent::__construct()</kbd>
+     *
+     * @return void
+     */
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database server, log in and open the database
+     *
+     * Don't call this method directly.  Use DB::connect() instead.
+     *
+     * Example of how to connect:
+     * <code>
+     * require_once 'DB.php';
+     * 
+     * // $dsn = 'msql://hostname/dbname';  // use a TCP connection
+     * $dsn = 'msql:///dbname';             // use a socket
+     * $options = array(
+     *     'portability' => DB_PORTABILITY_ALL,
+     * );
+     * 
+     * $db = DB::connect($dsn, $options);
+     * if (PEAR::isError($db)) {
+     *     die($db->getMessage());
+     * }
+     * </code>
+     *
+     * @param array $dsn         the data source name
+     * @param bool  $persistent  should the connection be persistent?
+     *
+     * @return int  DB_OK on success. A DB_Error object on failure.
+     */
+    function connect($dsn, $persistent = false)
+    {
+        if (!PEAR::loadExtension('msql')) {
+            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+        }
+
+        $this->dsn = $dsn;
+        if ($dsn['dbsyntax']) {
+            $this->dbsyntax = $dsn['dbsyntax'];
+        }
+
+        $params = array();
+        if ($dsn['hostspec']) {
+            $params[] = $dsn['port']
+                        ? $dsn['hostspec'] . ',' . $dsn['port']
+                        : $dsn['hostspec'];
+        }
+
+        $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect';
+
+        $ini = ini_get('track_errors');
+        $php_errormsg = '';
+        if ($ini) {
+            $this->connection = @call_user_func_array($connect_function,
+                                                      $params);
+        } else {
+            @ini_set('track_errors', 1);
+            $this->connection = @call_user_func_array($connect_function,
+                                                      $params);
+            @ini_set('track_errors', $ini);
+        }
+
+        if (!$this->connection) {
+            if (($err = @msql_error()) != '') {
+                return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+                                         null, null, null,
+                                         $err);
+            } else {
+                return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+                                         null, null, null,
+                                         $php_errormsg);
+            }
+        }
+
+        if (!@msql_select_db($dsn['database'], $this->connection)) {
+            return $this->msqlRaiseError();
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ disconnect()
+
+    /**
+     * Disconnects from the database server
+     *
+     * @return bool  TRUE on success, FALSE on failure
+     */
+    function disconnect()
+    {
+        $ret = @msql_close($this->connection);
+        $this->connection = null;
+        return $ret;
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * Sends a query to the database server
+     *
+     * @param string  the SQL query string
+     *
+     * @return mixed  + a PHP result resrouce for successful SELECT queries
+     *                + the DB_OK constant for other successful queries
+     *                + a DB_Error object on failure
+     */
+    function simpleQuery($query)
+    {
+        $this->last_query = $query;
+        $query = $this->modifyQuery($query);
+        $result = @msql_query($query, $this->connection);
+        if (!$result) {
+            return $this->msqlRaiseError();
+        }
+        // Determine which queries that should return data, and which
+        // should return an error code only.
+        if ($this->_checkManip($query)) {
+            $this->_result = $result;
+            return DB_OK;
+        } else {
+            $this->_result = false;
+            return $result;
+        }
+    }
+
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * Move the internal msql result pointer to the next available result
+     *
+     * @param a valid fbsql result resource
+     *
+     * @access public
+     *
+     * @return true if a result is available otherwise return false
+     */
+    function nextResult($result)
+    {
+        return false;
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * Places a row from the result set into the given array
+     *
+     * Formating of the array and the data therein are configurable.
+     * See DB_result::fetchInto() for more information.
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::fetchInto() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * PHP's mSQL extension did weird things with NULL values prior to PHP
+     * 4.3.11 and 5.0.4.  Make sure your version of PHP meets or exceeds
+     * those versions.
+     *
+     * @param resource $result    the query result resource
+     * @param array    $arr       the referenced array to put the data in
+     * @param int      $fetchmode how the resulting array should be indexed
+     * @param int      $rownum    the row number to fetch (0 = first row)
+     *
+     * @return mixed  DB_OK on success, NULL when the end of a result set is
+     *                 reached or on failure
+     *
+     * @see DB_result::fetchInto()
+     */
+    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+    {
+        if ($rownum !== null) {
+            if (!@msql_data_seek($result, $rownum)) {
+                return null;
+            }
+        }
+        if ($fetchmode & DB_FETCHMODE_ASSOC) {
+            $arr = @msql_fetch_array($result, MSQL_ASSOC);
+            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+                $arr = array_change_key_case($arr, CASE_LOWER);
+            }
+        } else {
+            $arr = @msql_fetch_row($result);
+        }
+        if (!$arr) {
+            return null;
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+            $this->_rtrimArrayValues($arr);
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+            $this->_convertNullArrayValuesToEmpty($arr);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * Deletes the result set and frees the memory occupied by the result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::free() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return bool  TRUE on success, FALSE if $result is invalid
+     *
+     * @see DB_result::free()
+     */
+    function freeResult($result)
+    {
+        return is_resource($result) ? msql_free_result($result) : false;
+    }
+
+    // }}}
+    // {{{ numCols()
+
+    /**
+     * Gets the number of columns in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numCols() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of columns.  A DB_Error object on failure.
+     *
+     * @see DB_result::numCols()
+     */
+    function numCols($result)
+    {
+        $cols = @msql_num_fields($result);
+        if (!$cols) {
+            return $this->msqlRaiseError();
+        }
+        return $cols;
+    }
+
+    // }}}
+    // {{{ numRows()
+
+    /**
+     * Gets the number of rows in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numRows() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     *
+     * @see DB_result::numRows()
+     */
+    function numRows($result)
+    {
+        $rows = @msql_num_rows($result);
+        if ($rows === false) {
+            return $this->msqlRaiseError();
+        }
+        return $rows;
+    }
+
+    // }}}
+    // {{{ affected()
+
+    /**
+     * Determines the number of rows affected by a data maniuplation query
+     *
+     * 0 is returned for queries that don't manipulate data.
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     */
+    function affectedRows()
+    {
+        if (!$this->_result) {
+            return 0;
+        }
+        return msql_affected_rows($this->_result);
+    }
+
+    // }}}
+    // {{{ nextId()
+
+    /**
+     * Returns the next free id in a sequence
+     *
+     * @param string  $seq_name  name of the sequence
+     * @param boolean $ondemand  when true, the seqence is automatically
+     *                            created if it does not exist
+     *
+     * @return int  the next id number in the sequence.
+     *               A DB_Error object on failure.
+     *
+     * @see DB_common::nextID(), DB_common::getSequenceName(),
+     *      DB_msql::createSequence(), DB_msql::dropSequence()
+     */
+    function nextId($seq_name, $ondemand = true)
+    {
+        $seqname = $this->getSequenceName($seq_name);
+        $repeat = false;
+        do {
+            $this->pushErrorHandling(PEAR_ERROR_RETURN);
+            $result = $this->query("SELECT _seq FROM ${seqname}");
+            $this->popErrorHandling();
+            if ($ondemand && DB::isError($result) &&
+                $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+                $repeat = true;
+                $this->pushErrorHandling(PEAR_ERROR_RETURN);
+                $result = $this->createSequence($seq_name);
+                $this->popErrorHandling();
+                if (DB::isError($result)) {
+                    return $this->raiseError($result);
+                }
+            } else {
+                $repeat = false;
+            }
+        } while ($repeat);
+        if (DB::isError($result)) {
+            return $this->raiseError($result);
+        }
+        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+        $result->free();
+        return $arr[0];
+    }
+
+    // }}}
+    // {{{ createSequence()
+
+    /**
+     * Creates a new sequence
+     *
+     * Also creates a new table to associate the sequence with.  Uses
+     * a separate table to ensure portability with other drivers.
+     *
+     * @param string $seq_name  name of the new sequence
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::createSequence(), DB_common::getSequenceName(),
+     *      DB_msql::nextID(), DB_msql::dropSequence()
+     */
+    function createSequence($seq_name)
+    {
+        $seqname = $this->getSequenceName($seq_name);
+        $res = $this->query('CREATE TABLE ' . $seqname
+                            . ' (id INTEGER NOT NULL)');
+        if (DB::isError($res)) {
+            return $res;
+        }
+        $res = $this->query("CREATE SEQUENCE ON ${seqname}");
+        return $res;
+    }
+
+    // }}}
+    // {{{ dropSequence()
+
+    /**
+     * Deletes a sequence
+     *
+     * @param string $seq_name  name of the sequence to be deleted
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+     *      DB_msql::nextID(), DB_msql::createSequence()
+     */
+    function dropSequence($seq_name)
+    {
+        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+    }
+
+    // }}}
+    // {{{ quoteIdentifier()
+
+    /**
+     * mSQL does not support delimited identifiers
+     *
+     * @param string $str  the identifier name to be quoted
+     *
+     * @return object  a DB_Error object
+     *
+     * @see DB_common::quoteIdentifier()
+     * @since Method available since Release 1.7.0
+     */
+    function quoteIdentifier($str)
+    {
+        return $this->raiseError(DB_ERROR_UNSUPPORTED);
+    }
+
+    // }}}
+    // {{{ quoteFloat()
+
+    /**
+     * Formats a float value for use within a query in a locale-independent
+     * manner.
+     *
+     * @param float the float value to be quoted.
+     * @return string the quoted string.
+     * @see DB_common::quoteSmart()
+     * @since Method available since release 1.7.8.
+     */
+    function quoteFloat($float) {
+        return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
+    }
+     
+    // }}}
+    // {{{ escapeSimple()
+
+    /**
+     * Escapes a string according to the current DBMS's standards
+     *
+     * @param string $str  the string to be escaped
+     *
+     * @return string  the escaped string
+     *
+     * @see DB_common::quoteSmart()
+     * @since Method available since Release 1.7.0
+     */
+    function escapeSimple($str)
+    {
+        return addslashes($str);
+    }
+
+    // }}}
+    // {{{ msqlRaiseError()
+
+    /**
+     * Produces a DB_Error object regarding the current problem
+     *
+     * @param int $errno  if the error is being manually raised pass a
+     *                     DB_ERROR* constant here.  If this isn't passed
+     *                     the error information gathered from the DBMS.
+     *
+     * @return object  the DB_Error object
+     *
+     * @see DB_common::raiseError(),
+     *      DB_msql::errorNative(), DB_msql::errorCode()
+     */
+    function msqlRaiseError($errno = null)
+    {
+        $native = $this->errorNative();
+        if ($errno === null) {
+            $errno = $this->errorCode($native);
+        }
+        return $this->raiseError($errno, null, null, null, $native);
+    }
+
+    // }}}
+    // {{{ errorNative()
+
+    /**
+     * Gets the DBMS' native error message produced by the last query
+     *
+     * @return string  the DBMS' error message
+     */
+    function errorNative()
+    {
+        return @msql_error();
+    }
+
+    // }}}
+    // {{{ errorCode()
+
+    /**
+     * Determines PEAR::DB error code from the database's text error message
+     *
+     * @param string $errormsg  the error message returned from the database
+     *
+     * @return integer  the error number from a DB_ERROR* constant
+     */
+    function errorCode($errormsg)
+    {
+        static $error_regexps;
+        
+        // PHP 5.2+ prepends the function name to $php_errormsg, so we need
+        // this hack to work around it, per bug #9599.
+        $errormsg = preg_replace('/^msql[a-z_]+\(\): /', '', $errormsg);
+
+        if (!isset($error_regexps)) {
+            $error_regexps = array(
+                '/^Access to database denied/i'
+                    => DB_ERROR_ACCESS_VIOLATION,
+                '/^Bad index name/i'
+                    => DB_ERROR_ALREADY_EXISTS,
+                '/^Bad order field/i'
+                    => DB_ERROR_SYNTAX,
+                '/^Bad type for comparison/i'
+                    => DB_ERROR_SYNTAX,
+                '/^Can\'t perform LIKE on/i'
+                    => DB_ERROR_SYNTAX,
+                '/^Can\'t use TEXT fields in LIKE comparison/i'
+                    => DB_ERROR_SYNTAX,
+                '/^Couldn\'t create temporary table/i'
+                    => DB_ERROR_CANNOT_CREATE,
+                '/^Error creating table file/i'
+                    => DB_ERROR_CANNOT_CREATE,
+                '/^Field .* cannot be null$/i'
+                    => DB_ERROR_CONSTRAINT_NOT_NULL,
+                '/^Index (field|condition) .* cannot be null$/i'
+                    => DB_ERROR_SYNTAX,
+                '/^Invalid date format/i'
+                    => DB_ERROR_INVALID_DATE,
+                '/^Invalid time format/i'
+                    => DB_ERROR_INVALID,
+                '/^Literal value for .* is wrong type$/i'
+                    => DB_ERROR_INVALID_NUMBER,
+                '/^No Database Selected/i'
+                    => DB_ERROR_NODBSELECTED,
+                '/^No value specified for field/i'
+                    => DB_ERROR_VALUE_COUNT_ON_ROW,
+                '/^Non unique value for unique index/i'
+                    => DB_ERROR_CONSTRAINT,
+                '/^Out of memory for temporary table/i'
+                    => DB_ERROR_CANNOT_CREATE,
+                '/^Permission denied/i'
+                    => DB_ERROR_ACCESS_VIOLATION,
+                '/^Reference to un-selected table/i'
+                    => DB_ERROR_SYNTAX,
+                '/^syntax error/i'
+                    => DB_ERROR_SYNTAX,
+                '/^Table .* exists$/i'
+                    => DB_ERROR_ALREADY_EXISTS,
+                '/^Unknown database/i'
+                    => DB_ERROR_NOSUCHDB,
+                '/^Unknown field/i'
+                    => DB_ERROR_NOSUCHFIELD,
+                '/^Unknown (index|system variable)/i'
+                    => DB_ERROR_NOT_FOUND,
+                '/^Unknown table/i'
+                    => DB_ERROR_NOSUCHTABLE,
+                '/^Unqualified field/i'
+                    => DB_ERROR_SYNTAX,
+            );
+        }
+
+        foreach ($error_regexps as $regexp => $code) {
+            if (preg_match($regexp, $errormsg)) {
+                return $code;
+            }
+        }
+        return DB_ERROR;
+    }
+
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table or a result set
+     *
+     * @param object|string  $result  DB_result object from a query or a
+     *                                 string containing the name of a table.
+     *                                 While this also accepts a query result
+     *                                 resource identifier, this behavior is
+     *                                 deprecated.
+     * @param int            $mode    a valid tableInfo mode
+     *
+     * @return array  an associative array with the information requested.
+     *                 A DB_Error object on failure.
+     *
+     * @see DB_common::setOption()
+     */
+    function tableInfo($result, $mode = null)
+    {
+        if (is_string($result)) {
+            /*
+             * Probably received a table name.
+             * Create a result resource identifier.
+             */
+            $id = @msql_query("SELECT * FROM $result",
+                              $this->connection);
+            $got_string = true;
+        } elseif (isset($result->result)) {
+            /*
+             * Probably received a result object.
+             * Extract the result resource identifier.
+             */
+            $id = $result->result;
+            $got_string = false;
+        } else {
+            /*
+             * Probably received a result resource identifier.
+             * Copy it.
+             * Deprecated.  Here for compatibility only.
+             */
+            $id = $result;
+            $got_string = false;
+        }
+
+        if (!is_resource($id)) {
+            return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
+        }
+
+        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+            $case_func = 'strtolower';
+        } else {
+            $case_func = 'strval';
+        }
+
+        $count = @msql_num_fields($id);
+        $res   = array();
+
+        if ($mode) {
+            $res['num_fields'] = $count;
+        }
+
+        for ($i = 0; $i < $count; $i++) {
+            $tmp = @msql_fetch_field($id);
+
+            $flags = '';
+            if ($tmp->not_null) {
+                $flags .= 'not_null ';
+            }
+            if ($tmp->unique) {
+                $flags .= 'unique_key ';
+            }
+            $flags = trim($flags);
+
+            $res[$i] = array(
+                'table' => $case_func($tmp->table),
+                'name'  => $case_func($tmp->name),
+                'type'  => $tmp->type,
+                'len'   => msql_field_len($id, $i),
+                'flags' => $flags,
+            );
+
+            if ($mode & DB_TABLEINFO_ORDER) {
+                $res['order'][$res[$i]['name']] = $i;
+            }
+            if ($mode & DB_TABLEINFO_ORDERTABLE) {
+                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+            }
+        }
+
+        // free the result only if we were called on a table
+        if ($got_string) {
+            @msql_free_result($id);
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ getSpecialQuery()
+
+    /**
+     * Obtain a list of a given type of objects
+     *
+     * @param string $type  the kind of objects you want to retrieve
+     *
+     * @return array  the array containing the list of objects requested
+     *
+     * @access protected
+     * @see DB_common::getListOf()
+     */
+    function getSpecialQuery($type)
+    {
+        switch ($type) {
+            case 'databases':
+                $id = @msql_list_dbs($this->connection);
+                break;
+            case 'tables':
+                $id = @msql_list_tables($this->dsn['database'],
+                                        $this->connection);
+                break;
+            default:
+                return null;
+        }
+        if (!$id) {
+            return $this->msqlRaiseError();
+        }
+        $out = array();
+        while ($row = @msql_fetch_row($id)) {
+            $out[] = $row[0];
+        }
+        return $out;
+    }
+
+    // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/civicrm/packages/DB/mssql.php b/civicrm/vendor/pear/db/DB/mssql.php
similarity index 99%
rename from civicrm/packages/DB/mssql.php
rename to civicrm/vendor/pear/db/DB/mssql.php
index 31798a5f43553da0be20155902c10c0ed678ecf9..82bf63f478a2d7e37cb09759c366ae5d49710691 100644
--- a/civicrm/packages/DB/mssql.php
+++ b/civicrm/vendor/pear/db/DB/mssql.php
@@ -49,7 +49,7 @@ require_once 'DB/common.php';
  * @author     Daniel Convissor <danielc@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB_mssql extends DB_common
diff --git a/civicrm/packages/DB/mysql.php b/civicrm/vendor/pear/db/DB/mysql.php
similarity index 99%
rename from civicrm/packages/DB/mysql.php
rename to civicrm/vendor/pear/db/DB/mysql.php
index 0ff72a48541a4ccd34d45d1f8180605fba3acbb4..1ca86fa69992c33846b5f9d4085cf623210560e5 100644
--- a/civicrm/packages/DB/mysql.php
+++ b/civicrm/vendor/pear/db/DB/mysql.php
@@ -41,7 +41,7 @@ require_once 'DB/common.php';
  * @author     Daniel Convissor <danielc@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB_mysql extends DB_common
@@ -251,7 +251,7 @@ class DB_mysql extends DB_common
         if (!$this->connection) {
             if (($err = @mysql_error()) != '') {
                 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
-                                         null, null, null,
+                                         null, null, null, 
                                          $err);
             } else {
                 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
@@ -930,7 +930,7 @@ class DB_mysql extends DB_common
                     return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
                 }
             }
-
+            
             /*
              * Probably received a table name.
              * Create a result resource identifier.
diff --git a/civicrm/packages/DB/mysqli.php b/civicrm/vendor/pear/db/DB/mysqli.php
similarity index 99%
rename from civicrm/packages/DB/mysqli.php
rename to civicrm/vendor/pear/db/DB/mysqli.php
index 91707c19d2cb4275d9fbfc013f8c32365fad7604..2ab5438b14dc78dc826d21c76491332505912277 100644
--- a/civicrm/packages/DB/mysqli.php
+++ b/civicrm/vendor/pear/db/DB/mysqli.php
@@ -43,7 +43,7 @@ require_once 'DB/common.php';
  * @author     Daniel Convissor <danielc@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  * @since      Class functional since Release 1.6.3
  */
@@ -256,7 +256,7 @@ class DB_mysqli extends DB_common
      * Example of how to connect using SSL:
      * <code>
      * require_once 'DB.php';
-     *
+     * 
      * $dsn = array(
      *     'phptype'  => 'mysqli',
      *     'username' => 'someuser',
@@ -269,11 +269,11 @@ class DB_mysqli extends DB_common
      *     'capath'   => '/path/to/ca/dir',
      *     'cipher'   => 'AES',
      * );
-     *
+     * 
      * $options = array(
      *     'ssl' => true,
      * );
-     *
+     * 
      * $db = DB::connect($dsn, $options);
      * if (PEAR::isError($db)) {
      *     die($db->getMessage());
diff --git a/civicrm/vendor/pear/db/DB/oci8.php b/civicrm/vendor/pear/db/DB/oci8.php
new file mode 100644
index 0000000000000000000000000000000000000000..06ea8c1a65a6dcb6a1039dd14997995e4b33a4ef
--- /dev/null
+++ b/civicrm/vendor/pear/db/DB/oci8.php
@@ -0,0 +1,1155 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's oci8 extension
+ * for interacting with Oracle databases
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     James L. Pine <jlp@valinux.com>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's oci8 extension
+ * for interacting with Oracle databases
+ *
+ * Definitely works with versions 8 and 9 of Oracle.
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * Be aware...  OCIError() only appears to return anything when given a
+ * statement, so functions return the generic DB_ERROR instead of more
+ * useful errors that have to do with feedback from the database.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     James L. Pine <jlp@valinux.com>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/DB
+ */
+class DB_oci8 extends DB_common
+{
+    // {{{ properties
+
+    /**
+     * The DB driver type (mysql, oci8, odbc, etc.)
+     * @var string
+     */
+    var $phptype = 'oci8';
+
+    /**
+     * The database syntax variant to be used (db2, access, etc.), if any
+     * @var string
+     */
+    var $dbsyntax = 'oci8';
+
+    /**
+     * The capabilities of this DB implementation
+     *
+     * The 'new_link' element contains the PHP version that first provided
+     * new_link support for this DBMS.  Contains false if it's unsupported.
+     *
+     * Meaning of the 'limit' element:
+     *   + 'emulate' = emulate with fetch row by number
+     *   + 'alter'   = alter the query
+     *   + false     = skip rows
+     *
+     * @var array
+     */
+    var $features = array(
+        'limit'         => 'alter',
+        'new_link'      => '5.0.0',
+        'numrows'       => 'subquery',
+        'pconnect'      => true,
+        'prepare'       => true,
+        'ssl'           => false,
+        'transactions'  => true,
+    );
+
+    /**
+     * A mapping of native error codes to DB error codes
+     * @var array
+     */
+    var $errorcode_map = array(
+        1     => DB_ERROR_CONSTRAINT,
+        900   => DB_ERROR_SYNTAX,
+        904   => DB_ERROR_NOSUCHFIELD,
+        913   => DB_ERROR_VALUE_COUNT_ON_ROW,
+        921   => DB_ERROR_SYNTAX,
+        923   => DB_ERROR_SYNTAX,
+        942   => DB_ERROR_NOSUCHTABLE,
+        955   => DB_ERROR_ALREADY_EXISTS,
+        1400  => DB_ERROR_CONSTRAINT_NOT_NULL,
+        1401  => DB_ERROR_INVALID,
+        1407  => DB_ERROR_CONSTRAINT_NOT_NULL,
+        1418  => DB_ERROR_NOT_FOUND,
+        1476  => DB_ERROR_DIVZERO,
+        1722  => DB_ERROR_INVALID_NUMBER,
+        2289  => DB_ERROR_NOSUCHTABLE,
+        2291  => DB_ERROR_CONSTRAINT,
+        2292  => DB_ERROR_CONSTRAINT,
+        2449  => DB_ERROR_CONSTRAINT,
+        12899 => DB_ERROR_INVALID,
+    );
+
+    /**
+     * The raw database connection created by PHP
+     * @var resource
+     */
+    var $connection;
+
+    /**
+     * The DSN information for connecting to a database
+     * @var array
+     */
+    var $dsn = array();
+
+
+    /**
+     * Should data manipulation queries be committed automatically?
+     * @var bool
+     * @access private
+     */
+    var $autocommit = true;
+
+    /**
+     * Stores the $data passed to execute() in the oci8 driver
+     *
+     * Gets reset to array() when simpleQuery() is run.
+     *
+     * Needed in case user wants to call numRows() after prepare/execute
+     * was used.
+     *
+     * @var array
+     * @access private
+     */
+    var $_data = array();
+
+    /**
+     * The result or statement handle from the most recently executed query
+     * @var resource
+     */
+    var $last_stmt;
+
+    /**
+     * Is the given prepared statement a data manipulation query?
+     * @var array
+     * @access private
+     */
+    var $manip_query = array();
+
+    /**
+     * Store of prepared SQL queries.
+     * @var array
+     * @access private
+     */
+    var $_prepared_queries = array();
+
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * This constructor calls <kbd>parent::__construct()</kbd>
+     *
+     * @return void
+     */
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database server, log in and open the database
+     *
+     * Don't call this method directly.  Use DB::connect() instead.
+     *
+     * If PHP is at version 5.0.0 or greater:
+     *   + Generally, oci_connect() or oci_pconnect() are used.
+     *   + But if the new_link DSN option is set to true, oci_new_connect()
+     *     is used.
+     *
+     * When using PHP version 4.x, OCILogon() or OCIPLogon() are used.
+     *
+     * PEAR DB's oci8 driver supports the following extra DSN options:
+     *   + charset       The character set to be used on the connection.
+     *                    Only used if PHP is at version 5.0.0 or greater
+     *                    and the Oracle server is at 9.2 or greater.
+     *                    Available since PEAR DB 1.7.0.
+     *   + new_link      If set to true, causes subsequent calls to
+     *                    connect() to return a new connection link
+     *                    instead of the existing one.  WARNING: this is
+     *                    not portable to other DBMS's.
+     *                    Available since PEAR DB 1.7.0.
+     *
+     * @param array $dsn         the data source name
+     * @param bool  $persistent  should the connection be persistent?
+     *
+     * @return int  DB_OK on success. A DB_Error object on failure.
+     */
+    function connect($dsn, $persistent = false)
+    {
+        if (!PEAR::loadExtension('oci8')) {
+            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+        }
+
+        $this->dsn = $dsn;
+        if ($dsn['dbsyntax']) {
+            $this->dbsyntax = $dsn['dbsyntax'];
+        }
+
+        // Backwards compatibility with DB < 1.7.0
+        if (empty($dsn['database']) && !empty($dsn['hostspec'])) {
+            $db = $dsn['hostspec'];
+        } else {
+            $db = $dsn['database'];
+        }
+
+        if (function_exists('oci_connect')) {
+            if (isset($dsn['new_link'])
+                && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
+            {
+                $connect_function = 'oci_new_connect';
+            } else {
+                $connect_function = $persistent ? 'oci_pconnect'
+                                    : 'oci_connect';
+            }
+            if (isset($this->dsn['port']) && $this->dsn['port']) {
+                $db = '//'.$db.':'.$this->dsn['port'];
+            }
+
+            $char = empty($dsn['charset']) ? null : $dsn['charset'];
+            $this->connection = @$connect_function($dsn['username'],
+                                                   $dsn['password'],
+                                                   $db,
+                                                   $char);
+            $error = OCIError();
+            if (!empty($error) && $error['code'] == 12541) {
+                // Couldn't find TNS listener.  Try direct connection.
+                $this->connection = @$connect_function($dsn['username'],
+                                                       $dsn['password'],
+                                                       null,
+                                                       $char);
+            }
+        } else {
+            $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon';
+            if ($db) {
+                $this->connection = @$connect_function($dsn['username'],
+                                                       $dsn['password'],
+                                                       $db);
+            } elseif ($dsn['username'] || $dsn['password']) {
+                $this->connection = @$connect_function($dsn['username'],
+                                                       $dsn['password']);
+            }
+        }
+
+        if (!$this->connection) {
+            $error = OCIError();
+            $error = (is_array($error)) ? $error['message'] : null;
+            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+                                     null, null, null,
+                                     $error);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ disconnect()
+
+    /**
+     * Disconnects from the database server
+     *
+     * @return bool  TRUE on success, FALSE on failure
+     */
+    function disconnect()
+    {
+        if (function_exists('oci_close')) {
+            $ret = @oci_close($this->connection);
+        } else {
+            $ret = @OCILogOff($this->connection);
+        }
+        $this->connection = null;
+        return $ret;
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * Sends a query to the database server
+     *
+     * To determine how many rows of a result set get buffered using
+     * ocisetprefetch(), see the "result_buffering" option in setOptions().
+     * This option was added in Release 1.7.0.
+     *
+     * @param string  the SQL query string
+     *
+     * @return mixed  + a PHP result resrouce for successful SELECT queries
+     *                + the DB_OK constant for other successful queries
+     *                + a DB_Error object on failure
+     */
+    function simpleQuery($query)
+    {
+        $this->_data = array();
+        $this->last_parameters = array();
+        $this->last_query = $query;
+        $query = $this->modifyQuery($query);
+        $result = @OCIParse($this->connection, $query);
+        if (!$result) {
+            return $this->oci8RaiseError();
+        }
+        if ($this->autocommit) {
+            $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS);
+        } else {
+            $success = @OCIExecute($result,OCI_DEFAULT);
+        }
+        if (!$success) {
+            return $this->oci8RaiseError($result);
+        }
+        $this->last_stmt = $result;
+        if ($this->_checkManip($query)) {
+            return DB_OK;
+        } else {
+            @ocisetprefetch($result, $this->options['result_buffering']);
+            return $result;
+        }
+    }
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * Move the internal oracle result pointer to the next available result
+     *
+     * @param a valid oci8 result resource
+     *
+     * @access public
+     *
+     * @return true if a result is available otherwise return false
+     */
+    function nextResult($result)
+    {
+        return false;
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * Places a row from the result set into the given array
+     *
+     * Formating of the array and the data therein are configurable.
+     * See DB_result::fetchInto() for more information.
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::fetchInto() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result    the query result resource
+     * @param array    $arr       the referenced array to put the data in
+     * @param int      $fetchmode how the resulting array should be indexed
+     * @param int      $rownum    the row number to fetch (0 = first row)
+     *
+     * @return mixed  DB_OK on success, NULL when the end of a result set is
+     *                 reached or on failure
+     *
+     * @see DB_result::fetchInto()
+     */
+    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+    {
+        if ($rownum !== null) {
+            return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+        }
+        if ($fetchmode & DB_FETCHMODE_ASSOC) {
+            $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS);
+            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE &&
+                $moredata)
+            {
+                $arr = array_change_key_case($arr, CASE_LOWER);
+            }
+        } else {
+            $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS);
+        }
+        if (!$moredata) {
+            return null;
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+            $this->_rtrimArrayValues($arr);
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+            $this->_convertNullArrayValuesToEmpty($arr);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * Deletes the result set and frees the memory occupied by the result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::free() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return bool  TRUE on success, FALSE if $result is invalid
+     *
+     * @see DB_result::free()
+     */
+    function freeResult($result)
+    {
+        return is_resource($result) ? OCIFreeStatement($result) : false;
+    }
+
+    /**
+     * Frees the internal resources associated with a prepared query
+     *
+     * @param resource $stmt           the prepared statement's resource
+     * @param bool     $free_resource  should the PHP resource be freed too?
+     *                                  Use false if you need to get data
+     *                                  from the result set later.
+     *
+     * @return bool  TRUE on success, FALSE if $result is invalid
+     *
+     * @see DB_oci8::prepare()
+     */
+    function freePrepared($stmt, $free_resource = true)
+    {
+        if (!is_resource($stmt)) {
+            return false;
+        }
+        if (isset($this->prepare_types[(int)$stmt])) {
+            unset($this->prepare_types[(int)$stmt]);
+            unset($this->manip_query[(int)$stmt]);
+            unset($this->_prepared_queries[(int)$stmt]);
+        } else {
+            return false;
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ numRows()
+
+    /**
+     * Gets the number of rows in a result set
+     *
+     * Only works if the DB_PORTABILITY_NUMROWS portability option
+     * is turned on.
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numRows() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     *
+     * @see DB_result::numRows(), DB_common::setOption()
+     */
+    function numRows($result)
+    {
+        // emulate numRows for Oracle.  yuck.
+        if ($this->options['portability'] & DB_PORTABILITY_NUMROWS &&
+            $result === $this->last_stmt)
+        {
+            $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')';
+            $save_query = $this->last_query;
+            $save_stmt = $this->last_stmt;
+
+            $count = $this->query($countquery);
+
+            // Restore the last query and statement.
+            $this->last_query = $save_query;
+            $this->last_stmt = $save_stmt;
+            
+            if (DB::isError($count) ||
+                DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED)))
+            {
+                return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+            }
+
+            return $row[0];
+        }
+        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+    }
+
+    // }}}
+    // {{{ numCols()
+
+    /**
+     * Gets the number of columns in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numCols() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of columns.  A DB_Error object on failure.
+     *
+     * @see DB_result::numCols()
+     */
+    function numCols($result)
+    {
+        $cols = @OCINumCols($result);
+        if (!$cols) {
+            return $this->oci8RaiseError($result);
+        }
+        return $cols;
+    }
+
+    // }}}
+    // {{{ prepare()
+
+    /**
+     * Prepares a query for multiple execution with execute().
+     *
+     * With oci8, this is emulated.
+     *
+     * prepare() requires a generic query as string like <code>
+     *    INSERT INTO numbers VALUES (?, ?, ?)
+     * </code>.  The <kbd>?</kbd> characters are placeholders.
+     *
+     * Three types of placeholders can be used:
+     *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
+     *   + <kbd>!</kbd>  value is inserted 'as is'
+     *   + <kbd>&</kbd>  requires a file name.  The file's contents get
+     *                     inserted into the query (i.e. saving binary
+     *                     data in a db)
+     *
+     * Use backslashes to escape placeholder characters if you don't want
+     * them to be interpreted as placeholders.  Example: <code>
+     *    "UPDATE foo SET col=? WHERE col='over \& under'"
+     * </code>
+     *
+     * @param string $query  the query to be prepared
+     *
+     * @return mixed  DB statement resource on success. DB_Error on failure.
+     *
+     * @see DB_oci8::execute()
+     */
+    function prepare($query)
+    {
+        $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
+                               PREG_SPLIT_DELIM_CAPTURE);
+        $binds    = count($tokens) - 1;
+        $token    = 0;
+        $types    = array();
+        $newquery = '';
+
+        foreach ($tokens as $key => $val) {
+            switch ($val) {
+                case '?':
+                    $types[$token++] = DB_PARAM_SCALAR;
+                    unset($tokens[$key]);
+                    break;
+                case '&':
+                    $types[$token++] = DB_PARAM_OPAQUE;
+                    unset($tokens[$key]);
+                    break;
+                case '!':
+                    $types[$token++] = DB_PARAM_MISC;
+                    unset($tokens[$key]);
+                    break;
+                default:
+                    $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
+                    if ($key != $binds) {
+                        $newquery .= $tokens[$key] . ':bind' . $token;
+                    } else {
+                        $newquery .= $tokens[$key];
+                    }
+            }
+        }
+
+        $this->last_query = $query;
+        $newquery = $this->modifyQuery($newquery);
+        if (!$stmt = @OCIParse($this->connection, $newquery)) {
+            return $this->oci8RaiseError();
+        }
+        $this->prepare_types[(int)$stmt] = $types;
+        $this->manip_query[(int)$stmt] = DB::isManip($query);
+        $this->_prepared_queries[(int)$stmt] = $newquery;
+        return $stmt;
+    }
+
+    // }}}
+    // {{{ execute()
+
+    /**
+     * Executes a DB statement prepared with prepare().
+     *
+     * To determine how many rows of a result set get buffered using
+     * ocisetprefetch(), see the "result_buffering" option in setOptions().
+     * This option was added in Release 1.7.0.
+     *
+     * @param resource  $stmt  a DB statement resource returned from prepare()
+     * @param mixed  $data  array, string or numeric data to be used in
+     *                      execution of the statement.  Quantity of items
+     *                      passed must match quantity of placeholders in
+     *                      query:  meaning 1 for non-array items or the
+     *                      quantity of elements in the array.
+     *
+     * @return mixed  returns an oic8 result resource for successful SELECT
+     *                queries, DB_OK for other successful queries.
+     *                A DB error object is returned on failure.
+     *
+     * @see DB_oci8::prepare()
+     */
+    function &execute($stmt, $data = array())
+    {
+        $data = (array)$data;
+        $this->last_parameters = $data;
+        $this->last_query = $this->_prepared_queries[(int)$stmt];
+        $this->_data = $data;
+
+        $types = $this->prepare_types[(int)$stmt];
+        if (count($types) != count($data)) {
+            $tmp = $this->raiseError(DB_ERROR_MISMATCH);
+            return $tmp;
+        }
+
+        $i = 0;
+        foreach ($data as $key => $value) {
+            if ($types[$i] == DB_PARAM_MISC) {
+                /*
+                 * Oracle doesn't seem to have the ability to pass a
+                 * parameter along unchanged, so strip off quotes from start
+                 * and end, plus turn two single quotes to one single quote,
+                 * in order to avoid the quotes getting escaped by
+                 * Oracle and ending up in the database.
+                 */
+                $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
+                $data[$key] = str_replace("''", "'", $data[$key]);
+            } elseif ($types[$i] == DB_PARAM_OPAQUE) {
+                $fp = @fopen($data[$key], 'rb');
+                if (!$fp) {
+                    $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
+                    return $tmp;
+                }
+                $data[$key] = fread($fp, filesize($data[$key]));
+                fclose($fp);
+            } elseif ($types[$i] == DB_PARAM_SCALAR) {
+                // Floats have to be converted to a locale-neutral
+                // representation.
+                if (is_float($data[$key])) {
+                    $data[$key] = $this->quoteFloat($data[$key]);
+                }
+            }
+            if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) {
+                $tmp = $this->oci8RaiseError($stmt);
+                return $tmp;
+            }
+            $this->last_query = preg_replace("/:bind$i(?!\d)/",
+                    $this->quoteSmart($data[$key]), $this->last_query, 1);
+            $i++;
+        }
+        if ($this->autocommit) {
+            $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS);
+        } else {
+            $success = @OCIExecute($stmt, OCI_DEFAULT);
+        }
+        if (!$success) {
+            $tmp = $this->oci8RaiseError($stmt);
+            return $tmp;
+        }
+        $this->last_stmt = $stmt;
+        if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
+            $this->_last_query_manip = true;
+            $this->_next_query_manip = false;
+            $tmp = DB_OK;
+        } else {
+            $this->_last_query_manip = false;
+            @ocisetprefetch($stmt, $this->options['result_buffering']);
+            $tmp = new DB_result($this, $stmt);
+        }
+        return $tmp;
+    }
+
+    // }}}
+    // {{{ autoCommit()
+
+    /**
+     * Enables or disables automatic commits
+     *
+     * @param bool $onoff  true turns it on, false turns it off
+     *
+     * @return int  DB_OK on success.  A DB_Error object if the driver
+     *               doesn't support auto-committing transactions.
+     */
+    function autoCommit($onoff = false)
+    {
+        $this->autocommit = (bool)$onoff;;
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ commit()
+
+    /**
+     * Commits the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function commit()
+    {
+        $result = @OCICommit($this->connection);
+        if (!$result) {
+            return $this->oci8RaiseError();
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ rollback()
+
+    /**
+     * Reverts the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function rollback()
+    {
+        $result = @OCIRollback($this->connection);
+        if (!$result) {
+            return $this->oci8RaiseError();
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ affectedRows()
+
+    /**
+     * Determines the number of rows affected by a data maniuplation query
+     *
+     * 0 is returned for queries that don't manipulate data.
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     */
+    function affectedRows()
+    {
+        if ($this->last_stmt === false) {
+            return $this->oci8RaiseError();
+        }
+        $result = @OCIRowCount($this->last_stmt);
+        if ($result === false) {
+            return $this->oci8RaiseError($this->last_stmt);
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ modifyQuery()
+
+    /**
+     * Changes a query string for various DBMS specific reasons
+     *
+     * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle.
+     *
+     * @param string $query  the query string to modify
+     *
+     * @return string  the modified query string
+     *
+     * @access protected
+     */
+    function modifyQuery($query)
+    {
+        if (preg_match('/^\s*SELECT/i', $query) &&
+            !preg_match('/\sFROM\s/i', $query)) {
+            $query .= ' FROM dual';
+        }
+        return $query;
+    }
+
+    // }}}
+    // {{{ modifyLimitQuery()
+
+    /**
+     * Adds LIMIT clauses to a query string according to current DBMS standards
+     *
+     * @param string $query   the query to modify
+     * @param int    $from    the row to start to fetching (0 = the first row)
+     * @param int    $count   the numbers of rows to fetch
+     * @param mixed  $params  array, string or numeric data to be used in
+     *                         execution of the statement.  Quantity of items
+     *                         passed must match quantity of placeholders in
+     *                         query:  meaning 1 placeholder for non-array
+     *                         parameters or 1 placeholder per array element.
+     *
+     * @return string  the query string with LIMIT clauses added
+     *
+     * @access protected
+     */
+    function modifyLimitQuery($query, $from, $count, $params = array())
+    {
+        // Let Oracle return the name of the columns instead of
+        // coding a "home" SQL parser
+
+        if (count($params)) {
+            $result = $this->prepare("SELECT * FROM ($query) "
+                                     . 'WHERE NULL = NULL');
+            $tmp = $this->execute($result, $params);
+        } else {
+            $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL";
+
+            if (!$result = @OCIParse($this->connection, $q_fields)) {
+                $this->last_query = $q_fields;
+                return $this->oci8RaiseError();
+            }
+            if (!@OCIExecute($result, OCI_DEFAULT)) {
+                $this->last_query = $q_fields;
+                return $this->oci8RaiseError($result);
+            }
+        }
+
+        $ncols = OCINumCols($result);
+        $cols  = array();
+        for ( $i = 1; $i <= $ncols; $i++ ) {
+            $cols[] = '"' . OCIColumnName($result, $i) . '"';
+        }
+        $fields = implode(', ', $cols);
+        // XXX Test that (tip by John Lim)
+        //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) {
+        //    // Introduce the FIRST_ROWS Oracle query optimizer
+        //    $query = substr($query, strlen($match[0]), strlen($query));
+        //    $query = "SELECT /* +FIRST_ROWS */ " . $query;
+        //}
+
+        // Construct the query
+        // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2
+        // Perhaps this could be optimized with the use of Unions
+        $query = "SELECT $fields FROM".
+                 "  (SELECT rownum as linenum, $fields FROM".
+                 "      ($query)".
+                 '  WHERE rownum <= '. ($from + $count) .
+                 ') WHERE linenum >= ' . ++$from;
+        return $query;
+    }
+
+    // }}}
+    // {{{ nextId()
+
+    /**
+     * Returns the next free id in a sequence
+     *
+     * @param string  $seq_name  name of the sequence
+     * @param boolean $ondemand  when true, the seqence is automatically
+     *                            created if it does not exist
+     *
+     * @return int  the next id number in the sequence.
+     *               A DB_Error object on failure.
+     *
+     * @see DB_common::nextID(), DB_common::getSequenceName(),
+     *      DB_oci8::createSequence(), DB_oci8::dropSequence()
+     */
+    function nextId($seq_name, $ondemand = true)
+    {
+        $seqname = $this->getSequenceName($seq_name);
+        $repeat = 0;
+        do {
+            $this->expectError(DB_ERROR_NOSUCHTABLE);
+            $result = $this->query("SELECT ${seqname}.nextval FROM dual");
+            $this->popExpect();
+            if ($ondemand && DB::isError($result) &&
+                $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+                $repeat = 1;
+                $result = $this->createSequence($seq_name);
+                if (DB::isError($result)) {
+                    return $this->raiseError($result);
+                }
+            } else {
+                $repeat = 0;
+            }
+        } while ($repeat);
+        if (DB::isError($result)) {
+            return $this->raiseError($result);
+        }
+        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+        return $arr[0];
+    }
+
+    /**
+     * Creates a new sequence
+     *
+     * @param string $seq_name  name of the new sequence
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::createSequence(), DB_common::getSequenceName(),
+     *      DB_oci8::nextID(), DB_oci8::dropSequence()
+     */
+    function createSequence($seq_name)
+    {
+        return $this->query('CREATE SEQUENCE '
+                            . $this->getSequenceName($seq_name));
+    }
+
+    // }}}
+    // {{{ dropSequence()
+
+    /**
+     * Deletes a sequence
+     *
+     * @param string $seq_name  name of the sequence to be deleted
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+     *      DB_oci8::nextID(), DB_oci8::createSequence()
+     */
+    function dropSequence($seq_name)
+    {
+        return $this->query('DROP SEQUENCE '
+                            . $this->getSequenceName($seq_name));
+    }
+
+    // }}}
+    // {{{ oci8RaiseError()
+
+    /**
+     * Produces a DB_Error object regarding the current problem
+     *
+     * @param int $errno  if the error is being manually raised pass a
+     *                     DB_ERROR* constant here.  If this isn't passed
+     *                     the error information gathered from the DBMS.
+     *
+     * @return object  the DB_Error object
+     *
+     * @see DB_common::raiseError(),
+     *      DB_oci8::errorNative(), DB_oci8::errorCode()
+     */
+    function oci8RaiseError($errno = null)
+    {
+        if ($errno === null) {
+            $error = @OCIError($this->connection);
+            return $this->raiseError($this->errorCode($error['code']),
+                                     null, null, null, $error['message']);
+        } elseif (is_resource($errno)) {
+            $error = @OCIError($errno);
+            return $this->raiseError($this->errorCode($error['code']),
+                                     null, null, null, $error['message']);
+        }
+        return $this->raiseError($this->errorCode($errno));
+    }
+
+    // }}}
+    // {{{ errorNative()
+
+    /**
+     * Gets the DBMS' native error code produced by the last query
+     *
+     * @return int  the DBMS' error code.  FALSE if the code could not be
+     *               determined
+     */
+    function errorNative()
+    {
+        if (is_resource($this->last_stmt)) {
+            $error = @OCIError($this->last_stmt);
+        } else {
+            $error = @OCIError($this->connection);
+        }
+        if (is_array($error)) {
+            return $error['code'];
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table or a result set
+     *
+     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+     * is a table name.
+     *
+     * NOTE: flags won't contain index information.
+     *
+     * @param object|string  $result  DB_result object from a query or a
+     *                                 string containing the name of a table.
+     *                                 While this also accepts a query result
+     *                                 resource identifier, this behavior is
+     *                                 deprecated.
+     * @param int            $mode    a valid tableInfo mode
+     *
+     * @return array  an associative array with the information requested.
+     *                 A DB_Error object on failure.
+     *
+     * @see DB_common::tableInfo()
+     */
+    function tableInfo($result, $mode = null)
+    {
+        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+            $case_func = 'strtolower';
+        } else {
+            $case_func = 'strval';
+        }
+
+        $res = array();
+
+        if (is_string($result)) {
+            /*
+             * Probably received a table name.
+             * Create a result resource identifier.
+             */
+            $result = strtoupper($result);
+            $q_fields = 'SELECT column_name, data_type, data_length, '
+                        . 'nullable '
+                        . 'FROM user_tab_columns '
+                        . "WHERE table_name='$result' ORDER BY column_id";
+
+            $this->last_query = $q_fields;
+
+            if (!$stmt = @OCIParse($this->connection, $q_fields)) {
+                return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA);
+            }
+            if (!@OCIExecute($stmt, OCI_DEFAULT)) {
+                return $this->oci8RaiseError($stmt);
+            }
+            
+            $i = 0;
+            while (@OCIFetch($stmt)) {
+                $res[$i] = array(
+                    'table' => $case_func($result),
+                    'name'  => $case_func(@OCIResult($stmt, 1)),
+                    'type'  => @OCIResult($stmt, 2),
+                    'len'   => @OCIResult($stmt, 3),
+                    'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '',
+                );
+                if ($mode & DB_TABLEINFO_ORDER) {
+                    $res['order'][$res[$i]['name']] = $i;
+                }
+                if ($mode & DB_TABLEINFO_ORDERTABLE) {
+                    $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+                }
+                $i++;
+            }
+
+            if ($mode) {
+                $res['num_fields'] = $i;
+            }
+            @OCIFreeStatement($stmt);
+
+        } else {
+            if (isset($result->result)) {
+                /*
+                 * Probably received a result object.
+                 * Extract the result resource identifier.
+                 */
+                $result = $result->result;
+            }
+
+            $res = array();
+
+            if ($result === $this->last_stmt) {
+                $count = @OCINumCols($result);
+                if ($mode) {
+                    $res['num_fields'] = $count;
+                }
+                for ($i = 0; $i < $count; $i++) {
+                    $res[$i] = array(
+                        'table' => '',
+                        'name'  => $case_func(@OCIColumnName($result, $i+1)),
+                        'type'  => @OCIColumnType($result, $i+1),
+                        'len'   => @OCIColumnSize($result, $i+1),
+                        'flags' => '',
+                    );
+                    if ($mode & DB_TABLEINFO_ORDER) {
+                        $res['order'][$res[$i]['name']] = $i;
+                    }
+                    if ($mode & DB_TABLEINFO_ORDERTABLE) {
+                        $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+                    }
+                }
+            } else {
+                return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+            }
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ getSpecialQuery()
+
+    /**
+     * Obtains the query string needed for listing a given type of objects
+     *
+     * @param string $type  the kind of objects you want to retrieve
+     *
+     * @return string  the SQL query string or null if the driver doesn't
+     *                  support the object type requested
+     *
+     * @access protected
+     * @see DB_common::getListOf()
+     */
+    function getSpecialQuery($type)
+    {
+        switch ($type) {
+            case 'tables':
+                return 'SELECT table_name FROM user_tables';
+            case 'synonyms':
+                return 'SELECT synonym_name FROM user_synonyms';
+            case 'views':
+                return 'SELECT view_name FROM user_views';
+            default:
+                return null;
+        }
+    }
+
+    // }}}
+    // {{{ quoteFloat()
+
+    /**
+     * Formats a float value for use within a query in a locale-independent
+     * manner.
+     *
+     * @param float the float value to be quoted.
+     * @return string the quoted string.
+     * @see DB_common::quoteSmart()
+     * @since Method available since release 1.7.8.
+     */
+    function quoteFloat($float) {
+        return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
+    }
+     
+    // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/civicrm/packages/DB/odbc.php b/civicrm/vendor/pear/db/DB/odbc.php
similarity index 99%
rename from civicrm/packages/DB/odbc.php
rename to civicrm/vendor/pear/db/DB/odbc.php
index f410a127159217740d97933a90481319410c17e6..a664331329ce153f3918569f787fa756d82ff0cb 100644
--- a/civicrm/packages/DB/odbc.php
+++ b/civicrm/vendor/pear/db/DB/odbc.php
@@ -44,7 +44,7 @@ require_once 'DB/common.php';
  * @author     Daniel Convissor <danielc@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB_odbc extends DB_common
diff --git a/civicrm/packages/DB/pgsql.php b/civicrm/vendor/pear/db/DB/pgsql.php
similarity index 99%
rename from civicrm/packages/DB/pgsql.php
rename to civicrm/vendor/pear/db/DB/pgsql.php
index 5d045dfdab32ff0c7f57eb66d3b987a536b12daa..8a75ca61f5ffdce4441cb51882600b672aecd028 100644
--- a/civicrm/packages/DB/pgsql.php
+++ b/civicrm/vendor/pear/db/DB/pgsql.php
@@ -43,7 +43,7 @@ require_once 'DB/common.php';
  * @author     Daniel Convissor <danielc@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB_pgsql extends DB_common
@@ -187,12 +187,12 @@ class DB_pgsql extends DB_common
      * Example of connecting to a new link via a socket:
      * <code>
      * require_once 'DB.php';
-     *
+     * 
      * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true';
      * $options = array(
      *     'portability' => DB_PORTABILITY_ALL,
      * );
-     *
+     * 
      * $db = DB::connect($dsn, $options);
      * if (PEAR::isError($db)) {
      *     die($db->getMessage());
@@ -480,7 +480,7 @@ class DB_pgsql extends DB_common
     function quoteBoolean($boolean) {
         return $boolean ? 'TRUE' : 'FALSE';
     }
-
+     
     // }}}
     // {{{ escapeSimple()
 
@@ -784,7 +784,7 @@ class DB_pgsql extends DB_common
     /**
      * Gets the DBMS' native error message produced by the last query
      *
-     * {@internal Error messages are used instead of error codes
+     * {@internal Error messages are used instead of error codes 
      * in order to support older versions of PostgreSQL.}}
      *
      * @return string  the DBMS' error message
diff --git a/civicrm/vendor/pear/db/DB/sqlite.php b/civicrm/vendor/pear/db/DB/sqlite.php
new file mode 100644
index 0000000000000000000000000000000000000000..b81a43f135cc08257182ec3aeaa3229371781aa2
--- /dev/null
+++ b/civicrm/vendor/pear/db/DB/sqlite.php
@@ -0,0 +1,963 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's sqlite extension
+ * for interacting with SQLite databases
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Urs Gehrig <urs@circle.ch>
+ * @author     Mika Tuupola <tuupola@appelsiini.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0 3.0
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's sqlite extension
+ * for interacting with SQLite databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * NOTICE:  This driver needs PHP's track_errors ini setting to be on.
+ * It is automatically turned on when connecting to the database.
+ * Make sure your scripts don't turn it off.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Urs Gehrig <urs@circle.ch>
+ * @author     Mika Tuupola <tuupola@appelsiini.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0 3.0
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/DB
+ */
+class DB_sqlite extends DB_common
+{
+    // {{{ properties
+
+    /**
+     * The DB driver type (mysql, oci8, odbc, etc.)
+     * @var string
+     */
+    var $phptype = 'sqlite';
+
+    /**
+     * The database syntax variant to be used (db2, access, etc.), if any
+     * @var string
+     */
+    var $dbsyntax = 'sqlite';
+
+    /**
+     * The capabilities of this DB implementation
+     *
+     * The 'new_link' element contains the PHP version that first provided
+     * new_link support for this DBMS.  Contains false if it's unsupported.
+     *
+     * Meaning of the 'limit' element:
+     *   + 'emulate' = emulate with fetch row by number
+     *   + 'alter'   = alter the query
+     *   + false     = skip rows
+     *
+     * @var array
+     */
+    var $features = array(
+        'limit'         => 'alter',
+        'new_link'      => false,
+        'numrows'       => true,
+        'pconnect'      => true,
+        'prepare'       => false,
+        'ssl'           => false,
+        'transactions'  => false,
+    );
+
+    /**
+     * A mapping of native error codes to DB error codes
+     *
+     * {@internal  Error codes according to sqlite_exec.  See the online
+     * manual at http://sqlite.org/c_interface.html for info.
+     * This error handling based on sqlite_exec is not yet implemented.}}
+     *
+     * @var array
+     */
+    var $errorcode_map = array(
+    );
+
+    /**
+     * The raw database connection created by PHP
+     * @var resource
+     */
+    var $connection;
+
+    /**
+     * The DSN information for connecting to a database
+     * @var array
+     */
+    var $dsn = array();
+
+
+    /**
+     * SQLite data types
+     *
+     * @link http://www.sqlite.org/datatypes.html
+     *
+     * @var array
+     */
+    var $keywords = array (
+        'BLOB'      => '',
+        'BOOLEAN'   => '',
+        'CHARACTER' => '',
+        'CLOB'      => '',
+        'FLOAT'     => '',
+        'INTEGER'   => '',
+        'KEY'       => '',
+        'NATIONAL'  => '',
+        'NUMERIC'   => '',
+        'NVARCHAR'  => '',
+        'PRIMARY'   => '',
+        'TEXT'      => '',
+        'TIMESTAMP' => '',
+        'UNIQUE'    => '',
+        'VARCHAR'   => '',
+        'VARYING'   => '',
+    );
+
+    /**
+     * The most recent error message from $php_errormsg
+     * @var string
+     * @access private
+     */
+    var $_lasterror = '';
+
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * This constructor calls <kbd>parent::__construct()</kbd>
+     *
+     * @return void
+     */
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database server, log in and open the database
+     *
+     * Don't call this method directly.  Use DB::connect() instead.
+     *
+     * PEAR DB's sqlite driver supports the following extra DSN options:
+     *   + mode  The permissions for the database file, in four digit
+     *            chmod octal format (eg "0600").
+     *
+     * Example of connecting to a database in read-only mode:
+     * <code>
+     * require_once 'DB.php';
+     * 
+     * $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400';
+     * $options = array(
+     *     'portability' => DB_PORTABILITY_ALL,
+     * );
+     * 
+     * $db = DB::connect($dsn, $options);
+     * if (PEAR::isError($db)) {
+     *     die($db->getMessage());
+     * }
+     * </code>
+     *
+     * @param array $dsn         the data source name
+     * @param bool  $persistent  should the connection be persistent?
+     *
+     * @return int  DB_OK on success. A DB_Error object on failure.
+     */
+    function connect($dsn, $persistent = false)
+    {
+        if (!PEAR::loadExtension('sqlite')) {
+            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+        }
+
+        $this->dsn = $dsn;
+        if ($dsn['dbsyntax']) {
+            $this->dbsyntax = $dsn['dbsyntax'];
+        }
+
+        if (!$dsn['database']) {
+            return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
+        }
+
+        if ($dsn['database'] !== ':memory:') {
+            if (!file_exists($dsn['database'])) {
+                if (!touch($dsn['database'])) {
+                    return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
+                }
+                if (!isset($dsn['mode']) ||
+                    !is_numeric($dsn['mode']))
+                {
+                    $mode = 0644;
+                } else {
+                    $mode = octdec($dsn['mode']);
+                }
+                if (!chmod($dsn['database'], $mode)) {
+                    return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
+                }
+                if (!file_exists($dsn['database'])) {
+                    return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
+                }
+            }
+            if (!is_file($dsn['database'])) {
+                return $this->sqliteRaiseError(DB_ERROR_INVALID);
+            }
+            if (!is_readable($dsn['database'])) {
+                return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
+            }
+        }
+
+        $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open';
+
+        // track_errors must remain on for simpleQuery()
+        @ini_set('track_errors', 1);
+        $php_errormsg = '';
+
+        if (!$this->connection = @$connect_function($dsn['database'])) {
+            return $this->raiseError(DB_ERROR_NODBSELECTED,
+                                     null, null, null,
+                                     $php_errormsg);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ disconnect()
+
+    /**
+     * Disconnects from the database server
+     *
+     * @return bool  TRUE on success, FALSE on failure
+     */
+    function disconnect()
+    {
+        $ret = @sqlite_close($this->connection);
+        $this->connection = null;
+        return $ret;
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * Sends a query to the database server
+     *
+     * NOTICE:  This method needs PHP's track_errors ini setting to be on.
+     * It is automatically turned on when connecting to the database.
+     * Make sure your scripts don't turn it off.
+     *
+     * @param string  the SQL query string
+     *
+     * @return mixed  + a PHP result resrouce for successful SELECT queries
+     *                + the DB_OK constant for other successful queries
+     *                + a DB_Error object on failure
+     */
+    function simpleQuery($query)
+    {
+        $ismanip = $this->_checkManip($query);
+        $this->last_query = $query;
+        $query = $this->modifyQuery($query);
+
+        $php_errormsg = '';
+
+        $result = @sqlite_query($query, $this->connection);
+        $this->_lasterror = $php_errormsg ? $php_errormsg : '';
+
+        $this->result = $result;
+        if (!$this->result) {
+            return $this->sqliteRaiseError(null);
+        }
+
+        // sqlite_query() seems to allways return a resource
+        // so cant use that. Using $ismanip instead
+        if (!$ismanip) {
+            $numRows = $this->numRows($result);
+            if (is_object($numRows)) {
+                // we've got PEAR_Error
+                return $numRows;
+            }
+            return $result;
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * Move the internal sqlite result pointer to the next available result
+     *
+     * @param resource $result  the valid sqlite result resource
+     *
+     * @return bool  true if a result is available otherwise return false
+     */
+    function nextResult($result)
+    {
+        return false;
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * Places a row from the result set into the given array
+     *
+     * Formating of the array and the data therein are configurable.
+     * See DB_result::fetchInto() for more information.
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::fetchInto() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result    the query result resource
+     * @param array    $arr       the referenced array to put the data in
+     * @param int      $fetchmode how the resulting array should be indexed
+     * @param int      $rownum    the row number to fetch (0 = first row)
+     *
+     * @return mixed  DB_OK on success, NULL when the end of a result set is
+     *                 reached or on failure
+     *
+     * @see DB_result::fetchInto()
+     */
+    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+    {
+        if ($rownum !== null) {
+            if (!@sqlite_seek($this->result, $rownum)) {
+                return null;
+            }
+        }
+        if ($fetchmode & DB_FETCHMODE_ASSOC) {
+            $arr = @sqlite_fetch_array($result, SQLITE_ASSOC);
+            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+                $arr = array_change_key_case($arr, CASE_LOWER);
+            }
+
+            /* Remove extraneous " characters from the fields in the result.
+             * Fixes bug #11716. */
+            if (is_array($arr) && count($arr) > 0) {
+                $strippedArr = array();
+                foreach ($arr as $field => $value) {
+                    $strippedArr[trim($field, '"')] = $value;
+                }
+                $arr = $strippedArr;
+            }
+        } else {
+            $arr = @sqlite_fetch_array($result, SQLITE_NUM);
+        }
+        if (!$arr) {
+            return null;
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+            /*
+             * Even though this DBMS already trims output, we do this because
+             * a field might have intentional whitespace at the end that
+             * gets removed by DB_PORTABILITY_RTRIM under another driver.
+             */
+            $this->_rtrimArrayValues($arr);
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+            $this->_convertNullArrayValuesToEmpty($arr);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * Deletes the result set and frees the memory occupied by the result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::free() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return bool  TRUE on success, FALSE if $result is invalid
+     *
+     * @see DB_result::free()
+     */
+    function freeResult(&$result)
+    {
+        // XXX No native free?
+        if (!is_resource($result)) {
+            return false;
+        }
+        $result = null;
+        return true;
+    }
+
+    // }}}
+    // {{{ numCols()
+
+    /**
+     * Gets the number of columns in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numCols() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of columns.  A DB_Error object on failure.
+     *
+     * @see DB_result::numCols()
+     */
+    function numCols($result)
+    {
+        $cols = @sqlite_num_fields($result);
+        if (!$cols) {
+            return $this->sqliteRaiseError();
+        }
+        return $cols;
+    }
+
+    // }}}
+    // {{{ numRows()
+
+    /**
+     * Gets the number of rows in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numRows() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     *
+     * @see DB_result::numRows()
+     */
+    function numRows($result)
+    {
+        $rows = @sqlite_num_rows($result);
+        if ($rows === null) {
+            return $this->sqliteRaiseError();
+        }
+        return $rows;
+    }
+
+    // }}}
+    // {{{ affected()
+
+    /**
+     * Determines the number of rows affected by a data maniuplation query
+     *
+     * 0 is returned for queries that don't manipulate data.
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     */
+    function affectedRows()
+    {
+        return @sqlite_changes($this->connection);
+    }
+
+    // }}}
+    // {{{ dropSequence()
+
+    /**
+     * Deletes a sequence
+     *
+     * @param string $seq_name  name of the sequence to be deleted
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+     *      DB_sqlite::nextID(), DB_sqlite::createSequence()
+     */
+    function dropSequence($seq_name)
+    {
+        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+    }
+
+    /**
+     * Creates a new sequence
+     *
+     * @param string $seq_name  name of the new sequence
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::createSequence(), DB_common::getSequenceName(),
+     *      DB_sqlite::nextID(), DB_sqlite::dropSequence()
+     */
+    function createSequence($seq_name)
+    {
+        $seqname = $this->getSequenceName($seq_name);
+        $query   = 'CREATE TABLE ' . $seqname .
+                   ' (id INTEGER UNSIGNED PRIMARY KEY) ';
+        $result  = $this->query($query);
+        if (DB::isError($result)) {
+            return($result);
+        }
+        $query   = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname
+                    BEGIN
+                        DELETE FROM $seqname WHERE id<LAST_INSERT_ROWID();
+                    END ";
+        $result  = $this->query($query);
+        if (DB::isError($result)) {
+            return($result);
+        }
+    }
+
+    // }}}
+    // {{{ nextId()
+
+    /**
+     * Returns the next free id in a sequence
+     *
+     * @param string  $seq_name  name of the sequence
+     * @param boolean $ondemand  when true, the seqence is automatically
+     *                            created if it does not exist
+     *
+     * @return int  the next id number in the sequence.
+     *               A DB_Error object on failure.
+     *
+     * @see DB_common::nextID(), DB_common::getSequenceName(),
+     *      DB_sqlite::createSequence(), DB_sqlite::dropSequence()
+     */
+    function nextId($seq_name, $ondemand = true)
+    {
+        $seqname = $this->getSequenceName($seq_name);
+
+        do {
+            $repeat = 0;
+            $this->pushErrorHandling(PEAR_ERROR_RETURN);
+            $result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)");
+            $this->popErrorHandling();
+            if ($result === DB_OK) {
+                $id = @sqlite_last_insert_rowid($this->connection);
+                if ($id != 0) {
+                    return $id;
+                }
+            } elseif ($ondemand && DB::isError($result) &&
+                      $result->getCode() == DB_ERROR_NOSUCHTABLE)
+            {
+                $result = $this->createSequence($seq_name);
+                if (DB::isError($result)) {
+                    return $this->raiseError($result);
+                } else {
+                    $repeat = 1;
+                }
+            }
+        } while ($repeat);
+
+        return $this->raiseError($result);
+    }
+
+    // }}}
+    // {{{ getDbFileStats()
+
+    /**
+     * Get the file stats for the current database
+     *
+     * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size,
+     * atime, mtime, ctime, blksize, blocks or a numeric key between
+     * 0 and 12.
+     *
+     * @param string $arg  the array key for stats()
+     *
+     * @return mixed  an array on an unspecified key, integer on a passed
+     *                arg and false at a stats error
+     */
+    function getDbFileStats($arg = '')
+    {
+        $stats = stat($this->dsn['database']);
+        if ($stats == false) {
+            return false;
+        }
+        if (is_array($stats)) {
+            if (is_numeric($arg)) {
+                if (((int)$arg <= 12) & ((int)$arg >= 0)) {
+                    return false;
+                }
+                return $stats[$arg ];
+            }
+            if (array_key_exists(trim($arg), $stats)) {
+                return $stats[$arg ];
+            }
+        }
+        return $stats;
+    }
+
+    // }}}
+    // {{{ escapeSimple()
+
+    /**
+     * Escapes a string according to the current DBMS's standards
+     *
+     * In SQLite, this makes things safe for inserts/updates, but may
+     * cause problems when performing text comparisons against columns
+     * containing binary data. See the
+     * {@link http://php.net/sqlite_escape_string PHP manual} for more info.
+     *
+     * @param string $str  the string to be escaped
+     *
+     * @return string  the escaped string
+     *
+     * @since Method available since Release 1.6.1
+     * @see DB_common::escapeSimple()
+     */
+    function escapeSimple($str)
+    {
+        return @sqlite_escape_string($str);
+    }
+
+    // }}}
+    // {{{ modifyLimitQuery()
+
+    /**
+     * Adds LIMIT clauses to a query string according to current DBMS standards
+     *
+     * @param string $query   the query to modify
+     * @param int    $from    the row to start to fetching (0 = the first row)
+     * @param int    $count   the numbers of rows to fetch
+     * @param mixed  $params  array, string or numeric data to be used in
+     *                         execution of the statement.  Quantity of items
+     *                         passed must match quantity of placeholders in
+     *                         query:  meaning 1 placeholder for non-array
+     *                         parameters or 1 placeholder per array element.
+     *
+     * @return string  the query string with LIMIT clauses added
+     *
+     * @access protected
+     */
+    function modifyLimitQuery($query, $from, $count, $params = array())
+    {
+        return "$query LIMIT $count OFFSET $from";
+    }
+
+    // }}}
+    // {{{ modifyQuery()
+
+    /**
+     * Changes a query string for various DBMS specific reasons
+     *
+     * This little hack lets you know how many rows were deleted
+     * when running a "DELETE FROM table" query.  Only implemented
+     * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
+     *
+     * @param string $query  the query string to modify
+     *
+     * @return string  the modified query string
+     *
+     * @access protected
+     * @see DB_common::setOption()
+     */
+    function modifyQuery($query)
+    {
+        if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
+            if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
+                $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
+                                      'DELETE FROM \1 WHERE 1=1', $query);
+            }
+        }
+        return $query;
+    }
+
+    // }}}
+    // {{{ sqliteRaiseError()
+
+    /**
+     * Produces a DB_Error object regarding the current problem
+     *
+     * @param int $errno  if the error is being manually raised pass a
+     *                     DB_ERROR* constant here.  If this isn't passed
+     *                     the error information gathered from the DBMS.
+     *
+     * @return object  the DB_Error object
+     *
+     * @see DB_common::raiseError(),
+     *      DB_sqlite::errorNative(), DB_sqlite::errorCode()
+     */
+    function sqliteRaiseError($errno = null)
+    {
+        $native = $this->errorNative();
+        if ($errno === null) {
+            $errno = $this->errorCode($native);
+        }
+
+        $errorcode = @sqlite_last_error($this->connection);
+        $userinfo = "$errorcode ** $this->last_query";
+
+        return $this->raiseError($errno, null, null, $userinfo, $native);
+    }
+
+    // }}}
+    // {{{ errorNative()
+
+    /**
+     * Gets the DBMS' native error message produced by the last query
+     *
+     * {@internal This is used to retrieve more meaningfull error messages
+     * because sqlite_last_error() does not provide adequate info.}}
+     *
+     * @return string  the DBMS' error message
+     */
+    function errorNative()
+    {
+        return $this->_lasterror;
+    }
+
+    // }}}
+    // {{{ errorCode()
+
+    /**
+     * Determines PEAR::DB error code from the database's text error message
+     *
+     * @param string $errormsg  the error message returned from the database
+     *
+     * @return integer  the DB error number
+     */
+    function errorCode($errormsg)
+    {
+        static $error_regexps;
+        
+        // PHP 5.2+ prepends the function name to $php_errormsg, so we need
+        // this hack to work around it, per bug #9599.
+        $errormsg = preg_replace('/^sqlite[a-z_]+\(\): /', '', $errormsg);
+        
+        if (!isset($error_regexps)) {
+            $error_regexps = array(
+                '/^no such table:/' => DB_ERROR_NOSUCHTABLE,
+                '/^no such index:/' => DB_ERROR_NOT_FOUND,
+                '/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS,
+                '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT,
+                '/is not unique/' => DB_ERROR_CONSTRAINT,
+                '/columns .* are not unique/i' => DB_ERROR_CONSTRAINT,
+                '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT,
+                '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL,
+                '/^no such column:/' => DB_ERROR_NOSUCHFIELD,
+                '/no column named/' => DB_ERROR_NOSUCHFIELD,
+                '/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD,
+                '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX,
+                '/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW,
+            );
+        }
+        foreach ($error_regexps as $regexp => $code) {
+            if (preg_match($regexp, $errormsg)) {
+                return $code;
+            }
+        }
+        // Fall back to DB_ERROR if there was no mapping.
+        return DB_ERROR;
+    }
+
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table
+     *
+     * @param string         $result  a string containing the name of a table
+     * @param int            $mode    a valid tableInfo mode
+     *
+     * @return array  an associative array with the information requested.
+     *                 A DB_Error object on failure.
+     *
+     * @see DB_common::tableInfo()
+     * @since Method available since Release 1.7.0
+     */
+    function tableInfo($result, $mode = null)
+    {
+        if (is_string($result)) {
+            /*
+             * Probably received a table name.
+             * Create a result resource identifier.
+             */
+            $id = @sqlite_array_query($this->connection,
+                                      "PRAGMA table_info('$result');",
+                                      SQLITE_ASSOC);
+            $got_string = true;
+        } else {
+            $this->last_query = '';
+            return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null,
+                                     'This DBMS can not obtain tableInfo' .
+                                     ' from result sets');
+        }
+
+        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+            $case_func = 'strtolower';
+        } else {
+            $case_func = 'strval';
+        }
+
+        $count = count($id);
+        $res   = array();
+
+        if ($mode) {
+            $res['num_fields'] = $count;
+        }
+
+        for ($i = 0; $i < $count; $i++) {
+            if (strpos($id[$i]['type'], '(') !== false) {
+                $bits = explode('(', $id[$i]['type']);
+                $type = $bits[0];
+                $len  = rtrim($bits[1],')');
+            } else {
+                $type = $id[$i]['type'];
+                $len  = 0;
+            }
+
+            $flags = '';
+            if ($id[$i]['pk']) {
+                $flags .= 'primary_key ';
+                if (strtoupper($type) == 'INTEGER') {
+                    $flags .= 'auto_increment ';
+                }
+            }
+            if ($id[$i]['notnull']) {
+                $flags .= 'not_null ';
+            }
+            if ($id[$i]['dflt_value'] !== null) {
+                $flags .= 'default_' . rawurlencode($id[$i]['dflt_value']);
+            }
+            $flags = trim($flags);
+
+            $res[$i] = array(
+                'table' => $case_func($result),
+                'name'  => $case_func($id[$i]['name']),
+                'type'  => $type,
+                'len'   => $len,
+                'flags' => $flags,
+            );
+
+            if ($mode & DB_TABLEINFO_ORDER) {
+                $res['order'][$res[$i]['name']] = $i;
+            }
+            if ($mode & DB_TABLEINFO_ORDERTABLE) {
+                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+            }
+        }
+
+        return $res;
+    }
+
+    // }}}
+    // {{{ getSpecialQuery()
+
+    /**
+     * Obtains the query string needed for listing a given type of objects
+     *
+     * @param string $type  the kind of objects you want to retrieve
+     * @param array  $args  SQLITE DRIVER ONLY: a private array of arguments
+     *                       used by the getSpecialQuery().  Do not use
+     *                       this directly.
+     *
+     * @return string  the SQL query string or null if the driver doesn't
+     *                  support the object type requested
+     *
+     * @access protected
+     * @see DB_common::getListOf()
+     */
+    function getSpecialQuery($type, $args = array())
+    {
+        if (!is_array($args)) {
+            return $this->raiseError('no key specified', null, null, null,
+                                     'Argument has to be an array.');
+        }
+
+        switch ($type) {
+            case 'master':
+                return 'SELECT * FROM sqlite_master;';
+            case 'tables':
+                return "SELECT name FROM sqlite_master WHERE type='table' "
+                       . 'UNION ALL SELECT name FROM sqlite_temp_master '
+                       . "WHERE type='table' ORDER BY name;";
+            case 'schema':
+                return 'SELECT sql FROM (SELECT * FROM sqlite_master '
+                       . 'UNION ALL SELECT * FROM sqlite_temp_master) '
+                       . "WHERE type!='meta' "
+                       . 'ORDER BY tbl_name, type DESC, name;';
+            case 'schemax':
+            case 'schema_x':
+                /*
+                 * Use like:
+                 * $res = $db->query($db->getSpecialQuery('schema_x',
+                 *                   array('table' => 'table3')));
+                 */
+                return 'SELECT sql FROM (SELECT * FROM sqlite_master '
+                       . 'UNION ALL SELECT * FROM sqlite_temp_master) '
+                       . "WHERE tbl_name LIKE '{$args['table']}' "
+                       . "AND type!='meta' "
+                       . 'ORDER BY type DESC, name;';
+            case 'alter':
+                /*
+                 * SQLite does not support ALTER TABLE; this is a helper query
+                 * to handle this. 'table' represents the table name, 'rows'
+                 * the news rows to create, 'save' the row(s) to keep _with_
+                 * the data.
+                 *
+                 * Use like:
+                 * $args = array(
+                 *     'table' => $table,
+                 *     'rows'  => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT",
+                 *     'save'  => "NULL, titel, content, datetime"
+                 * );
+                 * $res = $db->query( $db->getSpecialQuery('alter', $args));
+                 */
+                $rows = strtr($args['rows'], $this->keywords);
+
+                $q = array(
+                    'BEGIN TRANSACTION',
+                    "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})",
+                    "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}",
+                    "DROP TABLE {$args['table']}",
+                    "CREATE TABLE {$args['table']} ({$args['rows']})",
+                    "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup",
+                    "DROP TABLE {$args['table']}_backup",
+                    'COMMIT',
+                );
+
+                /*
+                 * This is a dirty hack, since the above query will not get
+                 * executed with a single query call so here the query method
+                 * will be called directly and return a select instead.
+                 */
+                foreach ($q as $query) {
+                    $this->query($query);
+                }
+                return "SELECT * FROM {$args['table']};";
+            default:
+                return null;
+        }
+    }
+
+    // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/civicrm/packages/DB/storage.php b/civicrm/vendor/pear/db/DB/storage.php
similarity index 99%
rename from civicrm/packages/DB/storage.php
rename to civicrm/vendor/pear/db/DB/storage.php
index 081100486be1bb249dbbfb324ef6761b37ad7d45..f68f3dd3216505e9fa4bcf76e12f943c54fb4dd2 100644
--- a/civicrm/packages/DB/storage.php
+++ b/civicrm/vendor/pear/db/DB/storage.php
@@ -38,7 +38,7 @@ require_once 'DB.php';
  * @author     Stig Bakken <stig@php.net>
  * @copyright  1997-2007 The PHP Group
  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.9.3
+ * @version    Release: @package_version@
  * @link       http://pear.php.net/package/DB
  */
 class DB_storage extends PEAR
@@ -336,7 +336,7 @@ class DB_storage extends PEAR
         }
         reset($rowdata);
         $found_keycolumn = false;
-        while (list($key, $value) = each($rowdata)) {
+        foreach ($rowdata as $key => $value) {
             if ($key == $this->_keycolumn) {
                 $found_keycolumn = true;
             }
diff --git a/civicrm/vendor/pear/db/DB/sybase.php b/civicrm/vendor/pear/db/DB/sybase.php
new file mode 100644
index 0000000000000000000000000000000000000000..aa3a1b7c78dd4d6e4aac3dec8f999855a382f86c
--- /dev/null
+++ b/civicrm/vendor/pear/db/DB/sybase.php
@@ -0,0 +1,942 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's sybase extension
+ * for interacting with Sybase databases
+ *
+ * PHP version 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Sterling Hughes <sterling@php.net>
+ * @author     Antônio Carlos Venâncio Júnior <floripa@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id$
+ * @link       http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's sybase extension
+ * for interacting with Sybase databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * WARNING:  This driver may fail with multiple connections under the
+ * same user/pass/host and different databases.
+ *
+ * @category   Database
+ * @package    DB
+ * @author     Sterling Hughes <sterling@php.net>
+ * @author     Antônio Carlos Venâncio Júnior <floripa@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1997-2007 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/DB
+ */
+class DB_sybase extends DB_common
+{
+    // {{{ properties
+
+    /**
+     * The DB driver type (mysql, oci8, odbc, etc.)
+     * @var string
+     */
+    var $phptype = 'sybase';
+
+    /**
+     * The database syntax variant to be used (db2, access, etc.), if any
+     * @var string
+     */
+    var $dbsyntax = 'sybase';
+
+    /**
+     * The capabilities of this DB implementation
+     *
+     * The 'new_link' element contains the PHP version that first provided
+     * new_link support for this DBMS.  Contains false if it's unsupported.
+     *
+     * Meaning of the 'limit' element:
+     *   + 'emulate' = emulate with fetch row by number
+     *   + 'alter'   = alter the query
+     *   + false     = skip rows
+     *
+     * @var array
+     */
+    var $features = array(
+        'limit'         => 'emulate',
+        'new_link'      => false,
+        'numrows'       => true,
+        'pconnect'      => true,
+        'prepare'       => false,
+        'ssl'           => false,
+        'transactions'  => true,
+    );
+
+    /**
+     * A mapping of native error codes to DB error codes
+     * @var array
+     */
+    var $errorcode_map = array(
+    );
+
+    /**
+     * The raw database connection created by PHP
+     * @var resource
+     */
+    var $connection;
+
+    /**
+     * The DSN information for connecting to a database
+     * @var array
+     */
+    var $dsn = array();
+
+
+    /**
+     * Should data manipulation queries be committed automatically?
+     * @var bool
+     * @access private
+     */
+    var $autocommit = true;
+
+    /**
+     * The quantity of transactions begun
+     *
+     * {@internal  While this is private, it can't actually be designated
+     * private in PHP 5 because it is directly accessed in the test suite.}}
+     *
+     * @var integer
+     * @access private
+     */
+    var $transaction_opcount = 0;
+
+    /**
+     * The database specified in the DSN
+     *
+     * It's a fix to allow calls to different databases in the same script.
+     *
+     * @var string
+     * @access private
+     */
+    var $_db = '';
+
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * This constructor calls <kbd>parent::__construct()</kbd>
+     *
+     * @return void
+     */
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database server, log in and open the database
+     *
+     * Don't call this method directly.  Use DB::connect() instead.
+     *
+     * PEAR DB's sybase driver supports the following extra DSN options:
+     *   + appname       The application name to use on this connection.
+     *                   Available since PEAR DB 1.7.0.
+     *   + charset       The character set to use on this connection.
+     *                   Available since PEAR DB 1.7.0.
+     *
+     * @param array $dsn         the data source name
+     * @param bool  $persistent  should the connection be persistent?
+     *
+     * @return int  DB_OK on success. A DB_Error object on failure.
+     */
+    function connect($dsn, $persistent = false)
+    {
+        if (!PEAR::loadExtension('sybase') &&
+            !PEAR::loadExtension('sybase_ct'))
+        {
+            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+        }
+
+        $this->dsn = $dsn;
+        if ($dsn['dbsyntax']) {
+            $this->dbsyntax = $dsn['dbsyntax'];
+        }
+
+        $dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost';
+        $dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false;
+        $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false;
+        $dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false;
+
+        $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect';
+
+        if ($dsn['username']) {
+            $this->connection = @$connect_function($dsn['hostspec'],
+                                                   $dsn['username'],
+                                                   $dsn['password'],
+                                                   $dsn['charset'],
+                                                   $dsn['appname']);
+        } else {
+            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+                                     null, null, null,
+                                     'The DSN did not contain a username.');
+        }
+
+        if (!$this->connection) {
+            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+                                     null, null, null,
+                                     @sybase_get_last_message());
+        }
+
+        if ($dsn['database']) {
+            if (!@sybase_select_db($dsn['database'], $this->connection)) {
+                return $this->raiseError(DB_ERROR_NODBSELECTED,
+                                         null, null, null,
+                                         @sybase_get_last_message());
+            }
+            $this->_db = $dsn['database'];
+        }
+
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ disconnect()
+
+    /**
+     * Disconnects from the database server
+     *
+     * @return bool  TRUE on success, FALSE on failure
+     */
+    function disconnect()
+    {
+        $ret = @sybase_close($this->connection);
+        $this->connection = null;
+        return $ret;
+    }
+
+    // }}}
+    // {{{ simpleQuery()
+
+    /**
+     * Sends a query to the database server
+     *
+     * @param string  the SQL query string
+     *
+     * @return mixed  + a PHP result resrouce for successful SELECT queries
+     *                + the DB_OK constant for other successful queries
+     *                + a DB_Error object on failure
+     */
+    function simpleQuery($query)
+    {
+        $ismanip = $this->_checkManip($query);
+        $this->last_query = $query;
+        if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+            return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+        }
+        $query = $this->modifyQuery($query);
+        if (!$this->autocommit && $ismanip) {
+            if ($this->transaction_opcount == 0) {
+                $result = @sybase_query('BEGIN TRANSACTION', $this->connection);
+                if (!$result) {
+                    return $this->sybaseRaiseError();
+                }
+            }
+            $this->transaction_opcount++;
+        }
+        $result = @sybase_query($query, $this->connection);
+        if (!$result) {
+            return $this->sybaseRaiseError();
+        }
+        if (is_resource($result)) {
+            return $result;
+        }
+        // Determine which queries that should return data, and which
+        // should return an error code only.
+        return $ismanip ? DB_OK : $result;
+    }
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * Move the internal sybase result pointer to the next available result
+     *
+     * @param a valid sybase result resource
+     *
+     * @access public
+     *
+     * @return true if a result is available otherwise return false
+     */
+    function nextResult($result)
+    {
+        return false;
+    }
+
+    // }}}
+    // {{{ fetchInto()
+
+    /**
+     * Places a row from the result set into the given array
+     *
+     * Formating of the array and the data therein are configurable.
+     * See DB_result::fetchInto() for more information.
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::fetchInto() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result    the query result resource
+     * @param array    $arr       the referenced array to put the data in
+     * @param int      $fetchmode how the resulting array should be indexed
+     * @param int      $rownum    the row number to fetch (0 = first row)
+     *
+     * @return mixed  DB_OK on success, NULL when the end of a result set is
+     *                 reached or on failure
+     *
+     * @see DB_result::fetchInto()
+     */
+    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+    {
+        if ($rownum !== null) {
+            if (!@sybase_data_seek($result, $rownum)) {
+                return null;
+            }
+        }
+        if ($fetchmode & DB_FETCHMODE_ASSOC) {
+            if (function_exists('sybase_fetch_assoc')) {
+                $arr = @sybase_fetch_assoc($result);
+            } else {
+                if ($arr = @sybase_fetch_array($result)) {
+                    foreach ($arr as $key => $value) {
+                        if (is_int($key)) {
+                            unset($arr[$key]);
+                        }
+                    }
+                }
+            }
+            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+                $arr = array_change_key_case($arr, CASE_LOWER);
+            }
+        } else {
+            $arr = @sybase_fetch_row($result);
+        }
+        if (!$arr) {
+            return null;
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+            $this->_rtrimArrayValues($arr);
+        }
+        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+            $this->_convertNullArrayValuesToEmpty($arr);
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ freeResult()
+
+    /**
+     * Deletes the result set and frees the memory occupied by the result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::free() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return bool  TRUE on success, FALSE if $result is invalid
+     *
+     * @see DB_result::free()
+     */
+    function freeResult($result)
+    {
+        return is_resource($result) ? sybase_free_result($result) : false;
+    }
+
+    // }}}
+    // {{{ numCols()
+
+    /**
+     * Gets the number of columns in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numCols() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of columns.  A DB_Error object on failure.
+     *
+     * @see DB_result::numCols()
+     */
+    function numCols($result)
+    {
+        $cols = @sybase_num_fields($result);
+        if (!$cols) {
+            return $this->sybaseRaiseError();
+        }
+        return $cols;
+    }
+
+    // }}}
+    // {{{ numRows()
+
+    /**
+     * Gets the number of rows in a result set
+     *
+     * This method is not meant to be called directly.  Use
+     * DB_result::numRows() instead.  It can't be declared "protected"
+     * because DB_result is a separate object.
+     *
+     * @param resource $result  PHP's query result resource
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     *
+     * @see DB_result::numRows()
+     */
+    function numRows($result)
+    {
+        $rows = @sybase_num_rows($result);
+        if ($rows === false) {
+            return $this->sybaseRaiseError();
+        }
+        return $rows;
+    }
+
+    // }}}
+    // {{{ affectedRows()
+
+    /**
+     * Determines the number of rows affected by a data maniuplation query
+     *
+     * 0 is returned for queries that don't manipulate data.
+     *
+     * @return int  the number of rows.  A DB_Error object on failure.
+     */
+    function affectedRows()
+    {
+        if ($this->_last_query_manip) {
+            $result = @sybase_affected_rows($this->connection);
+        } else {
+            $result = 0;
+        }
+        return $result;
+     }
+
+    // }}}
+    // {{{ nextId()
+
+    /**
+     * Returns the next free id in a sequence
+     *
+     * @param string  $seq_name  name of the sequence
+     * @param boolean $ondemand  when true, the seqence is automatically
+     *                            created if it does not exist
+     *
+     * @return int  the next id number in the sequence.
+     *               A DB_Error object on failure.
+     *
+     * @see DB_common::nextID(), DB_common::getSequenceName(),
+     *      DB_sybase::createSequence(), DB_sybase::dropSequence()
+     */
+    function nextId($seq_name, $ondemand = true)
+    {
+        $seqname = $this->getSequenceName($seq_name);
+        if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+            return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+        }
+        $repeat = 0;
+        do {
+            $this->pushErrorHandling(PEAR_ERROR_RETURN);
+            $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
+            $this->popErrorHandling();
+            if ($ondemand && DB::isError($result) &&
+                ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
+            {
+                $repeat = 1;
+                $result = $this->createSequence($seq_name);
+                if (DB::isError($result)) {
+                    return $this->raiseError($result);
+                }
+            } elseif (!DB::isError($result)) {
+                $result = $this->query("SELECT @@IDENTITY FROM $seqname");
+                $repeat = 0;
+            } else {
+                $repeat = false;
+            }
+        } while ($repeat);
+        if (DB::isError($result)) {
+            return $this->raiseError($result);
+        }
+        $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
+        return $result[0];
+    }
+
+    /**
+     * Creates a new sequence
+     *
+     * @param string $seq_name  name of the new sequence
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::createSequence(), DB_common::getSequenceName(),
+     *      DB_sybase::nextID(), DB_sybase::dropSequence()
+     */
+    function createSequence($seq_name)
+    {
+        return $this->query('CREATE TABLE '
+                            . $this->getSequenceName($seq_name)
+                            . ' (id numeric(10, 0) IDENTITY NOT NULL,'
+                            . ' vapor int NULL)');
+    }
+
+    // }}}
+    // {{{ dropSequence()
+
+    /**
+     * Deletes a sequence
+     *
+     * @param string $seq_name  name of the sequence to be deleted
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     *
+     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+     *      DB_sybase::nextID(), DB_sybase::createSequence()
+     */
+    function dropSequence($seq_name)
+    {
+        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+    }
+
+    // }}}
+    // {{{ quoteFloat()
+
+    /**
+     * Formats a float value for use within a query in a locale-independent
+     * manner.
+     *
+     * @param float the float value to be quoted.
+     * @return string the quoted string.
+     * @see DB_common::quoteSmart()
+     * @since Method available since release 1.7.8.
+     */
+    function quoteFloat($float) {
+        return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
+    }
+     
+    // }}}
+    // {{{ autoCommit()
+
+    /**
+     * Enables or disables automatic commits
+     *
+     * @param bool $onoff  true turns it on, false turns it off
+     *
+     * @return int  DB_OK on success.  A DB_Error object if the driver
+     *               doesn't support auto-committing transactions.
+     */
+    function autoCommit($onoff = false)
+    {
+        // XXX if $this->transaction_opcount > 0, we should probably
+        // issue a warning here.
+        $this->autocommit = $onoff ? true : false;
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ commit()
+
+    /**
+     * Commits the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function commit()
+    {
+        if ($this->transaction_opcount > 0) {
+            if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+                return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+            }
+            $result = @sybase_query('COMMIT', $this->connection);
+            $this->transaction_opcount = 0;
+            if (!$result) {
+                return $this->sybaseRaiseError();
+            }
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ rollback()
+
+    /**
+     * Reverts the current transaction
+     *
+     * @return int  DB_OK on success.  A DB_Error object on failure.
+     */
+    function rollback()
+    {
+        if ($this->transaction_opcount > 0) {
+            if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+                return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+            }
+            $result = @sybase_query('ROLLBACK', $this->connection);
+            $this->transaction_opcount = 0;
+            if (!$result) {
+                return $this->sybaseRaiseError();
+            }
+        }
+        return DB_OK;
+    }
+
+    // }}}
+    // {{{ sybaseRaiseError()
+
+    /**
+     * Produces a DB_Error object regarding the current problem
+     *
+     * @param int $errno  if the error is being manually raised pass a
+     *                     DB_ERROR* constant here.  If this isn't passed
+     *                     the error information gathered from the DBMS.
+     *
+     * @return object  the DB_Error object
+     *
+     * @see DB_common::raiseError(),
+     *      DB_sybase::errorNative(), DB_sybase::errorCode()
+     */
+    function sybaseRaiseError($errno = null)
+    {
+        $native = $this->errorNative();
+        if ($errno === null) {
+            $errno = $this->errorCode($native);
+        }
+        return $this->raiseError($errno, null, null, null, $native);
+    }
+
+    // }}}
+    // {{{ errorNative()
+
+    /**
+     * Gets the DBMS' native error message produced by the last query
+     *
+     * @return string  the DBMS' error message
+     */
+    function errorNative()
+    {
+        return @sybase_get_last_message();
+    }
+
+    // }}}
+    // {{{ errorCode()
+
+    /**
+     * Determines PEAR::DB error code from the database's text error message.
+     *
+     * @param  string  $errormsg  error message returned from the database
+     * @return integer  an error number from a DB error constant
+     */
+    function errorCode($errormsg)
+    {
+        static $error_regexps;
+        
+        // PHP 5.2+ prepends the function name to $php_errormsg, so we need
+        // this hack to work around it, per bug #9599.
+        $errormsg = preg_replace('/^sybase[a-z_]+\(\): /', '', $errormsg);
+        
+        if (!isset($error_regexps)) {
+            $error_regexps = array(
+                '/Incorrect syntax near/'
+                    => DB_ERROR_SYNTAX,
+                '/^Unclosed quote before the character string [\"\'].*[\"\']\./'
+                    => DB_ERROR_SYNTAX,
+                '/Implicit conversion (from datatype|of NUMERIC value)/i'
+                    => DB_ERROR_INVALID_NUMBER,
+                '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./'
+                    => DB_ERROR_NOSUCHTABLE,
+                '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./'
+                    => DB_ERROR_ACCESS_VIOLATION,
+                '/^.+ permission denied on object .+, database .+, owner .+/'
+                    => DB_ERROR_ACCESS_VIOLATION,
+                '/^.* permission denied, database .+, owner .+/'
+                    => DB_ERROR_ACCESS_VIOLATION,
+                '/[^.*] not found\./'
+                    => DB_ERROR_NOSUCHTABLE,
+                '/There is already an object named/'
+                    => DB_ERROR_ALREADY_EXISTS,
+                '/Invalid column name/'
+                    => DB_ERROR_NOSUCHFIELD,
+                '/does not allow null values/'
+                    => DB_ERROR_CONSTRAINT_NOT_NULL,
+                '/Command has been aborted/'
+                    => DB_ERROR_CONSTRAINT,
+                '/^Cannot drop the index .* because it doesn\'t exist/i'
+                    => DB_ERROR_NOT_FOUND,
+                '/^There is already an index/i'
+                    => DB_ERROR_ALREADY_EXISTS,
+                '/^There are fewer columns in the INSERT statement than values specified/i'
+                    => DB_ERROR_VALUE_COUNT_ON_ROW,
+                '/Divide by zero/i'
+                    => DB_ERROR_DIVZERO,
+            );
+        }
+
+        foreach ($error_regexps as $regexp => $code) {
+            if (preg_match($regexp, $errormsg)) {
+                return $code;
+            }
+        }
+        return DB_ERROR;
+    }
+
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table or a result set
+     *
+     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+     * is a table name.
+     *
+     * @param object|string  $result  DB_result object from a query or a
+     *                                 string containing the name of a table.
+     *                                 While this also accepts a query result
+     *                                 resource identifier, this behavior is
+     *                                 deprecated.
+     * @param int            $mode    a valid tableInfo mode
+     *
+     * @return array  an associative array with the information requested.
+     *                 A DB_Error object on failure.
+     *
+     * @see DB_common::tableInfo()
+     * @since Method available since Release 1.6.0
+     */
+    function tableInfo($result, $mode = null)
+    {
+        if (is_string($result)) {
+            /*
+             * Probably received a table name.
+             * Create a result resource identifier.
+             */
+            if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
+                return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
+            }
+            $id = @sybase_query("SELECT * FROM $result WHERE 1=0",
+                                $this->connection);
+            $got_string = true;
+        } elseif (isset($result->result)) {
+            /*
+             * Probably received a result object.
+             * Extract the result resource identifier.
+             */
+            $id = $result->result;
+            $got_string = false;
+        } else {
+            /*
+             * Probably received a result resource identifier.
+             * Copy it.
+             * Deprecated.  Here for compatibility only.
+             */
+            $id = $result;
+            $got_string = false;
+        }
+
+        if (!is_resource($id)) {
+            return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA);
+        }
+
+        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+            $case_func = 'strtolower';
+        } else {
+            $case_func = 'strval';
+        }
+
+        $count = @sybase_num_fields($id);
+        $res   = array();
+
+        if ($mode) {
+            $res['num_fields'] = $count;
+        }
+
+        for ($i = 0; $i < $count; $i++) {
+            $f = @sybase_fetch_field($id, $i);
+            // column_source is often blank
+            $res[$i] = array(
+                'table' => $got_string
+                           ? $case_func($result)
+                           : $case_func($f->column_source),
+                'name'  => $case_func($f->name),
+                'type'  => $f->type,
+                'len'   => $f->max_length,
+                'flags' => '',
+            );
+            if ($res[$i]['table']) {
+                $res[$i]['flags'] = $this->_sybase_field_flags(
+                        $res[$i]['table'], $res[$i]['name']);
+            }
+            if ($mode & DB_TABLEINFO_ORDER) {
+                $res['order'][$res[$i]['name']] = $i;
+            }
+            if ($mode & DB_TABLEINFO_ORDERTABLE) {
+                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+            }
+        }
+
+        // free the result only if we were called on a table
+        if ($got_string) {
+            @sybase_free_result($id);
+        }
+        return $res;
+    }
+
+    // }}}
+    // {{{ _sybase_field_flags()
+
+    /**
+     * Get the flags for a field
+     *
+     * Currently supports:
+     *  + <samp>unique_key</samp>    (unique index, unique check or primary_key)
+     *  + <samp>multiple_key</samp>  (multi-key index)
+     *
+     * @param string  $table   the table name
+     * @param string  $column  the field name
+     *
+     * @return string  space delimited string of flags.  Empty string if none.
+     *
+     * @access private
+     */
+    function _sybase_field_flags($table, $column)
+    {
+        static $tableName = null;
+        static $flags = array();
+
+        if ($table != $tableName) {
+            $flags = array();
+            $tableName = $table;
+
+            /* We're running sp_helpindex directly because it doesn't exist in
+             * older versions of ASE -- unfortunately, we can't just use
+             * DB::isError() because the user may be using callback error
+             * handling. */
+            $res = @sybase_query("sp_helpindex $table", $this->connection);
+
+            if ($res === false || $res === true) {
+                // Fake a valid response for BC reasons.
+                return '';
+            }
+
+            while (($val = sybase_fetch_assoc($res)) !== false) {
+                if (!isset($val['index_keys'])) {
+                    /* No useful information returned. Break and be done with
+                     * it, which preserves the pre-1.7.9 behaviour. */
+                    break;
+                }
+
+                $keys = explode(', ', trim($val['index_keys']));
+
+                if (sizeof($keys) > 1) {
+                    foreach ($keys as $key) {
+                        $this->_add_flag($flags[$key], 'multiple_key');
+                    }
+                }
+
+                if (strpos($val['index_description'], 'unique')) {
+                    foreach ($keys as $key) {
+                        $this->_add_flag($flags[$key], 'unique_key');
+                    }
+                }
+            }
+
+            sybase_free_result($res);
+
+        }
+
+        if (array_key_exists($column, $flags)) {
+            return(implode(' ', $flags[$column]));
+        }
+
+        return '';
+    }
+
+    // }}}
+    // {{{ _add_flag()
+
+    /**
+     * Adds a string to the flags array if the flag is not yet in there
+     * - if there is no flag present the array is created
+     *
+     * @param array  $array  reference of flags array to add a value to
+     * @param mixed  $value  value to add to the flag array
+     *
+     * @return void
+     *
+     * @access private
+     */
+    function _add_flag(&$array, $value)
+    {
+        if (!is_array($array)) {
+            $array = array($value);
+        } elseif (!in_array($value, $array)) {
+            array_push($array, $value);
+        }
+    }
+
+    // }}}
+    // {{{ getSpecialQuery()
+
+    /**
+     * Obtains the query string needed for listing a given type of objects
+     *
+     * @param string $type  the kind of objects you want to retrieve
+     *
+     * @return string  the SQL query string or null if the driver doesn't
+     *                  support the object type requested
+     *
+     * @access protected
+     * @see DB_common::getListOf()
+     */
+    function getSpecialQuery($type)
+    {
+        switch ($type) {
+            case 'tables':
+                return "SELECT name FROM sysobjects WHERE type = 'U'"
+                       . ' ORDER BY name';
+            case 'views':
+                return "SELECT name FROM sysobjects WHERE type = 'V'";
+            default:
+                return null;
+        }
+    }
+
+    // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/civicrm/vendor/pear/db/PATCHES.txt b/civicrm/vendor/pear/db/PATCHES.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5885f7128ce026814c1e55b0bce0dcce4ed034be
--- /dev/null
+++ b/civicrm/vendor/pear/db/PATCHES.txt
@@ -0,0 +1,7 @@
+This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches)
+Patches applied to this directory:
+
+Apply CiviCRM Customisations for the pear:db package
+Source: https://raw.githubusercontent.com/civicrm/civicrm-core/a48a43c2b5f6d694fff1cfb99d522c5d9e2459a0/tools/scripts/composer/pear_db_civicrm_changes.patch
+
+
diff --git a/civicrm/vendor/pear/db/composer.json b/civicrm/vendor/pear/db/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..dff216a9d0c9b68420d08b964d3e7b87128e621c
--- /dev/null
+++ b/civicrm/vendor/pear/db/composer.json
@@ -0,0 +1,43 @@
+{
+    "authors": [
+        {
+            "email": "danielc@php.net",
+            "name": "Daniel Convissor",
+            "role": "Lead"
+        },
+        {
+            "email": "aharvey@php.net",
+            "name": "Adam Harvey",
+            "role": "Lead"
+        },
+        {
+            "email": "stig@php.net",
+            "name": "Stig Bakken",
+            "role": "Developer"
+        },
+        {
+            "email": "cox@idecnet.com",
+            "name": "Tomas V.V.Cox",
+            "role": "Developer"
+        }
+    ],
+    "autoload": {
+        "psr-0": {
+            "DB": "./"
+        }
+    },
+    "description": "More info available on: http://pear.php.net/package/DB",
+    "include-path": [
+        "./"
+    ],
+    "license": "PHP License v3.01",
+    "name": "pear/db",
+    "support": {
+        "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=DB",
+        "source": "https://github.com/pear/DB"
+    },
+    "type": "library",
+    "require": {
+        "pear/pear-core-minimal": "*"
+    }
+}
diff --git a/civicrm/xml/schema/Activity/Activity.xml b/civicrm/xml/schema/Activity/Activity.xml
index 8a37ea3d76156f28972b2e351b017acfc9d004cd..1fee89be2f4d91bb0bcd49bd35a3a46728bb6eb6 100644
--- a/civicrm/xml/schema/Activity/Activity.xml
+++ b/civicrm/xml/schema/Activity/Activity.xml
@@ -21,30 +21,6 @@
     <name>id</name>
     <autoincrement>true</autoincrement>
   </primaryKey>
-  <field>
-    <name>source_contact_id</name>
-    <type>int unsigned</type>
-    <title>Source Contact</title>
-    <import>true</import>
-    <headerPattern>/(activity.)?source(.contact(.id)?)?/i</headerPattern>
-    <comment>Contact ID of the person scheduling or logging this Activity. Usually the authenticated user.</comment>
-    <add>1.1</add>
-    <drop>4.4</drop>
-  </field>
-  <foreignKey>
-    <name>source_contact_id</name>
-    <table>civicrm_contact</table>
-    <key>id</key>
-    <add>1.1</add>
-    <drop>4.4</drop>
-    <onDelete>SET NULL</onDelete>
-  </foreignKey>
-  <index>
-    <name>UI_source_contact_id</name>
-    <fieldName>source_contact_id</fieldName>
-    <add>2.0</add>
-    <drop>3.2</drop>
-  </index>
   <field>
     <name>source_record_id</name>
     <type>int unsigned</type>
@@ -59,7 +35,7 @@
   </index>
   <field>
     <name>activity_type_id</name>
-    <title>Activity Type</title>
+    <title>Activity Type ID</title>
     <import>true</import>
     <type>int unsigned</type>
     <required>true</required>
@@ -73,6 +49,7 @@
     </pseudoconstant>
     <html>
       <type>Select</type>
+      <label>Activity Type</label>
     </html>
   </field>
   <index>
@@ -80,23 +57,6 @@
     <fieldName>activity_type_id</fieldName>
     <add>1.6</add>
   </index>
-  <field>
-    <name>target_entity_table</name>
-    <type>varchar</type>
-    <length>64</length>
-    <required>true</required>
-    <comment>Name of table where item being referenced is stored.</comment>
-    <add>1.1</add>
-    <drop>2.0</drop>
-  </field>
-  <field>
-    <name>target_entity_id</name>
-    <type>int unsigned</type>
-    <required>true</required>
-    <comment>Foreign key to the referenced item.</comment>
-    <add>1.1</add>
-    <drop>2.0</drop>
-  </field>
   <field>
     <name>subject</name>
     <uniqueName>activity_subject</uniqueName>
@@ -112,13 +72,6 @@
     <add>1.1</add>
     <change>2.0</change>
   </field>
-  <field>
-    <name>scheduled_date</name>
-    <type>datetime</type>
-    <comment>Date and time meeting is scheduled to occur.</comment>
-    <add>1.1</add>
-    <drop>2.0</drop>
-  </field>
   <field>
     <name>activity_date_time</name>
     <import>true</import>
@@ -140,27 +93,6 @@
     <fieldName>activity_date_time</fieldName>
     <add>4.7</add>
   </index>
-  <field>
-    <name>due_date_time</name>
-    <type>datetime</type>
-    <comment>Date and time this activity is due.</comment>
-    <add>2.0</add>
-    <drop>3.0</drop>
-  </field>
-  <field>
-    <name>duration_hours</name>
-    <type>int unsigned</type>
-    <comment>Planned or actual duration of meeting - hours.</comment>
-    <add>1.1</add>
-    <drop>2.0</drop>
-  </field>
-  <field>
-    <name>duration_minutes</name>
-    <type>int unsigned</type>
-    <comment>Planned or actual duration of meeting - minutes.</comment>
-    <add>1.1</add>
-    <drop>2.0</drop>
-  </field>
   <field>
     <name>duration</name>
     <uniqueName>activity_duration</uniqueName>
@@ -191,11 +123,12 @@
   <field>
     <name>phone_id</name>
     <type>int unsigned</type>
-    <title>Phone (called)</title>
+    <title>Phone (called) ID</title>
     <comment>Phone ID of the number called (optional - used if an existing phone number is selected).</comment>
     <add>2.0</add>
     <html>
       <type>EntityRef</type>
+      <label>Phone (called)</label>
     </html>
   </field>
   <foreignKey>
@@ -231,15 +164,6 @@
     </html>
     <add>1.1</add>
   </field>
-  <field>
-    <name>status</name>
-    <type>enum</type>
-    <title>Status</title>
-    <values>Scheduled, Completed</values>
-    <comment>What is the status of this meeting? Completed meeting status results in activity history entry.</comment>
-    <add>1.1</add>
-    <drop>2.0</drop>
-  </field>
   <field>
     <name>status_id</name>
     <uniqueName>activity_status_id</uniqueName>
@@ -374,12 +298,6 @@
     <add>2.2</add>
     <onDelete>CASCADE</onDelete>
   </foreignKey>
-  <index>
-    <name>UI_original_id</name>
-    <fieldName>original_id</fieldName>
-    <add>2.2</add>
-    <drop>3.2</drop>
-  </index>
   <field>
     <name>result</name>
     <uniqueName>activity_result</uniqueName>
diff --git a/civicrm/xml/schema/Activity/ActivityAssignment.xml b/civicrm/xml/schema/Activity/ActivityAssignment.xml
deleted file mode 100644
index f3df06bb54ab71d9b610e0d136af4ecccc571b0a..0000000000000000000000000000000000000000
--- a/civicrm/xml/schema/Activity/ActivityAssignment.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<table>
-  <base>CRM/Activity</base>
-  <class>ActivityAssignment</class>
-  <name>civicrm_activity_assignment</name>
-  <comment>Activity assignments</comment>
-  <add>1.8</add>
-  <log>true</log>
-  <field>
-    <name>id</name>
-    <type>int unsigned</type>
-    <title>Activity Assignment ID</title>
-    <required>true</required>
-    <comment>Activity assignment id</comment>
-    <add>1.8</add>
-  </field>
-  <primaryKey>
-    <name>id</name>
-    <autoincrement>true</autoincrement>
-  </primaryKey>
-
-  <field>
-    <name>activity_entity_table</name>
-    <type>varchar</type>
-    <length>64</length>
-    <required>true</required>
-    <comment>Name of table where item being referenced is stored (activity, phonecall or meeting).</comment>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </field>
-
-  <field>
-    <name>activity_entity_id</name>
-    <type>int unsigned</type>
-    <required>true</required>
-    <comment>Entity (activity, phonecall or meeting) id for which the assigment is created</comment>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </field>
-
-  <dynamicForeignKey>
-    <idColumn>activity_entity_id</idColumn>
-    <typeColumn>activity_entity_table</typeColumn>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </dynamicForeignKey>
-
-  <field>
-    <name>target_entity_table</name>
-    <type>varchar</type>
-    <length>64</length>
-    <required>true</required>
-    <comment>Name of table where item being referenced is stored (contact assigned to given activity).</comment>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </field>
-
-  <field>
-    <name>target_entity_id</name>
-    <type>int unsigned</type>
-    <required>true</required>
-    <comment>Foreign key to the referenced item.</comment>
-    <add>1.1</add>
-    <drop>2.0</drop>
-  </field>
-
-  <dynamicForeignKey>
-    <idColumn>target_entity_id</idColumn>
-    <typeColumn>target_entity_table</typeColumn>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </dynamicForeignKey>
-
-  <field>
-    <name>activity_id</name>
-    <type>int unsigned</type>
-    <title>Activity ID</title>
-    <required>true</required>
-    <comment>Foreign key to the activity for this assignment.</comment>
-    <add>2.0</add>
-  </field>
-  <foreignKey>
-    <name>activity_id</name>
-    <table>civicrm_activity</table>
-    <key>id</key>
-    <add>2.0</add>
-    <onDelete>CASCADE</onDelete>
-  </foreignKey>
-
-  <field>
-    <name>assignee_contact_id</name>
-    <type>int unsigned</type>
-    <title>Assignee Contact ID</title>
-    <required>true</required>
-    <comment>Foreign key to the contact for this assignment.</comment>
-    <add>2.0</add>
-  </field>
-  <foreignKey>
-    <name>assignee_contact_id</name>
-    <table>civicrm_contact</table>
-    <key>id</key>
-    <add>2.0</add>
-    <onDelete>CASCADE</onDelete>
-  </foreignKey>
-
-  <index>
-    <name>UI_activity_assignee_contact_id</name>
-    <fieldName>assignee_contact_id</fieldName>
-    <fieldName>activity_id</fieldName>
-    <unique>true</unique>
-    <add>2.0</add>
-  </index>
-
-
-</table>
diff --git a/civicrm/xml/schema/Activity/ActivityTarget.xml b/civicrm/xml/schema/Activity/ActivityTarget.xml
deleted file mode 100644
index 6b5cba7867d1e5602bdfae1eea27ce58f441d4db..0000000000000000000000000000000000000000
--- a/civicrm/xml/schema/Activity/ActivityTarget.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<table>
-  <base>CRM/Activity</base>
-  <class>ActivityTarget</class>
-  <name>civicrm_activity_target</name>
-  <comment>Activity targets</comment>
-  <add>2.0</add>
-  <log>true</log>
-  <field>
-    <name>id</name>
-    <type>int unsigned</type>
-    <title>Activity Target ID</title>
-    <required>true</required>
-    <comment>Activity target id</comment>
-    <add>2.0</add>
-  </field>
-  <primaryKey>
-    <name>id</name>
-    <autoincrement>true</autoincrement>
-  </primaryKey>
-   <field>
-    <name>activity_id</name>
-    <type>int unsigned</type>
-    <title>Activity ID</title>
-    <required>true</required>
-    <comment>Foreign key to the activity for this target.</comment>
-    <add>2.0</add>
-   </field>
-   <foreignKey>
-    <name>activity_id</name>
-    <table>civicrm_activity</table>
-    <key>id</key>
-    <add>2.0</add>
-    <onDelete>CASCADE</onDelete>
-  </foreignKey>
-
-   <field>
-    <name>target_contact_id</name>
-    <title>Contact ID (match to contact)</title>
-    <import>true</import>
-    <type>int unsigned</type>
-    <required>true</required>
-    <comment>Foreign key to the contact for this target.</comment>
-    <add>2.0</add>
-   </field>
-   <foreignKey>
-    <name>target_contact_id</name>
-    <table>civicrm_contact</table>
-    <key>id</key>
-    <add>2.0</add>
-    <onDelete>CASCADE</onDelete>
-  </foreignKey>
-
-  <index>
-       <name>UI_activity_target_contact_id</name>
-       <fieldName>target_contact_id</fieldName>
-       <fieldName>activity_id</fieldName>
-       <unique>true</unique>
-       <add>2.0</add>
-  </index>
-</table>
diff --git a/civicrm/xml/schema/Batch/Batch.xml b/civicrm/xml/schema/Batch/Batch.xml
index 3bcbc444092122a5c7359b5b9ad20f832cafae1b..811ecf4f219f5a3aa861bcfab26aa14ec0d98eb5 100644
--- a/civicrm/xml/schema/Batch/Batch.xml
+++ b/civicrm/xml/schema/Batch/Batch.xml
@@ -35,15 +35,6 @@
     <unique>true</unique>
     <add>4.2</add>
   </index>
-  <field>
-    <name>label</name>
-    <type>varchar</type>
-    <length>64</length>
-    <localizable>true</localizable>
-    <comment>Friendly Name.</comment>
-    <add>3.3</add>
-    <drop>4.2</drop>
-  </field>
   <field>
     <name>title</name>
     <title>Batch Title</title>
diff --git a/civicrm/xml/schema/Batch/EntityBatch.xml b/civicrm/xml/schema/Batch/EntityBatch.xml
index 462c40d1a71ed92702f1ff7cffb711469601a61a..004289557d364ec8cf19c29f072d56c35cd91c1d 100644
--- a/civicrm/xml/schema/Batch/EntityBatch.xml
+++ b/civicrm/xml/schema/Batch/EntityBatch.xml
@@ -5,6 +5,7 @@
   <class>EntityBatch</class>
   <name>civicrm_entity_batch</name>
   <comment>Batch entities (Contributions, Participants, Contacts) to a batch.</comment>
+  <add>3.3</add>
   <field>
     <name>id</name>
     <title>EntityBatch ID</title>
diff --git a/civicrm/xml/schema/Case/Case.xml b/civicrm/xml/schema/Case/Case.xml
index a91e955a55eb23023095746ff079285cc0c32825..492a1df43d46e148f5d9e333b59dc4fe3ecdb05a 100644
--- a/civicrm/xml/schema/Case/Case.xml
+++ b/civicrm/xml/schema/Case/Case.xml
@@ -24,23 +24,6 @@
     <name>id</name>
     <autoincrement>true</autoincrement>
   </primaryKey>
-  <field>
-    <name>contact_id</name>
-    <type>int unsigned</type>
-    <uniqueName>case_contact_id</uniqueName>
-    <required>true</required>
-    <comment>Contact ID of contact record given case belongs to.</comment>
-    <add>1.8</add>
-    <drop>2.1</drop>
-  </field>
-  <foreignKey>
-    <name>contact_id</name>
-    <table>civicrm_contact</table>
-    <key>id</key>
-    <add>1.8</add>
-    <drop>2.1</drop>
-    <onDelete>CASCADE</onDelete>
-  </foreignKey>
   <field>
     <name>case_type_id</name>
     <type>int unsigned</type>
@@ -70,51 +53,6 @@
     <key>id</key>
     <add>4.5</add>
   </foreignKey>
-  <field>
-    <name>casetag1_id</name>
-    <type>varchar</type>
-    <length>128</length>
-    <required>true</required>
-    <comment>Id of first case category.</comment>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </field>
-  <index>
-    <name>index_casetag1_id</name>
-    <fieldName>casetag1_id</fieldName>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </index>
-  <field>
-    <name>casetag2_id</name>
-    <type>varchar</type>
-    <length>128</length>
-    <required>true</required>
-    <comment>Id of second case category.</comment>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </field>
-  <index>
-    <name>index_casetag2_id</name>
-    <fieldName>casetag2_id</fieldName>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </index>
-  <field>
-    <name>casetag3_id</name>
-    <type>varchar</type>
-    <length>128</length>
-    <required>true</required>
-    <comment>Id of third case category.</comment>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </field>
-  <index>
-    <name>index_casetag3_id</name>
-    <fieldName>casetag3_id</fieldName>
-    <add>1.8</add>
-    <drop>2.0</drop>
-  </index>
   <field>
     <name>subject</name>
     <type>varchar</type>
diff --git a/civicrm/xml/schema/Case/CaseActivity.xml b/civicrm/xml/schema/Case/CaseActivity.xml
index 0ae6710e4d74324c7fb318185775507cec00e5e4..daa0a895d216bf97f57e86ad959efb279393cfae 100644
--- a/civicrm/xml/schema/Case/CaseActivity.xml
+++ b/civicrm/xml/schema/Case/CaseActivity.xml
@@ -54,31 +54,4 @@
     <fieldName>activity_id</fieldName>
     <add>2.0</add>
   </index>
-
-  <field>
-   <name>activity_entity_table</name>
-   <type>varchar</type>
-   <length>64</length>
-   <required>true</required>
-   <comment>Name of table where item being referenced is stored (activity, phonecall or meeting).</comment>
-   <add>1.8</add>
-   <drop>2.0</drop>
-  </field>
-
-  <field>
-   <name>activity_entity_id</name>
-   <type>int unsigned</type>
-   <required>true</required>
-   <comment>Entity (activity, phonecall or meeting) id for which the assigment is created</comment>
-   <add>1.8</add>
-   <drop>2.0</drop>
-  </field>
-
-  <dynamicForeignKey>
-   <idColumn>activity_entity_id</idColumn>
-   <typeColumn>activity_entity_table</typeColumn>
-   <add>1.8</add>
-   <drop>2.0</drop>
-  </dynamicForeignKey>
-
 </table>
diff --git a/civicrm/xml/schema/Contact/Contact.xml b/civicrm/xml/schema/Contact/Contact.xml
index dda19acd7f4b70ecc8f5c3a661ddb0b3609ab84b..8200affa648c656720ea1dbadae17d22a5fcbd1f 100644
--- a/civicrm/xml/schema/Contact/Contact.xml
+++ b/civicrm/xml/schema/Contact/Contact.xml
@@ -246,19 +246,6 @@
     <fulltext/>
     <add>1.1</add>
   </field>
-  <field>
-    <name>home_URL</name>
-    <rule>url</rule>
-    <title>Website</title>
-    <type>varchar</type>
-    <length>128</length>
-    <import>true</import>
-    <headerPattern>/^(home\sURL)|URL|web|site/i</headerPattern>
-    <dataPattern>/^[\w\/\:\.]+$/</dataPattern>
-    <comment>optional "home page" URL for this contact.</comment>
-    <add>1.1</add>
-    <drop>3.2</drop>
-  </field>
   <field>
     <name>image_URL</name>
     <type>text</type>
@@ -509,32 +496,6 @@
     <fieldName>communication_style_id</fieldName>
     <add>4.4</add>
   </index>
-  <field>
-    <name>greeting_type</name>
-    <type>varchar</type>
-    <length>128</length>
-    <import>true</import>
-    <comment>Preferred greeting format.</comment>
-    <add>1.1</add>
-    <drop>2.2</drop>
-  </field>
-  <field>
-    <name>greeting_type_id</name>
-    <title>Greeting Type</title>
-    <type>int unsigned</type>
-    <comment>FK to civicrm_option_value.id, that has to be valid, registered Greeting type.</comment>
-    <add>2.2</add>
-    <drop>3.0</drop>
-  </field>
-  <field>
-    <name>custom_greeting</name>
-    <type>varchar</type>
-    <length>128</length>
-    <import>true</import>
-    <comment>Custom greeting message.</comment>
-    <add>1.1</add>
-    <drop>3.0</drop>
-  </field>
   <field>
     <name>email_greeting_id</name>
     <type>int unsigned</type>
@@ -654,13 +615,14 @@
   </field>
   <field>
     <name>gender_id</name>
-    <title>Gender</title>
+    <title>Gender ID</title>
     <type>int unsigned</type>
     <pseudoconstant>
       <optionGroupName>gender</optionGroupName>
     </pseudoconstant>
     <html>
       <type>Select</type>
+      <label>Gender</label>
     </html>
     <headerPattern>/^gender$/i</headerPattern>
     <comment>FK to gender ID</comment>
@@ -721,23 +683,6 @@
     </html>
     <contactType>Individual</contactType>
   </field>
-  <field>
-    <name>mail_to_household_id</name>
-    <title>Mail to Household ID</title>
-    <type>int unsigned</type>
-    <comment>OPTIONAL FK to civicrm_contact_household record. If NOT NULL, direct mail communications to household rather than individual location. </comment>
-    <export>true</export>
-    <add>1.1</add>
-    <drop>3.3</drop>
-  </field>
-  <foreignKey>
-    <name>mail_to_household_id</name>
-    <table>civicrm_contact</table>
-    <key>id</key>
-    <add>2.1</add>
-    <onDelete>SET NULL</onDelete>
-    <drop>3.3</drop>
-  </foreignKey>
   <field>
     <name>household_name</name>
     <type>varchar</type>
@@ -825,7 +770,7 @@
   </field>
   <field>
     <name>employer_id</name>
-    <title>Current Employer</title>
+    <title>Current Employer ID</title>
     <uniqueName>current_employer_id</uniqueName>
     <type>int unsigned</type>
     <comment>OPTIONAL FK to civicrm_contact record.</comment>
@@ -833,6 +778,7 @@
     <add>2.1</add>
     <html>
       <type>EntityRef</type>
+      <label>Current Employer</label>
     </html>
     <contactType>Individual</contactType>
   </field>
@@ -857,12 +803,6 @@
     </html>
     <permission>access deleted contacts</permission>
   </field>
-  <index>
-    <name>index_is_deleted</name>
-    <fieldName>is_deleted</fieldName>
-    <add>3.2</add>
-    <drop>4.4</drop>
-  </index>
   <index>
     <name>index_is_deleted_sort_name</name>
     <fieldName>is_deleted</fieldName>
diff --git a/civicrm/xml/schema/Contact/Individual.xml b/civicrm/xml/schema/Contact/Individual.xml
index 45f27206778691466b3a82896e691debfdf20a66..9956f17bebafbf7db4c81e1c480feae0c10164be 100644
--- a/civicrm/xml/schema/Contact/Individual.xml
+++ b/civicrm/xml/schema/Contact/Individual.xml
@@ -74,14 +74,6 @@
     <fieldName>last_name</fieldName>
     <add>1.8</add>
   </index>
-  <field>
-    <name>gender</name>
-    <type>enum</type>
-    <import>true</import>
-    <values>Female, Male, Other</values>
-    <add>1.1</add>
-    <drop>1.2</drop>
-  </field>
   <field>
     <name>prefix_id</name>
     <type>int unsigned</type>
@@ -108,32 +100,6 @@
     <fieldName>suffix_id</fieldName>
     <add>1.6</add>
   </index>
-  <field>
-    <name>prefix</name>
-    <type>varchar</type>
-    <length>64</length>
-    <import>true</import>
-    <comment>Prefix to Name.</comment>
-    <add>1.1</add>
-    <drop>1.2</drop>
-  </field>
-  <field>
-    <name>suffix</name>
-    <type>varchar</type>
-    <length>64</length>
-    <import>true</import>
-    <comment>Suffix to Name.</comment>
-    <add>1.1</add>
-    <drop>1.2</drop>
-  </field>
-  <field>
-    <name>greeting_type</name>
-    <type>varchar</type>
-    <length>128</length>
-    <comment>Preferred greeting format.</comment>
-    <add>1.1</add>
-    <drop>2.2</drop>
-  </field>
   <field>
     <name>greeting_type_id</name>
     <title>Greeting Type</title>
diff --git a/civicrm/xml/schema/Contact/SavedSearch.xml b/civicrm/xml/schema/Contact/SavedSearch.xml
index 89f578084b3387200caef35c5c3c652fc5ad54eb..2d73182ac5c55948c9c0d3d5ec1d2bc671208b5a 100644
--- a/civicrm/xml/schema/Contact/SavedSearch.xml
+++ b/civicrm/xml/schema/Contact/SavedSearch.xml
@@ -18,15 +18,6 @@
     <name>id</name>
     <autoincrement>false</autoincrement>
   </primaryKey>
-  <field>
-    <name>query</name>
-    <title>SQL Query</title>
-    <type>text</type>
-    <import>true</import>
-    <comment>SQL query for this search</comment>
-    <add>1.1</add>
-    <drop>1.5</drop>
-  </field>
   <field>
     <name>form_values</name>
     <title>Submitted Form Values</title>
@@ -36,14 +27,6 @@
     <serialize>PHP</serialize>
     <add>1.1</add>
   </field>
-  <field>
-    <name>is_active</name>
-    <type>boolean</type>
-    <title>Saved Search Enabled</title>
-    <comment>Is this entry active?</comment>
-    <add>1.1</add>
-    <drop>1.5</drop>
-  </field>
   <field>
     <name>mapping_id</name>
     <type>int unsigned</type>
diff --git a/civicrm/xml/schema/Contribute/Contribution.xml b/civicrm/xml/schema/Contribute/Contribution.xml
index 246c55bd32abbbd2aa7b8a85fe9e649bd155b7e6..fed11d358c87d55b59ae90f106a72f000d9de42c 100644
--- a/civicrm/xml/schema/Contribute/Contribution.xml
+++ b/civicrm/xml/schema/Contribute/Contribution.xml
@@ -46,42 +46,9 @@
     <add>1.3</add>
     <onDelete>CASCADE</onDelete>
   </foreignKey>
-  <field>
-    <name>solicitor_id</name>
-    <title>Solicitor ID</title>
-    <type>int unsigned</type>
-    <comment>FK to Solicitor ID</comment>
-    <add>1.4</add>
-    <drop>2.2</drop>
-  </field>
-  <foreignKey>
-    <name>solicitor_id</name>
-    <table>civicrm_contact</table>
-    <key>id</key>
-    <add>1.4</add>
-    <drop>2.2</drop>
-    <onDelete>SET NULL</onDelete>
-  </foreignKey>
-  <field>
-    <name>contribution_type_id</name>
-    <title>Contribution Type</title>
-    <export>false</export>
-    <type>int unsigned</type>
-    <comment>FK to Contribution Type</comment>
-    <add>1.3</add>
-    <drop>4.3</drop>
-  </field>
-  <foreignKey>
-    <name>contribution_type_id</name>
-    <table>civicrm_contribution_type</table>
-    <key>id</key>
-    <add>1.3</add>
-    <drop>4.3</drop>
-    <onDelete>SET NULL</onDelete>
-  </foreignKey>
   <field>
     <name>financial_type_id</name>
-    <title>Financial Type</title>
+    <title>Financial Type ID</title>
     <type>int unsigned</type>
     <comment>FK to Financial Type for (total_amount - non_deductible_amount).</comment>
     <pseudoconstant>
@@ -92,6 +59,7 @@
     <export>true</export>
     <html>
       <type>Select</type>
+      <label>Financial Type</label>
     </html>
     <add>4.3</add>
   </field>
@@ -114,6 +82,7 @@
     </pseudoconstant>
     <html>
       <type>Select</type>
+      <label>Contribution Page</label>
     </html>
     <add>1.5</add>
   </field>
@@ -359,16 +328,6 @@
       <type>Text</type>
     </html>
   </field>
-  <field>
-    <name>note</name>
-    <type>text</type>
-    <comment>Note and/or Comment.</comment>
-    <import>true</import>
-    <headerPattern>/Note|Comment/i</headerPattern>
-    <dataPattern>//</dataPattern>
-    <add>1.4</add>
-    <drop>1.7</drop>
-  </field>
   <index>
     <name>UI_contrib_trxn_id</name>
     <fieldName>trxn_id</fieldName>
diff --git a/civicrm/xml/schema/Contribute/ContributionPage.xml b/civicrm/xml/schema/Contribute/ContributionPage.xml
index 9c08b4738bce0bd87e43ba515ec21c6f1bfa79f0..fb93703f74769ddbe5df4c46d7416e5be3f5d2ca 100644
--- a/civicrm/xml/schema/Contribute/ContributionPage.xml
+++ b/civicrm/xml/schema/Contribute/ContributionPage.xml
@@ -41,20 +41,6 @@
     <comment>Text and html allowed. Displayed below title.</comment>
     <add>1.3</add>
   </field>
-  <field>
-    <name>contribution_type_id</name>
-    <type>int unsigned</type>
-    <required>true</required>
-    <comment>default Contribution type assigned to contributions submitted via this page, e.g. Contribution, Campaign Contribution</comment>
-    <add>1.3</add>
-    <drop>4.3</drop>
-  </field>
-  <foreignKey>
-    <name>contribution_type_id</name>
-    <table>civicrm_contribution_type</table>
-    <key>id</key>
-    <drop>4.3</drop>
-  </foreignKey>
   <field>
     <name>financial_type_id</name>
     <title>Financial Type</title>
@@ -80,6 +66,7 @@
     <name>payment_processor</name>
     <type>varchar</type>
     <length>128</length>
+    <serialize>SEPARATOR_TRIMMED</serialize>
     <comment>Payment Processors configured for this contribution Page</comment>
     <pseudoconstant>
       <table>civicrm_payment_processor</table>
diff --git a/civicrm/xml/schema/Contribute/ContributionProduct.xml b/civicrm/xml/schema/Contribute/ContributionProduct.xml
index d5ff804a1009cc7e04f5368cf5f4dbaca6c61f12..1e3526d27c5a53bbef786a6f849d9898c618a0cd 100644
--- a/civicrm/xml/schema/Contribute/ContributionProduct.xml
+++ b/civicrm/xml/schema/Contribute/ContributionProduct.xml
@@ -55,17 +55,6 @@
     <comment></comment>
     <add>1.4</add>
   </field>
-  <field>
-    <name>total_cost</name>
-    <type>decimal</type>
-    <required>true</required>
-    <import>true</import>
-    <headerPattern>/^total|(.?^am(ou)?nt)/i</headerPattern>
-    <dataPattern>/^\d+(\.\d{2})?$/</dataPattern>
-    <comment>quantity X civicrm_product.cost.</comment>
-    <add>1.3</add>
-    <drop>4.1</drop>
-  </field>
   <field>
     <name>fulfilled_date</name>
     <type>date</type>
diff --git a/civicrm/xml/schema/Contribute/ContributionRecur.xml b/civicrm/xml/schema/Contribute/ContributionRecur.xml
index eddc342d9c5ad8e6f719d84646a96a9ddae02343..11e70b6afc496ff8bdd6fb8d95bd126cff8d5e05 100644
--- a/civicrm/xml/schema/Contribute/ContributionRecur.xml
+++ b/civicrm/xml/schema/Contribute/ContributionRecur.xml
@@ -286,13 +286,6 @@
     <html>
       <type>Text</type>
     </html>
-  </field>
-  <field>
-    <name>next_sched_contribution</name>
-    <type>datetime</type>
-    <comment>At Groundspring this was used by the cron job which triggered payments. If we''re not doing that but we know about payments, it might still be useful to store for display to org andor contributors.</comment>
-    <add>1.6</add>
-    <drop>4.4</drop>
   </field>
     <field>
     <name>next_sched_contribution_date</name>
@@ -366,23 +359,6 @@
     <add>3.3</add>
     <onDelete>SET NULL</onDelete>
   </foreignKey>
-  <field>
-    <name>contribution_type_id</name>
-    <title>Contribution Type</title>
-    <export>false</export>
-    <type>int unsigned</type>
-    <comment>FK to Contribution Type</comment>
-    <add>4.1</add>
-    <drop>4.3</drop>
-  </field>
-  <foreignKey>
-    <name>contribution_type_id</name>
-    <table>civicrm_contribution_type</table>
-    <key>id</key>
-    <add>4.1</add>
-    <drop>4.3</drop>
-    <onDelete>SET NULL</onDelete>
-  </foreignKey>
   <field>
     <name>financial_type_id</name>
     <title>Financial Type</title>
diff --git a/civicrm/xml/schema/Contribute/PremiumsProduct.xml b/civicrm/xml/schema/Contribute/PremiumsProduct.xml
index 7f9b86604767f86ed026e5dfe99352be072872cf..6ed7ca1ce61761e2f7fb900ce81c8f2fb74eeecd 100644
--- a/civicrm/xml/schema/Contribute/PremiumsProduct.xml
+++ b/civicrm/xml/schema/Contribute/PremiumsProduct.xml
@@ -47,14 +47,6 @@
     <key>id</key>
     <add>1.4</add>
   </foreignKey>
-  <field>
-    <name>sort_position</name>
-    <title>Sort Position</title>
-    <type>int unsigned</type>
-    <required>true</required>
-    <add>1.4</add>
-    <drop>2.0</drop>
-  </field>
   <field>
     <name>weight</name>
     <title>Order</title>
diff --git a/civicrm/xml/schema/Core/ActionSchedule.xml b/civicrm/xml/schema/Core/ActionSchedule.xml
index 4c7d78c219bf52e993cba838450bc3b81ef53c36..536f174cfbc6cc3b542bdfbd20548ae24792ecfd 100644
--- a/civicrm/xml/schema/Core/ActionSchedule.xml
+++ b/civicrm/xml/schema/Core/ActionSchedule.xml
@@ -51,6 +51,7 @@
     <type>varchar</type>
     <length>255</length>
     <comment>Entity value</comment>
+    <serialize>SEPARATOR_TRIMMED</serialize>
     <add>3.4</add>
   </field>
   <field>
@@ -58,6 +59,7 @@
     <type>varchar</type>
     <length>64</length>
     <comment>Entity status</comment>
+    <serialize>SEPARATOR_TRIMMED</serialize>
     <add>3.4</add>
   </field>
   <field>
@@ -169,6 +171,7 @@
     <type>varchar</type>
     <length>128</length>
     <comment>Contact IDs to which reminder should be sent.</comment>
+    <serialize>COMMA</serialize>
     <add>3.4</add>
   </field>
   <field>
diff --git a/civicrm/xml/schema/Core/Cache.xml b/civicrm/xml/schema/Core/Cache.xml
index 564a2773292b117051960462feca88bf7ecf1e89..1e8d9a389ff7e8f0a51021d8ee3a3af322fcc332 100644
--- a/civicrm/xml/schema/Core/Cache.xml
+++ b/civicrm/xml/schema/Core/Cache.xml
@@ -35,14 +35,6 @@
     <comment>Unique path name for cache element</comment>
     <add>2.1</add>
   </field>
-  <index>
-    <name>UI_group_path</name>
-    <fieldName>group_name</fieldName>
-    <fieldName>path</fieldName>
-    <unique>true</unique>
-    <add>2.1</add>
-    <drop>4.2</drop>
-  </index>
   <index>
     <name>UI_group_path_date</name>
     <fieldName>group_name</fieldName>
diff --git a/civicrm/xml/schema/Core/CustomField.xml b/civicrm/xml/schema/Core/CustomField.xml
index 3e8c2b7d6401b5d656d093b5e35b17d6b75a848b..44c632256010c3656ab9cd475ba9c5ea5d4cd713 100644
--- a/civicrm/xml/schema/Core/CustomField.xml
+++ b/civicrm/xml/schema/Core/CustomField.xml
@@ -211,14 +211,6 @@
     <comment>Date may be up to end_date_years years after the current date.</comment>
     <add>1.4</add>
   </field>
-  <field>
-    <name>date_parts</name>
-    <type>varchar</type>
-    <length>255</length>
-    <comment>which date part included in display </comment>
-    <add>1.4</add>
-    <drop>3.1</drop>
-  </field>
   <field>
     <name>date_format</name>
     <type>varchar</type>
diff --git a/civicrm/xml/schema/Core/CustomGroup.xml b/civicrm/xml/schema/Core/CustomGroup.xml
index e0f424ddfc49ed037432389bf02b1682d6d3bfc5..e21458ecbafa24855f260fe98012698ce7ada03d 100644
--- a/civicrm/xml/schema/Core/CustomGroup.xml
+++ b/civicrm/xml/schema/Core/CustomGroup.xml
@@ -47,14 +47,6 @@
     <comment>Type of object this group extends (can add other options later e.g. contact_address, etc.).</comment>
     <add>1.1</add>
   </field>
-  <field>
-    <name>extends_entity_column_name</name>
-    <type>varchar</type>
-    <length>64</length>
-    <comment>linking custom group for dynamic object</comment>
-    <add>1.6</add>
-    <drop>2.2</drop>
-  </field>
   <field>
     <name>extends_entity_column_id</name>
     <type>int unsigned</type>
diff --git a/civicrm/xml/schema/Core/Dashboard.xml b/civicrm/xml/schema/Core/Dashboard.xml
index 00500943a8da8e4961b2bff8b2882d559144f30a..6b34614dbcc23802de5095f988a386ae314b628b 100644
--- a/civicrm/xml/schema/Core/Dashboard.xml
+++ b/civicrm/xml/schema/Core/Dashboard.xml
@@ -62,13 +62,6 @@
     <comment>url in case of external dashlet</comment>
     <add>3.1</add>
   </field>
-  <field>
-    <name>content</name>
-    <type>text</type>
-    <comment>dashlet content</comment>
-    <add>3.1</add>
-    <drop>3.3</drop>
-  </field>
   <field>
     <name>permission</name>
     <type>varchar</type>
@@ -155,12 +148,4 @@
     <required>true</required>
     <add>4.7</add>
   </field>
-  <field>
-    <name>created_date</name>
-    <type>datetime</type>
-    <title>Dashlet Created Date</title>
-    <comment>When was content populated</comment>
-    <add>3.1</add>
-    <drop>3.3</drop>
-  </field>
 </table>
diff --git a/civicrm/xml/schema/Core/Discount.xml b/civicrm/xml/schema/Core/Discount.xml
index 4dabdfcf85360bc34250dfa032618bc929092157..54535e48576436a78921e77649b8d7dd9c99a729 100644
--- a/civicrm/xml/schema/Core/Discount.xml
+++ b/civicrm/xml/schema/Core/Discount.xml
@@ -5,6 +5,7 @@
   <class>Discount</class>
   <name>civicrm_discount</name>
   <comment>Stores discounts for events on the basis of date</comment>
+  <add>2.1</add>
   <log>true</log>
   <field>
     <name>id</name>
@@ -45,25 +46,6 @@
     <fieldName>entity_id</fieldName>
     <add>2.1</add>
   </index>
-  <field>
-    <name>option_group_id</name>
-    <uniqueName>participant_discount_name</uniqueName>
-    <title>Discount Name</title>
-    <type>int unsigned</type>
-    <required>true</required>
-    <export>true</export>
-    <comment>FK to civicrm_price_set</comment>
-    <add>2.1</add>
-    <drop>4.3</drop>
-  </field>
-  <foreignKey>
-    <name>option_group_id</name>
-    <table>civicrm_price_set</table>
-    <key>id</key>
-    <add>2.1</add>
-    <onDelete>CASCADE</onDelete>
-    <drop>4.3</drop>
-  </foreignKey>
   <field>
     <name>price_set_id</name>
     <uniqueName>participant_discount_name</uniqueName>
diff --git a/civicrm/xml/schema/Core/Domain.xml b/civicrm/xml/schema/Core/Domain.xml
index ddb2a0dc6a2fde41068815f620a48d1aa11df71b..8e4e119a7f31dae93d0576d7fadd8ee4819d07c0 100644
--- a/civicrm/xml/schema/Core/Domain.xml
+++ b/civicrm/xml/schema/Core/Domain.xml
@@ -46,46 +46,6 @@
     <unique>true</unique>
     <add>1.1</add>
   </index>
-  <field>
-    <name>contact_name</name>
-    <type>varchar</type>
-    <length>64</length>
-    <comment>Name of the person responsible for this domain</comment>
-    <add>1.1</add>
-    <drop>1.9</drop>
-  </field>
-  <field>
-    <name>email_name</name>
-    <type>varchar</type>
-    <length>255</length>
-    <comment>The default email name that is used in the from address for all outgoing emails</comment>
-    <add>1.9</add>
-    <drop>2.2</drop>
-  </field>
-  <field>
-    <name>email_address</name>
-    <type>varchar</type>
-    <length>255</length>
-    <comment>The default email address that is used as the from address for all outgoing emails</comment>
-    <add>1.9</add>
-    <drop>2.2</drop>
-  </field>
-  <field>
-    <name>email_domain</name>
-    <type>varchar</type>
-    <length>64</length>
-    <comment>The domain from which outgoing email for this domain will appear to originate</comment>
-    <add>1.1</add>
-    <drop>2.2</drop>
-  </field>
-  <field>
-    <name>email_return_path</name>
-    <type>varchar</type>
-    <length>64</length>
-    <comment>The domain from which outgoing email for this domain will appear to originate</comment>
-    <add>1.1</add>
-    <drop>2.2</drop>
-  </field>
   <field>
     <name>config_backend</name>
     <type>text</type>
@@ -103,13 +63,6 @@
     <comment>The civicrm version this instance is running</comment>
     <add>2.0</add>
   </field>
-  <field>
-    <name>loc_block_id</name>
-    <type>int unsigned</type>
-    <comment>FK to Location Block ID. This is specifically not an FK to avoid circular constraints</comment>
-    <add>2.0</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>contact_id</name>
     <type>int unsigned</type>
diff --git a/civicrm/xml/schema/Core/EntityFile.xml b/civicrm/xml/schema/Core/EntityFile.xml
index e4b06ffd251ffa733c542ad22ee51874588ef856..cad60ba916fcc7beaf633a38fcb9c786426f36c5 100644
--- a/civicrm/xml/schema/Core/EntityFile.xml
+++ b/civicrm/xml/schema/Core/EntityFile.xml
@@ -5,6 +5,7 @@
   <class>EntityFile</class>
   <name>civicrm_entity_file</name>
   <comment>Attaches (joins) uploaded files (images, documents, etc.) to entities (Contacts, Groups, Actions).</comment>
+  <add>1.5</add>
   <log>true</log>
   <field>
     <name>id</name>
diff --git a/civicrm/xml/schema/Core/EntityTag.xml b/civicrm/xml/schema/Core/EntityTag.xml
index 1f3543dcf0a12ecc31b6cb2d5cd9095c8bee9bae..301706a42b4d39b244848d554709ea37467f1eef 100644
--- a/civicrm/xml/schema/Core/EntityTag.xml
+++ b/civicrm/xml/schema/Core/EntityTag.xml
@@ -5,6 +5,7 @@
   <class>EntityTag</class>
   <name>civicrm_entity_tag</name>
   <comment>Tag entities (Contacts, Groups, Actions) to categories.</comment>
+  <add>1.1</add>
   <log>true</log>
   <field>
     <name>id</name>
@@ -18,22 +19,6 @@
     <name>id</name>
     <autoincrement>true</autoincrement>
   </primaryKey>
-  <field>
-    <name>contact_id</name>
-    <type>int unsigned</type>
-    <required>true</required>
-    <comment>FK to contact table.</comment>
-    <add>2.0</add>
-    <drop>3.2</drop>
-  </field>
-  <foreignKey>
-    <name>contact_id</name>
-    <table>civicrm_contact</table>
-    <key>id</key>
-    <add>2.0</add>
-    <drop>3.2</drop>
-    <onDelete>CASCADE</onDelete>
-  </foreignKey>
   <field>
     <name>entity_table</name>
     <type>varchar</type>
@@ -58,13 +43,6 @@
     <typeColumn>entity_table</typeColumn>
     <add>3.2</add>
   </dynamicForeignKey>
-  <index>
-    <name>index_entity</name>
-    <fieldName>entity_table</fieldName>
-    <fieldName>entity_id</fieldName>
-    <add>3.2</add>
-    <drop>3.4</drop>
-  </index>
   <field>
     <name>tag_id</name>
     <type>int unsigned</type>
diff --git a/civicrm/xml/schema/Core/Job.xml b/civicrm/xml/schema/Core/Job.xml
index 45429a4afff94779db49007f4b3ea5cb95734ee5..a135360f051beee4b538fce321f0ac801e50039a 100644
--- a/civicrm/xml/schema/Core/Job.xml
+++ b/civicrm/xml/schema/Core/Job.xml
@@ -85,15 +85,6 @@
     <comment>Description of the job</comment>
     <add>4.1</add>
   </field>
-  <field>
-    <name>api_prefix</name>
-    <type>varchar</type>
-    <length>255</length>
-    <default>"civicrm_api3"</default>
-    <comment>Prefix of the job api call</comment>
-    <add>4.1</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>api_entity</name>
     <title>API Entity</title>
diff --git a/civicrm/xml/schema/Core/Mapping.xml b/civicrm/xml/schema/Core/Mapping.xml
index 54ab26d3d0bec725a595d11ae66cc4ec7b85a52d..bd5420ea27152e1cbf5ac225f32f4e79715e4fd1 100644
--- a/civicrm/xml/schema/Core/Mapping.xml
+++ b/civicrm/xml/schema/Core/Mapping.xml
@@ -35,16 +35,6 @@
     <comment>Description of Mapping.</comment>
     <add>1.2</add>
   </field>
-  <field>
-    <name>mapping_type</name>
-    <type>enum</type>
-    <values>Export, Import, Export Contributions, Import Contributions, Import Activity, Search Builder, Import
-      Memberships, Import Participants
-    </values>
-    <comment>Type of Mapping.</comment>
-    <add>1.2</add>
-    <drop>2.1</drop>
-  </field>
   <field>
     <name>mapping_type_id</name>
     <type>int unsigned</type>
diff --git a/civicrm/xml/schema/Core/MappingField.xml b/civicrm/xml/schema/Core/MappingField.xml
index d28b69b30c94cda0c608591e3564fc46d23ca5f2..0c444143756a4e3d7dfa708876489bac707c6f35 100644
--- a/civicrm/xml/schema/Core/MappingField.xml
+++ b/civicrm/xml/schema/Core/MappingField.xml
@@ -78,14 +78,6 @@
     <key>id</key>
     <add>1.2</add>
   </foreignKey>
-  <field>
-    <name>phone_type</name>
-    <type>varchar</type>
-    <length>64</length>
-    <comment>Phone type, if required</comment>
-    <add>1.2</add>
-    <drop>2.2</drop>
-  </field>
   <field>
     <name>phone_type_id</name>
     <title>Phone Type</title>
diff --git a/civicrm/xml/schema/Core/Phone.xml b/civicrm/xml/schema/Core/Phone.xml
index 453c64db5f5eaf6523e62c34002408ffef1a10be..eb0ad8e1f6a90a4f4a16b7e9be1a58b4267a7d88 100644
--- a/civicrm/xml/schema/Core/Phone.xml
+++ b/civicrm/xml/schema/Core/Phone.xml
@@ -36,7 +36,7 @@
   </foreignKey>
   <field>
     <name>location_type_id</name>
-    <title>Phone Location Type</title>
+    <title>Phone Location Type ID</title>
     <type>int unsigned</type>
     <comment>Which Location does this phone belong to.</comment>
     <pseudoconstant>
@@ -46,6 +46,7 @@
     </pseudoconstant>
     <html>
       <type>Select</type>
+      <label>Phone Location Type</label>
     </html>
     <add>2.0</add>
   </field>
@@ -136,19 +137,9 @@
     <fieldName>phone_numeric</fieldName>
     <add>4.3</add>
   </index>
-  <field>
-    <name>phone_type</name>
-    <type>enum</type>
-    <values>Phone, Mobile, Fax, Pager</values>
-    <headerPattern>/phone\s+type/i</headerPattern>
-    <dataPattern>/phone|mobile|fax|pager/i</dataPattern>
-    <comment>What type of telecom device is this.</comment>
-    <add>1.1</add>
-    <drop>2.2</drop>
-  </field>
   <field>
     <name>phone_type_id</name>
-    <title>Phone Type</title>
+    <title>Phone Type ID</title>
     <type>int unsigned</type>
     <export>true</export>
     <comment>Which type of phone does this number belongs.</comment>
@@ -157,6 +148,7 @@
     </pseudoconstant>
     <html>
       <type>Select</type>
+      <label>Phone Type</label>
     </html>
     <add>2.2</add>
   </field>
diff --git a/civicrm/xml/schema/Core/UFField.xml b/civicrm/xml/schema/Core/UFField.xml
index fdcdcf83b3f348b5a1477518ff75ecfbf0a5326f..9b012ab4c8376550050b08fb332ac2c1bd291d84 100644
--- a/civicrm/xml/schema/Core/UFField.xml
+++ b/civicrm/xml/schema/Core/UFField.xml
@@ -103,22 +103,6 @@
     <comment>Description and/or help text to display before this field.</comment>
     <add>3.2</add>
   </field>
-  <field>
-    <name>is_registration</name>
-    <type>boolean</type>
-    <default>0</default>
-    <comment>Is this field included in new user registration forms?</comment>
-    <add>1.1</add>
-    <drop>1.3</drop>
-  </field>
-  <field>
-    <name>is_match</name>
-    <type>boolean</type>
-    <default>0</default>
-    <comment>Is this field part of the key for matching users to contacts?</comment>
-    <add>1.1</add>
-    <drop>1.3</drop>
-  </field>
   <field>
     <name>visibility</name>
     <title>Profile Field Visibility</title>
@@ -134,14 +118,6 @@
       <type>Select</type>
     </html>
   </field>
-  <field>
-    <name>listings_title</name>
-    <type>varchar</type>
-    <length>64</length>
-    <comment>Page title for listings page (users who share a common value for this property).</comment>
-    <add>1.1</add>
-    <drop>1.2</drop>
-  </field>
   <field>
     <name>in_selector</name>
     <title>Profile Field Is a Filter</title>
@@ -172,14 +148,6 @@
     <add>1.3</add>
     <onDelete>SET NULL</onDelete>
   </foreignKey>
-  <field>
-    <name>phone_type</name>
-    <type>varchar</type>
-    <length>64</length>
-    <comment>Phone type, if required</comment>
-    <add>1.3</add>
-    <drop>2.2</drop>
-  </field>
   <field>
     <name>phone_type_id</name>
     <title>Profile Field Phone Type</title>
diff --git a/civicrm/xml/schema/Core/UFGroup.xml b/civicrm/xml/schema/Core/UFGroup.xml
index cafe160ac631ff8e3fd012a0b4e437ae0ebe9b32..37d5d0a8aa8d10ca8a811dac749ceb7093a1e6d3 100644
--- a/civicrm/xml/schema/Core/UFGroup.xml
+++ b/civicrm/xml/schema/Core/UFGroup.xml
@@ -37,13 +37,6 @@
     <serialize>COMMA</serialize>
     <add>2.1</add>
   </field>
-  <field>
-    <name>form_type</name>
-    <type>enum</type>
-    <values>CiviCRM Profile</values>
-    <comment>Type of form.</comment>
-    <drop>2.1</drop>
-  </field>
   <field>
     <name>title</name>
     <title>Profile Name</title>
@@ -81,14 +74,6 @@
     <comment>Optional verbose description of the profile.</comment>
     <add>4.4</add>
   </field>
-  <field>
-    <name>collapse_display</name>
-    <type>int unsigned</type>
-    <default>0</default>
-    <comment>Will this group be in collapsed or expanded mode on initial display ?</comment>
-    <add>1.1</add>
-    <drop>2.2</drop>
-  </field>
   <field>
     <name>help_pre</name>
     <type>text</type>
@@ -114,19 +99,6 @@
     </html>
     <add>1.2</add>
   </field>
-  <field>
-    <name>weight</name>
-    <title>Profile Weight</title>
-    <type>int</type>
-    <required>true</required>
-    <default>1</default>
-    <html>
-      <type>Text</type>
-    </html>
-    <comment>Controls display order when multiple user framework groups are setup for concurrent display.</comment>
-    <add>1.2</add>
-    <drop>1.3</drop>
-  </field>
   <field>
     <name>limit_listings_group_id</name>
     <title>Profile Search Limit Group</title>
diff --git a/civicrm/xml/schema/Core/UFMatch.xml b/civicrm/xml/schema/Core/UFMatch.xml
index fb3a05425dfb6af68856c6c1ecf9bd288bd5a8ef..9e573a5a79df7bf143a8e904fb18a2152e61a94c 100644
--- a/civicrm/xml/schema/Core/UFMatch.xml
+++ b/civicrm/xml/schema/Core/UFMatch.xml
@@ -73,15 +73,6 @@
     <add>1.1</add>
     <onDelete>CASCADE</onDelete>
   </foreignKey>
-  <field>
-    <name>email</name>
-    <type>varchar</type>
-    <length>64</length>
-    <rule>email</rule>
-    <comment>Email address</comment>
-    <add>1.1</add>
-    <drop>2.0</drop>
-  </field>
   <field>
     <name>language</name>
     <title>Preferred Language</title>
@@ -90,14 +81,6 @@
     <comment>UI language preferred by the given user/contact</comment>
     <add>2.1</add>
   </field>
-  <index>
-    <name>UI_uf_id_domain_id</name>
-    <fieldName>uf_id</fieldName>
-    <fieldName>domain_id</fieldName>
-    <unique>true</unique>
-    <add>1.5</add>
-    <drop>1.7</drop>
-  </index>
   <index>
     <name>UI_uf_name_domain_id</name>
     <fieldName>uf_name</fieldName>
diff --git a/civicrm/xml/schema/Dedupe/RuleGroup.xml b/civicrm/xml/schema/Dedupe/RuleGroup.xml
index 6adbe3dba41e236ab8f87cc6d9cc552d59fa1e3b..09a5873628381c2565a390bd72b9d4b3e7437912 100644
--- a/civicrm/xml/schema/Dedupe/RuleGroup.xml
+++ b/civicrm/xml/schema/Dedupe/RuleGroup.xml
@@ -45,15 +45,6 @@
       <type>Text</type>
     </html>
   </field>
-  <field>
-    <name>level</name>
-    <title>Level</title>
-    <type>enum</type>
-    <values>Strict, Fuzzy</values>
-    <comment>Whether the rule should be used for cases where strict matching of the given contact type is required or a fuzzy one</comment>
-    <add>2.1</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>used</name>
     <type>varchar</type>
@@ -69,14 +60,6 @@
       <type>Radio</type>
     </html>
   </field>
-  <field>
-    <name>is_default</name>
-    <title>Default></title>
-    <type>boolean</type>
-    <comment>Is this a default rule (one rule for every contact type + level combination should be default)</comment>
-    <add>2.1</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>name</name>
     <title>Name</title>
diff --git a/civicrm/xml/schema/Event/Event.xml b/civicrm/xml/schema/Event/Event.xml
index d4c9c65299dfa31b57f30e1687622fe03d3e2b7c..979a84df4de453565b0df4caeee064e77a763763 100644
--- a/civicrm/xml/schema/Event/Event.xml
+++ b/civicrm/xml/schema/Event/Event.xml
@@ -218,14 +218,6 @@
       <type>CheckBox</type>
     </html>
   </field>
-  <field>
-    <name>contribution_type_id</name>
-    <type>int unsigned</type>
-    <default>0</default>
-    <comment>Contribution type assigned to paid event registrations for this event. Required if is_monetary is true.</comment>
-    <add>1.7</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>financial_type_id</name>
     <type>int unsigned</type>
@@ -246,6 +238,7 @@
     <name>payment_processor</name>
     <type>varchar</type>
     <length>128</length>
+    <serialize>SEPARATOR_TRIMMED</serialize>
     <comment>Payment Processors configured for this Event (if is_monetary is true)</comment>
     <pseudoconstant>
       <table>civicrm_payment_processor</table>
@@ -317,19 +310,6 @@
     <onDelete>SET NULL</onDelete>
     <add>2.0</add>
   </foreignKey>
-  <field>
-    <name>receipt_text</name>
-    <type>varchar</type>
-    <html>
-      <type>TextArea</type>
-      <rows>6</rows>
-      <cols>50</cols>
-    </html>
-    <length>255</length>
-    <comment>Receipt Text for off-line event participation</comment>
-    <add>2.0</add>
-    <drop>2.1</drop>
-  </field>
   <field>
     <name>default_role_id</name>
     <uniqueName>default_role_id</uniqueName>
@@ -679,7 +659,7 @@
   </field>
   <field>
     <name>selfcancelxfer_time</name>
-    <type>int unsigned</type>
+    <type>int</type>
     <default>0</default>
     <title>Self-service Cancellation or Transfer Time</title>
     <comment>Number of hours prior to event start date to allow self-service cancellation or transfer.</comment>
diff --git a/civicrm/xml/schema/Financial/EntityFinancialTrxn.xml b/civicrm/xml/schema/Financial/EntityFinancialTrxn.xml
index 653fc792d5d61bd4705ce81d6a6214248d216821..f48a3f3c6574af9723d4357d1cf6f95143b0c932 100644
--- a/civicrm/xml/schema/Financial/EntityFinancialTrxn.xml
+++ b/civicrm/xml/schema/Financial/EntityFinancialTrxn.xml
@@ -63,15 +63,6 @@
     <comment>allocated amount of transaction to this entity</comment>
     <add>3.2</add>
   </field>
-  <field>
-    <name>currency</name>
-    <type>varchar</type>
-    <length>3</length>
-    <default>NULL</default>
-    <comment>3 character string, value from config setting or input via user.</comment>
-    <add>3.2</add>
-    <drop>4.3</drop>
-  </field>
   <index>
     <name>UI_entity_financial_trxn_entity_table</name>
     <fieldName>entity_table</fieldName>
diff --git a/civicrm/xml/schema/Financial/FinancialAccount.xml b/civicrm/xml/schema/Financial/FinancialAccount.xml
index eedf5c621069640207403b413993446f6d62fba4..3188e5847e3113bb3a0a21bf9c68af76d70f16d9 100644
--- a/civicrm/xml/schema/Financial/FinancialAccount.xml
+++ b/civicrm/xml/schema/Financial/FinancialAccount.xml
@@ -42,13 +42,6 @@
     <add>4.3</add>
     <onDelete>SET NULL</onDelete>
   </foreignKey>
-  <field>
-    <name>account_type_id</name>
-    <type>int unsigned</type>
-    <required>true</required>
-    <add>3.2</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>financial_account_type_id</name>
     <type>int unsigned</type>
diff --git a/civicrm/xml/schema/Financial/FinancialTrxn.xml b/civicrm/xml/schema/Financial/FinancialTrxn.xml
index 94a2e3f023e5e03fe5733272812a5798c56784e2..e34d9b91123c79838ab7caa7294672e5010a95a6 100644
--- a/civicrm/xml/schema/Financial/FinancialTrxn.xml
+++ b/civicrm/xml/schema/Financial/FinancialTrxn.xml
@@ -17,34 +17,6 @@
     <name>id</name>
     <autoincrement>true</autoincrement>
   </primaryKey>
-  <field>
-    <name>from_account_id</name>
-    <type>int unsigned</type>
-    <comment>FK to financial_account table.</comment>
-    <add>3.2</add>
-    <drop>4.3</drop>
-  </field>
-  <foreignKey>
-    <name>from_account_id</name>
-    <table>civicrm_financial_account</table>
-    <key>id</key>
-    <add>3.2</add>
-    <drop>4.3</drop>
-  </foreignKey>
-  <field>
-    <name>to_account_id</name>
-    <type>int unsigned</type>
-    <comment>FK to financial_account table.</comment>
-    <add>3.2</add>
-    <drop>4.3</drop>
-  </field>
-  <foreignKey>
-    <name>to_account_id</name>
-    <table>civicrm_financial_account</table>
-    <key>id</key>
-    <add>3.2</add>
-    <drop>4.3</drop>
-  </foreignKey>
   <field>
     <name>from_financial_account_id</name>
     <type>int unsigned</type>
@@ -99,15 +71,6 @@
       <formatType>activityDateTime</formatType>
     </html>
   </field>
-  <field>
-    <name>trxn_type</name>
-    <title>Financial Transaction Type</title>
-    <type>enum</type>
-    <values>Debit,Credit</values>
-    <required>true</required>
-    <add>1.3</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>total_amount</name>
     <title>Financial Total Amount</title>
@@ -161,15 +124,6 @@
     <comment>Is this entry either a payment or a reversal of a payment?</comment>
     <add>4.7</add>
   </field>
-  <field>
-    <name>payment_processor</name>
-    <type>varchar</type>
-    <length>64</length>
-    <required>true</required>
-    <comment>derived from Processor setting in civicrm.settings.php.</comment>
-    <add>1.3</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>trxn_id</name>
     <title>Transaction ID</title>
diff --git a/civicrm/xml/schema/Financial/FinancialType.xml b/civicrm/xml/schema/Financial/FinancialType.xml
index bbdca886dae8338a792992bab2dcfc2aed1cec63..3d02bf8b0991bab93460c21c15629eafa355f033 100644
--- a/civicrm/xml/schema/Financial/FinancialType.xml
+++ b/civicrm/xml/schema/Financial/FinancialType.xml
@@ -37,16 +37,6 @@
     </html>
     <add>1.3</add>
   </field>
-  <field>
-    <name>accounting_code</name>
-    <title>Accounting Code</title>
-    <type>varchar</type>
-    <length>64</length>
-    <export>true</export>
-    <comment>Optional value for mapping contributions to accounting system codes for each type/category of contribution.</comment>
-    <add>1.3</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>description</name>
     <type>varchar</type>
diff --git a/civicrm/xml/schema/Financial/PaymentProcessor.xml b/civicrm/xml/schema/Financial/PaymentProcessor.xml
index e1aac7cab3313d3ab3336805edb1a7438879c11b..a363ce8784677d213180de420e5e58b2753d1bec 100644
--- a/civicrm/xml/schema/Financial/PaymentProcessor.xml
+++ b/civicrm/xml/schema/Financial/PaymentProcessor.xml
@@ -70,14 +70,6 @@
     <comment>Payment Processor Description.</comment>
     <add>1.8</add>
   </field>
-  <field>
-    <name>payment_processor_type</name>
-    <type>varchar</type>
-    <length>255</length>
-    <comment>Payment Processor Type.</comment>
-    <add>1.8</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>payment_processor_type_id</name>
     <title>Payment Processor Type ID</title>
diff --git a/civicrm/xml/schema/Grant/Grant.xml b/civicrm/xml/schema/Grant/Grant.xml
index 6e222f8e0d6995c37a57f1aec0e910cf3bd7abaa..b6c2c8923519bbf48baf1c875e677d7d2bdf957a 100644
--- a/civicrm/xml/schema/Grant/Grant.xml
+++ b/civicrm/xml/schema/Grant/Grant.xml
@@ -179,15 +179,6 @@
       <type>Select</type>
     </html>
   </field>
-  <field>
-    <name>currency</name>
-    <type>varchar</type>
-    <length>8</length>
-    <default>NULL</default>
-    <comment>3 character string, value from config setting or input via user.</comment>
-    <add>3.2</add>
-    <drop>4.3</drop>
-  </field>
   <field>
     <name>rationale</name>
     <type>text</type>
diff --git a/civicrm/xml/schema/Member/MembershipType.xml b/civicrm/xml/schema/Member/MembershipType.xml
index 0007eefaf2fe76a2ca347cc4221a357c54a1b49e..cd528adaf26fc8d599c30550e5c14fc0405259e0 100644
--- a/civicrm/xml/schema/Member/MembershipType.xml
+++ b/civicrm/xml/schema/Member/MembershipType.xml
@@ -84,21 +84,6 @@
     <add>1.5</add>
     <onDelete>RESTRICT</onDelete>
   </foreignKey>
-  <field>
-    <name>contribution_type_id</name>
-    <type>int unsigned</type>
-    <required>true</required>
-    <comment>If membership is paid by a contribution - what contribution type should be used. FK to Contribution Type ID</comment>
-    <add>1.5</add>
-    <drop>4.3</drop>
-  </field>
-  <foreignKey>
-    <name>contribution_type_id</name>
-    <table>civicrm_contribution_type</table>
-    <key>id</key>
-    <add>1.5</add>
-    <drop>4.3</drop>
-  </foreignKey>
   <field>
     <name>financial_type_id</name>
     <title>Membership Financial Type</title>
@@ -192,13 +177,6 @@
     <add>1.5</add>
     <serialize>SEPARATOR_TRIMMED</serialize>
   </field>
-  <foreignKey>
-    <name>relationship_type_id</name>
-    <table>civicrm_relationship_type</table>
-    <key>id</key>
-    <add>1.5</add>
-    <drop>3.3</drop>
-  </foreignKey>
   <index>
     <name>index_relationship_type_id</name>
     <fieldName>relationship_type_id</fieldName>
diff --git a/civicrm/xml/schema/PCP/PCP.xml b/civicrm/xml/schema/PCP/PCP.xml
index dfb8a13f8c7429d032f737b0a8c8fc8277b48fbc..0ef29ee2563cd656777b61a40442f4b8971a3237 100644
--- a/civicrm/xml/schema/PCP/PCP.xml
+++ b/civicrm/xml/schema/PCP/PCP.xml
@@ -91,20 +91,6 @@
       <type>Text</type>
     </html>
   </field>
-  <field>
-    <name>contribution_page_id</name>
-    <type>int unsigned</type>
-    <required>true</required>
-    <comment>The Contribution Page which triggered this pcp</comment>
-    <add>2.2</add>
-    <drop>4.1</drop>
-  </field>
-  <foreignKey>
-    <name>contribution_page_id</name>
-    <table>civicrm_contribution_page</table>
-    <key>id</key>
-    <drop>4.1</drop>
-  </foreignKey>
   <field>
     <name>page_id</name>
     <title>Contribution Page</title>
@@ -182,15 +168,6 @@
       <type>Select</type>
     </html>
   </field>
-  <field>
-    <name>referer</name>
-    <title>Referer</title>
-    <type>varchar</type>
-    <length>255</length>
-    <default>NULL</default>
-    <add>2.2</add>
-    <drop>4.1</drop>
-  </field>
   <field>
     <name>is_active</name>
     <title>Enabled?</title>
diff --git a/civicrm/xml/schema/PCP/PCPBlock.xml b/civicrm/xml/schema/PCP/PCPBlock.xml
index b07bc5b035556ca14c48574688a879614af825f3..d25b6eb7c221e986b066158e08dd1635306e25a8 100644
--- a/civicrm/xml/schema/PCP/PCPBlock.xml
+++ b/civicrm/xml/schema/PCP/PCPBlock.xml
@@ -39,13 +39,6 @@
     <typeColumn>entity_table</typeColumn>
     <add>2.2</add>
   </dynamicForeignKey>
-  <foreignKey>
-    <name>entity_id</name>
-    <table>civicrm_contribution_page</table>
-    <key>id</key>
-    <add>2.2</add>
-    <drop>4.1</drop>
-  </foreignKey>
   <field>
     <name>target_entity_type</name>
     <title>Target Entity</title>
diff --git a/civicrm/xml/schema/Pledge/Pledge.xml b/civicrm/xml/schema/Pledge/Pledge.xml
index 39943c58efa93b78611d6b290c2cbcb2ae5834df..32b806f39f52c8a0c057241d3e991e5c8dce8b25 100644
--- a/civicrm/xml/schema/Pledge/Pledge.xml
+++ b/civicrm/xml/schema/Pledge/Pledge.xml
@@ -40,23 +40,6 @@
     <add>2.1</add>
     <onDelete>CASCADE</onDelete>
   </foreignKey>
-  <field>
-    <name>contribution_type_id</name>
-    <uniqueName>pledge_contribution_type_id</uniqueName>
-    <export>false</export>
-    <type>int unsigned</type>
-    <comment>FK to Contribution Type. This is propagated to contribution record when pledge payments are made.</comment>
-    <add>2.1</add>
-    <drop>4.3</drop>
-  </field>
-  <foreignKey>
-    <name>contribution_type_id</name>
-    <table>civicrm_contribution_type</table>
-    <key>id</key>
-    <add>2.1</add>
-    <drop>4.3</drop>
-    <onDelete>SET NULL</onDelete>
-  </foreignKey>
   <field>
     <name>financial_type_id</name>
     <title>Type</title>
diff --git a/civicrm/xml/schema/Price/LineItem.xml b/civicrm/xml/schema/Price/LineItem.xml
index a1f006c9b53520ffa1e9024dab2b95344cdcd61c..82b9c9a7cc56c9d5d6c012334206ba9655d8ab97 100644
--- a/civicrm/xml/schema/Price/LineItem.xml
+++ b/civicrm/xml/schema/Price/LineItem.xml
@@ -67,15 +67,6 @@
     <key>id</key>
     <onDelete>SET NULL</onDelete>
   </foreignKey>
-  <field>
-    <name>option_group_id</name>
-    <title>Line Item Option Group</title>
-    <type>int unsigned</type>
-    <required>true</required>
-    <comment>FK to option group</comment>
-    <add>1.7</add>
-    <drop>3.3</drop>
-  </field>
   <field>
     <name>label</name>
     <title>Line Item Label</title>
diff --git a/civicrm/xml/schema/Price/PriceField.xml b/civicrm/xml/schema/Price/PriceField.xml
index 0c2c1af3c1a811f468e5a33afc5139fe16d32346..ce61ecc87c4fc5b1c49eb75b6b4554b0c1fa47d9 100644
--- a/civicrm/xml/schema/Price/PriceField.xml
+++ b/civicrm/xml/schema/Price/PriceField.xml
@@ -221,12 +221,4 @@
       <type>Select</type>
     </html>
   </field>
-  <field>
-    <name>count</name>
-    <type>int unsigned</type>
-    <default>NULL</default>
-    <comment>Number of Participants Per field</comment>
-    <add>3.2</add>
-    <drop>3.3</drop>
-  </field>
 </table>
diff --git a/civicrm/xml/schema/Price/PriceSet.xml b/civicrm/xml/schema/Price/PriceSet.xml
index 25266f1dbfc4380015787096eb272bd39386a04e..d84ac533927c08f81c08d77a287d8a6f57bb2c13 100644
--- a/civicrm/xml/schema/Price/PriceSet.xml
+++ b/civicrm/xml/schema/Price/PriceSet.xml
@@ -137,23 +137,6 @@
       <type>Text</type>
     </html>
   </field>
-  <field>
-    <name>contribution_type_id</name>
-    <title>Price Set Contribution Type</title>
-    <type>int unsigned</type>
-    <default>NULL</default>
-    <comment>FK to Contribution Type(for membership price sets only).</comment>
-    <add>3.4</add>
-    <drop>4.3</drop>
-  </field>
-  <foreignKey>
-    <name>contribution_type_id</name>
-    <table>civicrm_contribution_type</table>
-    <key>id</key>
-    <add>3.4</add>
-    <drop>4.3</drop>
-    <onDelete>SET NULL</onDelete>
-  </foreignKey>
   <field>
     <name>financial_type_id</name>
     <title>Financial Type</title>
diff --git a/civicrm/xml/templates/civicrm_data.tpl b/civicrm/xml/templates/civicrm_data.tpl
index dc9967e4fec5e4a6463761017d15ee14bfcbb9c1..d9891d3e1c96476a8f26f5fb10c0050a65bcf07c 100644
--- a/civicrm/xml/templates/civicrm_data.tpl
+++ b/civicrm/xml/templates/civicrm_data.tpl
@@ -1781,3 +1781,4 @@ VALUES
 -- do not try this at home folks.
 INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'sequentialcreditnotes', 'Sequential credit notes', 'Sequential credit notes', 'sequentialcreditnotes', 1);
 INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'eventcart', 'Event cart', 'Event cart', 'eventcart', 1);
+INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'financialacls', 'Financial ACLs', 'Financial ACLs', 'financialacls', 1);
diff --git a/civicrm/xml/templates/dao.tpl b/civicrm/xml/templates/dao.tpl
index 3a0c51e328a14abcd315670d808e7bdb143ceb06..87a3463531c9a096e0b7abfdcca99893207263ae 100644
--- a/civicrm/xml/templates/dao.tpl
+++ b/civicrm/xml/templates/dao.tpl
@@ -7,12 +7,15 @@
  * {$generated}
  * (GenCodeChecksum:{$genCodeChecksum})
  */
-
+{$useHelper}
 /**
  * Database access object for the {$table.entity} entity.
  */
 class {$table.className} extends CRM_Core_DAO {ldelim}
 
+     const EXT = {$ext};
+     const TABLE_ADDED = '{$table.add}';
+
      /**
       * Static instance to hold the table name.
       *
@@ -60,7 +63,7 @@ class {$table.className} extends CRM_Core_DAO {ldelim}
      * Returns localized title of this entity.
      */
     public static function getEntityTitle() {ldelim}
-        return ts('{$table.title}');
+        return {$tsFunctionName}('{$table.title}');
     {rdelim}
 
 
diff --git a/civicrm/xml/templates/message_templates/contribution_invoice_receipt_html.tpl b/civicrm/xml/templates/message_templates/contribution_invoice_receipt_html.tpl
index d6093e8510f7f090825e838f39b962c5963b2887..2222bf0b5c05c6c24d0e9bf1429fdc4d0173bb85 100644
--- a/civicrm/xml/templates/message_templates/contribution_invoice_receipt_html.tpl
+++ b/civicrm/xml/templates/message_templates/contribution_invoice_receipt_html.tpl
@@ -126,29 +126,28 @@
                 <td style="text-align:right;white-space: nowrap"><b><font size="1">{ts 1=$defaultCurrency}TOTAL %1{/ts}</font></b></td>
                 <td style="text-align:right;"><font size="1">{$amount|crmMoney:$currency}</font></td>
               </tr>
-             {if $amountDue != 0}
-                <tr>
-                  <td colspan="3"></td>
-                  <td style="text-align:right;white-space: nowrap"><font size="1">
-                    {if $contribution_status_id == $refundedStatusId}
-                      {ts}Amount Credited{/ts}
-                    {else}
-                      {ts}Amount Paid{/ts}
-                    {/if}
-                   </font>
-                  </td>
-                  <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td>
-                </tr>
-                <tr>
-                  <td colspan="3"></td>
-                  <td colspan="2"><hr></hr></td>
-                </tr>
-                <tr>
-                  <td colspan="3"></td>
-                  <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td>
-                  <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td>
-                </tr>
-              {/if}
+              <tr>
+                <td colspan="3"></td>
+                <td style="text-align:right;white-space: nowrap"><font size="1">
+                  {if $contribution_status_id == $refundedStatusId}
+                    {ts}Amount Credited{/ts}
+                  {else}
+                    {ts}Amount Paid{/ts}
+                  {/if}
+                 </font>
+                </td>
+                <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td>
+              </tr>
+              <tr>
+                <td colspan="3"></td>
+                <td colspan="2"><hr></hr></td>
+              </tr>
+              <tr>
+                <td colspan="3"></td>
+                <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td>
+                <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td>
+              </tr>
+
               <br/><br/><br/>
               <tr>
                 <td colspan="5"></td>
diff --git a/civicrm/xml/templates/message_templates/event_online_receipt_html.tpl b/civicrm/xml/templates/message_templates/event_online_receipt_html.tpl
index c9e0806256758b4ee211926e4ee31c794b33da98..b9fdec15b4439318ce6f4251931307a35bf7f219 100644
--- a/civicrm/xml/templates/message_templates/event_online_receipt_html.tpl
+++ b/civicrm/xml/templates/message_templates/event_online_receipt_html.tpl
@@ -497,7 +497,7 @@
     {if $event.allow_selfcancelxfer }
      <tr>
       <td colspan="2" {$valueStyle}>
-        {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
+        {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
         {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
         <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a>
       </td>
diff --git a/civicrm/xml/templates/message_templates/event_online_receipt_text.tpl b/civicrm/xml/templates/message_templates/event_online_receipt_text.tpl
index dbd5f1ba880383930183619d10706e17109ee9b3..05be6dff34a71d549e655fd1704e3339317a60e9 100644
--- a/civicrm/xml/templates/message_templates/event_online_receipt_text.tpl
+++ b/civicrm/xml/templates/message_templates/event_online_receipt_text.tpl
@@ -291,7 +291,7 @@ You were registered by: {$payer.name}
 {/if}
 
 {if $event.allow_selfcancelxfer }
-{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
+{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
    {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
 {ts}Transfer or cancel your registration:{/ts} {$selfService}
 {/if}
diff --git a/civicrm/xml/templates/message_templates/participant_confirm_html.tpl b/civicrm/xml/templates/message_templates/participant_confirm_html.tpl
index ed1d2b7b0db90d4d9f71837e27f653c073aedf6f..c4075a04bbb64bd797925d7fbc535142608424e5 100644
--- a/civicrm/xml/templates/message_templates/participant_confirm_html.tpl
+++ b/civicrm/xml/templates/message_templates/participant_confirm_html.tpl
@@ -165,7 +165,7 @@
   {if $event.allow_selfcancelxfer }
    <tr>
      <td colspan="2" {$valueStyle}>
-       {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
+       {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br />
          {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
        <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a>
      </td>
diff --git a/civicrm/xml/templates/message_templates/participant_confirm_text.tpl b/civicrm/xml/templates/message_templates/participant_confirm_text.tpl
index 6296b4594d76321131102b3a0876341451759be4..2c459e66e29fae1203f20663c35453397060d175 100644
--- a/civicrm/xml/templates/message_templates/participant_confirm_text.tpl
+++ b/civicrm/xml/templates/message_templates/participant_confirm_text.tpl
@@ -13,7 +13,7 @@ Click this link to go to a web page where you can confirm your registration onli
 {$confirmUrl}
 {/if}
 {if $event.allow_selfcancelxfer }
-{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
+{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}
    {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}"  h=0 a=1 fe=1}{/capture}
 {ts}Transfer or cancel your registration:{/ts} {$selfService}
 {/if}
diff --git a/civicrm/xml/version.xml b/civicrm/xml/version.xml
index b94dfffc42a2b47bf968c2b357c816f35e8a7f9e..e8dc6c3e450d8aa9e4577e010995195d9170c0be 100644
--- a/civicrm/xml/version.xml
+++ b/civicrm/xml/version.xml
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="iso-8859-1" ?>
 <version>
-  <version_no>5.29.1</version_no>
+  <version_no>5.30.0</version_no>
 </version>
diff --git a/wp-rest/Controller/AuthorizeIPN.php b/wp-rest/Controller/AuthorizeIPN.php
index 4cd9da9a97786f3176f55ec59c2528a77b774554..8771120b7932012c16442a70f1fb33e46e3bd28b 100644
--- a/wp-rest/Controller/AuthorizeIPN.php
+++ b/wp-rest/Controller/AuthorizeIPN.php
@@ -31,6 +31,7 @@ class AuthorizeIPN extends Base {
 		register_rest_route( $this->get_namespace(), $this->get_rest_base(), [
 			[
 				'methods' => \WP_REST_Server::ALLMETHODS,
+				'permission_callback' => '__return_true',
 				'callback' => [ $this, 'get_item' ]
 			]
 		] );
diff --git a/wp-rest/Controller/Cxn.php b/wp-rest/Controller/Cxn.php
index 7f7cca5c5621c3eb3441ca7060d5e1048eb85ade..7a21f2362217cfec766a2790f43f0a5f94790df3 100644
--- a/wp-rest/Controller/Cxn.php
+++ b/wp-rest/Controller/Cxn.php
@@ -29,6 +29,7 @@ class Cxn extends Base {
 		register_rest_route( $this->get_namespace(), $this->get_rest_base(), [
 			[
 				'methods' => \WP_REST_Server::ALLMETHODS,
+				'permission_callback' => '__return_true',
 				'callback' => [ $this, 'get_item' ]
 			]
 		] );
diff --git a/wp-rest/Controller/Open.php b/wp-rest/Controller/Open.php
index 450ef991a34897a169761dc9c1fdfcc57b4a0bf5..9ead546d01322fd1a1768af562e50663972ab5af 100644
--- a/wp-rest/Controller/Open.php
+++ b/wp-rest/Controller/Open.php
@@ -28,6 +28,7 @@ class Open extends Base {
 			[
 				'methods' => \WP_REST_Server::READABLE,
 				'callback' => [ $this, 'get_item' ],
+				'permission_callback' => '__return_true',
 				'args' => $this->get_item_args()
 			],
 			'schema' => [ $this, 'get_item_schema' ]
diff --git a/wp-rest/Controller/PayPalIPN.php b/wp-rest/Controller/PayPalIPN.php
index 5b5c38004525287b69024d51ea378cb5615f60bc..a6a4f1fd32b3c9b1d89e9aab1ddf856a08566e07 100644
--- a/wp-rest/Controller/PayPalIPN.php
+++ b/wp-rest/Controller/PayPalIPN.php
@@ -29,6 +29,7 @@ class PayPalIPN extends Base {
 		register_rest_route( $this->get_namespace(), $this->get_rest_base(), [
 			[
 				'methods' => \WP_REST_Server::ALLMETHODS,
+				'permission_callback' => '__return_true',
 				'callback' => [ $this, 'get_item' ]
 			]
 		] );
diff --git a/wp-rest/Controller/PxIPN.php b/wp-rest/Controller/PxIPN.php
index d68fc8d787ae3e87eb449f36b459e0b8d24d845a..c2aff12c04b97f36a381b0364055108a2926cfc1 100644
--- a/wp-rest/Controller/PxIPN.php
+++ b/wp-rest/Controller/PxIPN.php
@@ -29,6 +29,7 @@ class PxIPN extends Base {
 		register_rest_route( $this->get_namespace(), $this->get_rest_base(), [
 			[
 				'methods' => \WP_REST_Server::ALLMETHODS,
+				'permission_callback' => '__return_true',
 				'callback' => [ $this, 'get_item' ]
 			]
 		] );
diff --git a/wp-rest/Controller/Soap.php b/wp-rest/Controller/Soap.php
index 17402cc579a834ca8854014e683a53aad5011399..6a8704e24b31136bcba58130f3c6777b7aa0006d 100644
--- a/wp-rest/Controller/Soap.php
+++ b/wp-rest/Controller/Soap.php
@@ -29,6 +29,7 @@ class Soap extends Base {
 		register_rest_route( $this->get_namespace(), $this->get_rest_base(), [
 			[
 				'methods' => \WP_REST_Server::ALLMETHODS,
+				'permission_callback' => '__return_true',
 				'callback' => [ $this, 'get_item' ]
 			]
 		] );
diff --git a/wp-rest/Controller/Url.php b/wp-rest/Controller/Url.php
index a53d3383bcdc5c48cea27da210b813f3edce2001..5d367adad9a12a566e6069d0fc5d41f4f2c79063 100644
--- a/wp-rest/Controller/Url.php
+++ b/wp-rest/Controller/Url.php
@@ -28,6 +28,7 @@ class Url extends Base {
 			[
 				'methods' => \WP_REST_Server::READABLE,
 				'callback' => [ $this, 'get_item' ],
+				'permission_callback' => '__return_true',
 				'args' => $this->get_item_args()
 			],
 			'schema' => [ $this, 'get_item_schema' ]
diff --git a/wp-rest/Controller/Widget.php b/wp-rest/Controller/Widget.php
index 13fa1e2adde648de8c24b1278039385569bd5c21..5a31fc4d540ec4f14d78852143320860f0547fc0 100644
--- a/wp-rest/Controller/Widget.php
+++ b/wp-rest/Controller/Widget.php
@@ -30,6 +30,7 @@ class Widget extends Base {
 			[
 				'methods' => \WP_REST_Server::READABLE,
 				'callback' => [ $this, 'get_item' ],
+				'permission_callback' => '__return_true',
 				'args' => $this->get_item_args()
 			],
 			'schema' => [ $this, 'get_item_schema' ]