diff --git a/civicrm.php b/civicrm.php
index 23e7cdd85d34441ee05b8b5bcc5b4f5878781cc0..7823dd0fb3fa147fbaed6ea81c3a2ec66b35a6f8 100644
--- a/civicrm.php
+++ b/civicrm.php
@@ -2,7 +2,7 @@
 /**
  * Plugin Name: CiviCRM
  * Description: CiviCRM - Growing and Sustaining Relationships
- * Version: 5.59.4
+ * Version: 5.60.0
  * Requires at least: 4.9
  * Requires PHP:      7.3
  * Author: CiviCRM LLC
@@ -36,7 +36,7 @@ if (!defined('ABSPATH')) {
 }
 
 // Set version here: changing it forces Javascript and CSS to reload.
-define('CIVICRM_PLUGIN_VERSION', '5.59.4');
+define('CIVICRM_PLUGIN_VERSION', '5.60.0');
 
 // Store reference to this file.
 if (!defined('CIVICRM_PLUGIN_FILE')) {
diff --git a/civicrm/CRM/ACL/BAO/ACL.php b/civicrm/CRM/ACL/BAO/ACL.php
index d1166f87ed674171b131ee683af6345de5f3493e..89067de60a4c7a86268dfebe954fe87cf9da331c 100644
--- a/civicrm/CRM/ACL/BAO/ACL.php
+++ b/civicrm/CRM/ACL/BAO/ACL.php
@@ -447,6 +447,7 @@ SELECT g.*
    * @deprecated
    */
   public static function del($aclId) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     self::deleteRecord(['id' => $aclId]);
   }
 
diff --git a/civicrm/CRM/ACL/BAO/ACLEntityRole.php b/civicrm/CRM/ACL/BAO/ACLEntityRole.php
index 1e6899d721c8aa0cab1ef46cf42aa7b05eb18d4b..a892cf4ac70dac8d3cd1f8ad8dc021b1ea7755ac 100644
--- a/civicrm/CRM/ACL/BAO/ACLEntityRole.php
+++ b/civicrm/CRM/ACL/BAO/ACLEntityRole.php
@@ -39,6 +39,7 @@ class CRM_ACL_BAO_ACLEntityRole extends CRM_ACL_DAO_ACLEntityRole {
    * @return CRM_ACL_BAO_ACLEntityRole
    */
   public static function create(&$params) {
+    CRM_Core_Error::deprecatedFunctionWarning('writeRecord');
     return self::writeRecord($params);
   }
 
@@ -82,6 +83,7 @@ class CRM_ACL_BAO_ACLEntityRole extends CRM_ACL_DAO_ACLEntityRole {
    * @deprecated
    */
   public static function del($entityRoleId) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return self::deleteRecord(['id' => $entityRoleId]);
   }
 
diff --git a/civicrm/CRM/ACL/Form/ACL.php b/civicrm/CRM/ACL/Form/ACL.php
index bd82aedab0f53dd57262e18bfb5d9de1a26032b9..61138975ce3e0845e305cf2fbee6e30ebd411f11 100644
--- a/civicrm/CRM/ACL/Form/ACL.php
+++ b/civicrm/CRM/ACL/Form/ACL.php
@@ -247,7 +247,7 @@ class CRM_ACL_Form_ACL extends CRM_Admin_Form {
     CRM_Core_BAO_Cache::resetCaches();
 
     if ($this->_action & CRM_Core_Action::DELETE) {
-      CRM_ACL_BAO_ACL::del($this->_id);
+      CRM_ACL_BAO_ACL::deleteRecord(['id' => $this->_id]);
       CRM_Core_Session::setStatus(ts('Selected ACL has been deleted.'), ts('Record Deleted'), 'success');
     }
     else {
diff --git a/civicrm/CRM/Activity/BAO/Activity.php b/civicrm/CRM/Activity/BAO/Activity.php
index cb9d728e2dbf14f7e5a15df61f61b57bc6c41782..69442988d896774713640c6b86a107d83adee6b3 100644
--- a/civicrm/CRM/Activity/BAO/Activity.php
+++ b/civicrm/CRM/Activity/BAO/Activity.php
@@ -1629,6 +1629,7 @@ WHERE      activity.id IN ($activityIds)";
     //CRM-4027
     if ($targetContactID) {
       $activityParams['target_contact_id'][] = $targetContactID;
+      $activityParams['target_contact_id'] = array_unique($activityParams['target_contact_id'], SORT_NUMERIC);
     }
     // @todo - use api - remove lots of wrangling above. Remove deprecated fatal & let form layer
     // deal with any exceptions.
diff --git a/civicrm/CRM/Activity/Import/Form/DataSource.php b/civicrm/CRM/Activity/Import/Form/DataSource.php
index c965cd5fc4ecd6c8e3a66b6cb1e9fc4f7be15e1a..cc27fcd6e96cadc541e1aa477b088fc53608b8cf 100644
--- a/civicrm/CRM/Activity/Import/Form/DataSource.php
+++ b/civicrm/CRM/Activity/Import/Form/DataSource.php
@@ -20,10 +20,6 @@
  */
 class CRM_Activity_Import_Form_DataSource extends CRM_Import_Form_DataSource {
 
-  const PATH = 'civicrm/import/activity';
-
-  const IMPORT_ENTITY = 'Activity';
-
   /**
    * Get the name of the type to be stored in civicrm_user_job.type_id.
    *
@@ -38,13 +34,6 @@ class CRM_Activity_Import_Form_DataSource extends CRM_Import_Form_DataSource {
    */
   public $submitOnce = TRUE;
 
-  /**
-   * Build the form object.
-   */
-  public function buildQuickForm() {
-    parent::buildQuickForm();
-  }
-
   /**
    * @return CRM_Activity_Import_Parser_Activity
    */
diff --git a/civicrm/CRM/Activity/Selector/Search.php b/civicrm/CRM/Activity/Selector/Search.php
index 14215f6adc3d8ca5e52985fedae5d8548ca7d744..3e73fc285585f128a99969daeef1b6a6b6aeb6ff 100644
--- a/civicrm/CRM/Activity/Selector/Search.php
+++ b/civicrm/CRM/Activity/Selector/Search.php
@@ -205,7 +205,7 @@ class CRM_Activity_Selector_Search extends CRM_Core_Selector_Base implements CRM
    *   The row number to start from.
    * @param int $rowCount
    *   The number of rows to return.
-   * @param string $sort
+   * @param string|CRM_Utils_Sort $sort
    *   The sql string that describes the sort order.
    * @param string $output
    *   What should the result set include (web/email/csv).
@@ -265,7 +265,7 @@ class CRM_Activity_Selector_Search extends CRM_Core_Selector_Base implements CRM
       $row['source_contact_name'] = implode(',', array_values($row['source_contact_name']));
       $row['source_contact_id'] = implode(',', $row['source_contact_id']);
 
-      if ($this->_context == 'search') {
+      if ($this->_context === 'search') {
         $row['checkbox'] = CRM_Core_Form::CB_PREFIX . $result->activity_id;
       }
       $row['contact_type'] = CRM_Contact_BAO_Contact_Utils::getImage($result->contact_sub_type ? $result->contact_sub_type : $result->contact_type, FALSE, $result->contact_id
@@ -291,10 +291,10 @@ class CRM_Activity_Selector_Search extends CRM_Core_Selector_Base implements CRM
         $accessMailingReport = TRUE;
       }
       $activityActions = new CRM_Activity_Selector_Activity($result->contact_id, NULL);
-      $actionLinks = $activityActions->actionLinks($activityTypeId,
-        CRM_Utils_Array::value('source_record_id', $row),
+      $actionLinks = $activityActions::actionLinks($activityTypeId,
+        $row['source_record_id'] ?? NULL,
         $accessMailingReport,
-        CRM_Utils_Array::value('activity_id', $row),
+        $row['activity_id'] ?? NULL,
         $this->_key,
         $this->_compContext
       );
@@ -303,6 +303,10 @@ class CRM_Activity_Selector_Search extends CRM_Core_Selector_Base implements CRM
           'id' => $result->activity_id,
           'cid' => $contactId,
           'cxt' => $this->_context,
+          // Parameter for hook locked in by CRM_Activity_Selector_SearchTest
+          // Any additional parameters added should follow apiv4 style
+          // and be added to the test.
+          'activity_type_id' => $row['activity_type_id'],
         ],
         ts('more'),
         FALSE,
diff --git a/civicrm/CRM/Admin/Form/OptionGroup.php b/civicrm/CRM/Admin/Form/OptionGroup.php
index 6de152bb9e83a7ad7bf513f3bcf790d8e2f0dfdf..fbf87dfb07cc9b95f472f1e2def8b03221c39ddd 100644
--- a/civicrm/CRM/Admin/Form/OptionGroup.php
+++ b/civicrm/CRM/Admin/Form/OptionGroup.php
@@ -120,7 +120,7 @@ class CRM_Admin_Form_OptionGroup extends CRM_Admin_Form {
     CRM_Utils_System::flushCache();
 
     if ($this->_action & CRM_Core_Action::DELETE) {
-      CRM_Core_BAO_OptionGroup::del($this->_id);
+      CRM_Core_BAO_OptionGroup::deleteRecord(['id' => $this->_id]);
       CRM_Core_Session::setStatus(ts('Selected option group has been deleted.'), ts('Record Deleted'), 'success');
     }
     else {
diff --git a/civicrm/CRM/Admin/Form/Options.php b/civicrm/CRM/Admin/Form/Options.php
index 5a36fc49ba5190fe926a2dcef153518604d2b6ed..d63a963897b8b77b3ffd83813651cd792fdd62a3 100644
--- a/civicrm/CRM/Admin/Form/Options.php
+++ b/civicrm/CRM/Admin/Form/Options.php
@@ -459,7 +459,7 @@ class CRM_Admin_Form_Options extends CRM_Admin_Form {
       $fieldValues = ['option_group_id' => $this->_gid];
       CRM_Utils_Weight::delWeight('CRM_Core_DAO_OptionValue', $this->_id, $fieldValues);
 
-      if (CRM_Core_BAO_OptionValue::del($this->_id)) {
+      if (CRM_Core_BAO_OptionValue::deleteRecord(['id' => $this->_id])) {
         if ($this->_gName == 'phone_type') {
           CRM_Core_BAO_Phone::setOptionToNull(CRM_Utils_Array::value('value', $this->_defaultValues));
         }
diff --git a/civicrm/CRM/Admin/Form/PaymentProcessor.php b/civicrm/CRM/Admin/Form/PaymentProcessor.php
index 2e6a3b99d11093884073dc6140cd8815cb032e1f..014727e76b03fee3636478b829f2ae325a915f04 100644
--- a/civicrm/CRM/Admin/Form/PaymentProcessor.php
+++ b/civicrm/CRM/Admin/Form/PaymentProcessor.php
@@ -217,8 +217,7 @@ class CRM_Admin_Form_PaymentProcessor extends CRM_Admin_Form {
       'payment_processor_type_id',
       ts('Payment Processor Type'),
       CRM_Financial_BAO_PaymentProcessor::buildOptions('payment_processor_type_id'),
-      TRUE,
-      ['onchange' => "reload(true)"]
+      TRUE
     );
 
     // Financial Account of account type asset CRM-11515
diff --git a/civicrm/CRM/Admin/Form/Preferences/Mailing.php b/civicrm/CRM/Admin/Form/Preferences/Mailing.php
index 726a31dd4e208c44ff6603e72e1fd65ee5e00400..23cc19a9b8cba73b48f717382b9f0c728f49328d 100644
--- a/civicrm/CRM/Admin/Form/Preferences/Mailing.php
+++ b/civicrm/CRM/Admin/Form/Preferences/Mailing.php
@@ -35,6 +35,7 @@ class CRM_Admin_Form_Preferences_Mailing extends CRM_Admin_Form_Preferences {
     'auto_recipient_rebuild' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
     'url_tracking_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
     'open_tracking_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+    'scheduled_reminder_smarty' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
   ];
 
   public function postProcess() {
diff --git a/civicrm/CRM/Admin/Form/ScheduleReminders.php b/civicrm/CRM/Admin/Form/ScheduleReminders.php
index 25a8b57baba6b1179214fe0af421559d248124bd..67e9bef5276661dc6ebf24ab0fd7b3b2ee038266 100644
--- a/civicrm/CRM/Admin/Form/ScheduleReminders.php
+++ b/civicrm/CRM/Admin/Form/ScheduleReminders.php
@@ -15,6 +15,8 @@
  * @copyright CiviCRM LLC https://civicrm.org/licensing
  */
 
+use Civi\Token\TokenProcessor;
+
 /**
  * This class generates form components for Scheduling Reminders.
  */
@@ -696,19 +698,13 @@ class CRM_Admin_Form_ScheduleReminders extends CRM_Admin_Form {
    *
    * @return array
    */
-  public function listTokens() {
-    $tokenProcessor = new \Civi\Token\TokenProcessor(\Civi::dispatcher(), [
-      'controller' => get_class(),
+  public function listTokens(): array {
+    $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
+      'controller' => __CLASS__,
       'smarty' => FALSE,
-      'schema' => ['activityId', 'participantId'],
+      'schema' => ['activityId', 'participantId', 'membershipId', 'contactId', 'eventId', 'contributionId'],
     ]);
-    $tokens = $tokenProcessor->listTokens();
-
-    $tokens = array_merge(CRM_Core_SelectValues::contactTokens(), $tokens);
-    $tokens = array_merge(CRM_Core_SelectValues::eventTokens(), $tokens);
-    $tokens = array_merge(CRM_Core_SelectValues::membershipTokens(), $tokens);
-    $tokens = array_merge(CRM_Core_SelectValues::contributionTokens(), $tokens);
-    return $tokens;
+    return $tokenProcessor->listTokens();
   }
 
 }
diff --git a/civicrm/CRM/Admin/Form/Setting/UF.php b/civicrm/CRM/Admin/Form/Setting/UF.php
index f6da3da6deb9f256527db3d4703ca14cbfa751d9..041a3f5ff9097b795fc7893463b91ed076f02997 100644
--- a/civicrm/CRM/Admin/Form/Setting/UF.php
+++ b/civicrm/CRM/Admin/Form/Setting/UF.php
@@ -34,62 +34,25 @@ class CRM_Admin_Form_Setting_UF extends CRM_Admin_Form_Setting {
 
     $this->assign('wpBasePageEnabled', FALSE);
     $this->assign('userFrameworkUsersTableNameEnabled', FALSE);
+    $this->assign('viewsIntegration', FALSE);
 
     $this->setTitle(
       ts('Settings - %1 Integration', [1 => $this->_uf])
     );
 
-    if ($this->_uf === 'WordPress') {
+    if ($config->userSystem->canSetBasePage()) {
       $this->_settings['wpBasePage'] = CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME;
       $this->assign('wpBasePageEnabled', TRUE);
     }
 
-    if ($config->userSystem->is_drupal) {
+    if ($config->userSystem->hasUsersTable()) {
       $this->_settings['userFrameworkUsersTableName'] = CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME;
       $this->assign('userFrameworkUsersTableNameEnabled', TRUE);
     }
 
-    // find out if drupal has its database prefixed
-    if ($this->_uf === 'Drupal8') {
-      $databases['default'] = Drupal\Core\Database\Database::getConnectionInfo('default');
-    }
-    else {
-      global $databases;
-    }
-
-    $drupal_prefix = '';
-    if (isset($databases['default']['default']['prefix'])) {
-      if (is_array($databases['default']['default']['prefix'])) {
-        $drupal_prefix = $databases['default']['default']['prefix']['default'];
-      }
-      else {
-        $drupal_prefix = $databases['default']['default']['prefix'];
-      }
-    }
-
-    $this->assign('tablePrefixes', FALSE);
-
-    if ($config->userSystem->viewsExists() &&
-      (
-        $config->dsn != $config->userFrameworkDSN || !empty($drupal_prefix)
-      )
-    ) {
-
-      $dsnArray = DB::parseDSN(CRM_Utils_SQL::autoSwitchDSN($config->dsn));
-      $tableNames = CRM_Core_DAO::getTableNames();
-      asort($tableNames);
-      $tablePrefixes = '$databases[\'default\'][\'default\'][\'prefix\']= [';
-      if ($config->userFramework === 'Backdrop') {
-        $tablePrefixes = '$database_prefix = [';
-      }
-      // add default prefix: the drupal database prefix
-      $tablePrefixes .= "\n  'default' => '$drupal_prefix',";
-      $prefix = $config->userSystem->getCRMDatabasePrefix();
-      foreach ($tableNames as $tableName) {
-        $tablePrefixes .= "\n  '" . str_pad($tableName . "'", 41) . " => '{$prefix}',";
-      }
-      $tablePrefixes .= "\n];";
-      $this->assign('tablePrefixes', $tablePrefixes);
+    $viewsIntegration = $config->userSystem->viewsIntegration();
+    if ($viewsIntegration) {
+      $this->assign('viewsIntegration', $viewsIntegration);
     }
 
     parent::buildQuickForm();
diff --git a/civicrm/CRM/Badge/BAO/Layout.php b/civicrm/CRM/Badge/BAO/Layout.php
index a662445166bfc70642375e51c406d7f8ef98dec1..dd34add77a4f53a18cc21196fc3ee618e751233b 100644
--- a/civicrm/CRM/Badge/BAO/Layout.php
+++ b/civicrm/CRM/Badge/BAO/Layout.php
@@ -92,6 +92,7 @@ class CRM_Badge_BAO_Layout extends CRM_Core_DAO_PrintLabel {
    * @deprecated
    */
   public static function del($printLabelId) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     self::deleteRecord(['id' => $printLabelId]);
   }
 
diff --git a/civicrm/CRM/Badge/Form/Layout.php b/civicrm/CRM/Badge/Form/Layout.php
index 90026c7fe3f13446c460cb7b69f97279a3a94f7f..661fb70ac3b586167959edfcf34629591519106c 100644
--- a/civicrm/CRM/Badge/Form/Layout.php
+++ b/civicrm/CRM/Badge/Form/Layout.php
@@ -164,7 +164,7 @@ class CRM_Badge_Form_Layout extends CRM_Admin_Form {
    */
   public function postProcess() {
     if ($this->_action & CRM_Core_Action::DELETE) {
-      CRM_Badge_BAO_Layout::del($this->_id);
+      CRM_Badge_BAO_Layout::deleteRecord(['id' => $this->_id]);
       CRM_Core_Session::setStatus(ts('Selected badge layout has been deleted.'), ts('Record Deleted'), 'success');
       return;
     }
diff --git a/civicrm/CRM/Batch/BAO/EntityBatch.php b/civicrm/CRM/Batch/BAO/EntityBatch.php
index 3f64e75b33afbdfab797c8db9ed18c6fb0ab4107..18fffa96fac6413c2b9314f969f75cc8002030c1 100644
--- a/civicrm/CRM/Batch/BAO/EntityBatch.php
+++ b/civicrm/CRM/Batch/BAO/EntityBatch.php
@@ -63,6 +63,7 @@ class CRM_Batch_BAO_EntityBatch extends CRM_Batch_DAO_EntityBatch {
    * @return CRM_Batch_DAO_EntityBatch
    */
   public static function del($params) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     if (!is_array($params)) {
       $params = ['id' => $params];
     }
diff --git a/civicrm/CRM/Campaign/Form/Survey/Main.php b/civicrm/CRM/Campaign/Form/Survey/Main.php
index 44b760763e68aaaf691d87fb3618ff8f0468b8cb..72fc38c7c1616ba9dfcf71a4077660a842097e2c 100644
--- a/civicrm/CRM/Campaign/Form/Survey/Main.php
+++ b/civicrm/CRM/Campaign/Form/Survey/Main.php
@@ -187,7 +187,7 @@ class CRM_Campaign_Form_Survey_Main extends CRM_Campaign_Form_Survey {
       );
       // delete option group if no any survey is using it.
       if (!$countSurvey) {
-        CRM_Core_BAO_OptionGroup::del($this->_values['result_id']);
+        CRM_Core_BAO_OptionGroup::deleteRecord(['id' => $this->_values['result_id']]);
       }
     }
 
diff --git a/civicrm/CRM/Campaign/Form/SurveyType.php b/civicrm/CRM/Campaign/Form/SurveyType.php
index 713830405922e321700bf7f7847dc93215080162..02e8836ef5aa795c5b7c8438b5e6a3e66f788bc0 100644
--- a/civicrm/CRM/Campaign/Form/SurveyType.php
+++ b/civicrm/CRM/Campaign/Form/SurveyType.php
@@ -122,7 +122,7 @@ class CRM_Campaign_Form_SurveyType extends CRM_Admin_Form {
       $fieldValues = ['option_group_id' => $this->_gid];
       $wt = CRM_Utils_Weight::delWeight('CRM_Core_DAO_OptionValue', $this->_id, $fieldValues);
 
-      if (CRM_Core_BAO_OptionValue::del($this->_id)) {
+      if (CRM_Core_BAO_OptionValue::deleteRecord(['id' => $this->_id])) {
         CRM_Core_Session::setStatus(ts('Selected Survey type has been deleted.'), ts('Record Deleted'), 'success');
       }
     }
diff --git a/civicrm/CRM/Campaign/Selector/Search.php b/civicrm/CRM/Campaign/Selector/Search.php
index 2c3cdd4856a79d2371413ab05d7483952545ef87..097178b9bae93f2fcf02355ac3fa4264b41450b8 100644
--- a/civicrm/CRM/Campaign/Selector/Search.php
+++ b/civicrm/CRM/Campaign/Selector/Search.php
@@ -249,7 +249,7 @@ class CRM_Campaign_Selector_Search extends CRM_Core_Selector_Base implements CRM
   /**
    * @param $sort
    */
-  public function buildPrevNextCache($sort) {
+  private function buildPrevNextCache($sort) {
     //for prev/next pagination
     $crmPID = CRM_Utils_Request::retrieve('crmPID', 'Integer');
 
diff --git a/civicrm/CRM/Case/BAO/Case.php b/civicrm/CRM/Case/BAO/Case.php
index ae7ba35fb0019af0ec9e60105b5cc33f433eacae..e255c72a38f747d9aa1c06521b19fbbd13787751 100644
--- a/civicrm/CRM/Case/BAO/Case.php
+++ b/civicrm/CRM/Case/BAO/Case.php
@@ -765,7 +765,7 @@ HERESQL;
     $caseTypes = array_flip($caseTypes);
 
     // get statuses as headers for the table
-    $url = CRM_Utils_System::url('civicrm/case/search', "reset=1&force=1&all=1&status=");
+    $url = CRM_Utils_System::url('civicrm/case/search', "reset=1&force=1&all=1&case_status_id=");
     foreach ($caseStatuses as $key => $name) {
       $caseSummary['headers'][$key]['status'] = $name;
       $caseSummary['headers'][$key]['url'] = $url . $key;
@@ -831,7 +831,7 @@ SELECT civicrm_case.id, case_status.label AS case_status, status_id, civicrm_cas
         $rows[$res->case_type][$res->case_status] = [
           'count' => 1,
           'url' => CRM_Utils_System::url('civicrm/case/search',
-            "reset=1&force=1&status={$res->status_id}&type={$res->case_type_id}&case_owner={$case_owner}"
+            "reset=1&force=1&case_status_id={$res->status_id}&case_type_id={$res->case_type_id}&case_owner={$case_owner}"
           ),
         ];
       }
diff --git a/civicrm/CRM/Case/BAO/CaseType.php b/civicrm/CRM/Case/BAO/CaseType.php
index c3f745786464071564ecf7b89c2d17507c312faa..8021fa1ea96f039a8695cae98f65844bc4761f9d 100644
--- a/civicrm/CRM/Case/BAO/CaseType.php
+++ b/civicrm/CRM/Case/BAO/CaseType.php
@@ -439,6 +439,7 @@ class CRM_Case_BAO_CaseType extends CRM_Case_DAO_CaseType implements \Civi\Core\
    * @return CRM_Case_DAO_CaseType
    */
   public static function del($caseTypeId) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return static::deleteRecord(['id' => $caseTypeId]);
   }
 
diff --git a/civicrm/CRM/Contact/BAO/Contact.php b/civicrm/CRM/Contact/BAO/Contact.php
index 1e8aa46a2332143d4695c98c374429eb5f1df639..c9c9dc964c852f1ff6d3487acdacc409300b2210 100644
--- a/civicrm/CRM/Contact/BAO/Contact.php
+++ b/civicrm/CRM/Contact/BAO/Contact.php
@@ -1147,36 +1147,6 @@ WHERE     civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer');
     }
   }
 
-  /**
-   * Function to set is_delete true or restore deleted contact.
-   *
-   * @param CRM_Contact_DAO_Contact $contact
-   *   Contact DAO object.
-   * @param bool $restore
-   *   True to set the is_delete = 1 else false to restore deleted contact,
-   *   i.e. is_delete = 0
-   *
-   * @deprecated
-   *
-   * @return bool
-   * @throws \CRM_Core_Exception
-   */
-  public static function contactTrashRestore($contact, $restore = FALSE) {
-    CRM_Core_Error::deprecatedFunctionWarning('Use the api');
-
-    if ($restore) {
-      CRM_Core_Error::deprecatedFunctionWarning('Use contact.create to restore - this does nothing much');
-      // @todo deprecate calling contactDelete with the intention to restore.
-      $updateParams = [
-        'id' => $contact->id,
-        'is_deleted' => FALSE,
-      ];
-      self::create($updateParams);
-      return TRUE;
-    }
-    return self::contactTrash($contact);
-  }
-
   /**
    * Get contact type for a contact.
    *
@@ -2579,10 +2549,7 @@ LEFT JOIN civicrm_email    ON ( civicrm_contact.id = civicrm_email.contact_id )
         $values['preferred_mail_format'] = $preferredMailingFormat[$contact->preferred_mail_format];
       }
 
-      // get preferred languages
-      if (!empty($contact->preferred_language)) {
-        $values['preferred_language'] = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', 'preferred_language', $contact->preferred_language);
-      }
+      $values['preferred_language'] = empty($contact->preferred_language) ? NULL : CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', 'preferred_language', $contact->preferred_language);
 
       // Calculating Year difference
       if ($contact->birth_date) {
diff --git a/civicrm/CRM/Contact/BAO/Contact/Location.php b/civicrm/CRM/Contact/BAO/Contact/Location.php
index 2e3a0fdb993e95d334895e0e1f94a90d2a13e0d1..13f533ba6629a54e6228a135a9b30b6e90105af4 100644
--- a/civicrm/CRM/Contact/BAO/Contact/Location.php
+++ b/civicrm/CRM/Contact/BAO/Contact/Location.php
@@ -59,44 +59,6 @@ class CRM_Contact_BAO_Contact_Location {
     return $returnParams;
   }
 
-  /**
-   * @deprecated Not used anywhere, use the Phone API instead
-   * Get the sms number and display name of a contact.
-   *
-   * @param int $id
-   *   Id of the contact.
-   * @param string|null $type
-   *
-   * @return array
-   *   tuple of display_name and sms if found, or (null,null)
-   */
-  public static function getPhoneDetails($id, $type = NULL) {
-    CRM_Core_Error::deprecatedFunctionWarning('Phone.get API instead');
-    if (!$id) {
-      return [NULL, NULL];
-    }
-
-    $cond = NULL;
-    if ($type) {
-      $cond = " AND civicrm_phone.phone_type_id = '$type'";
-    }
-
-    $sql = "
-   SELECT civicrm_contact.display_name, civicrm_phone.phone, civicrm_contact.do_not_sms
-     FROM civicrm_contact
-LEFT JOIN civicrm_phone ON ( civicrm_phone.contact_id = civicrm_contact.id )
-    WHERE civicrm_phone.is_primary = 1
-          $cond
-      AND civicrm_contact.id = %1";
-
-    $params = [1 => [$id, 'Integer']];
-    $dao = CRM_Core_DAO::executeQuery($sql, $params);
-    if ($dao->fetch()) {
-      return [$dao->display_name, $dao->phone, $dao->do_not_sms];
-    }
-    return [NULL, NULL, NULL];
-  }
-
   /**
    * Get the information to map a contact.
    *
diff --git a/civicrm/CRM/Contact/BAO/GroupContact.php b/civicrm/CRM/Contact/BAO/GroupContact.php
index b058686ec85d0cffcdb0a8a4102843c15ef1de10..22d8902d5463290eca7d254787e16250d8f07c11 100644
--- a/civicrm/CRM/Contact/BAO/GroupContact.php
+++ b/civicrm/CRM/Contact/BAO/GroupContact.php
@@ -33,6 +33,7 @@ class CRM_Contact_BAO_GroupContact extends CRM_Contact_DAO_GroupContact implemen
    * @deprecated
    */
   public static function add(array $params): CRM_Contact_DAO_GroupContact {
+    CRM_Core_Error::deprecatedFunctionWarning('writeRecord');
     return self::writeRecord($params);
   }
 
@@ -503,7 +504,7 @@ SELECT    *
     // As of Aug 2020 it's not called from anywhere so we can remove the below code after some time
 
     CRM_Core_Error::deprecatedFunctionWarning('Use the GroupContact API');
-    return self::add($params);
+    return self::writeRecord($params);
   }
 
   /**
diff --git a/civicrm/CRM/Contact/DAO/Relationship.php b/civicrm/CRM/Contact/DAO/Relationship.php
index 3a676d714c7e6f331e069b97b4dd48ca94558b63..cdacc83527f80ead5fed3a315edeec7894823851 100644
--- a/civicrm/CRM/Contact/DAO/Relationship.php
+++ b/civicrm/CRM/Contact/DAO/Relationship.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Contact/Relationship.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:53d602cd851fdc4afcfa9f2301e20baa)
+ * (GenCodeChecksum:ea043ab2b1943a9fe5e86ec6a68fedf4)
  */
 
 /**
@@ -406,6 +406,7 @@ class CRM_Contact_DAO_Relationship extends CRM_Core_DAO {
           'FKClassName' => 'CRM_Case_DAO_Case',
           'component' => 'CiviCase',
           'html' => [
+            'type' => 'EntityRef',
             'label' => ts("Case"),
           ],
           'add' => '2.2',
diff --git a/civicrm/CRM/Contact/DAO/SubscriptionHistory.php b/civicrm/CRM/Contact/DAO/SubscriptionHistory.php
index c1186708f32473fc9a43ccad41431f23205244fb..18492d6e1ea0ed91b06d119e5c39b7bbcb2b5403 100644
--- a/civicrm/CRM/Contact/DAO/SubscriptionHistory.php
+++ b/civicrm/CRM/Contact/DAO/SubscriptionHistory.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Contact/SubscriptionHistory.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:352df636d72c00072ade0a93fe8ce181)
+ * (GenCodeChecksum:30b144a6f844fb0fb38b1bdbeee72ecf)
  */
 
 /**
@@ -203,6 +203,11 @@ class CRM_Contact_DAO_SubscriptionHistory extends CRM_Core_DAO {
           'entity' => 'SubscriptionHistory',
           'bao' => 'CRM_Contact_BAO_SubscriptionHistory',
           'localizable' => 0,
+          'html' => [
+            'type' => 'Select Date',
+            'formatType' => 'activityDateTime',
+            'label' => ts("Group Membership Action Date"),
+          ],
           'add' => '1.1',
         ],
         'method' => [
diff --git a/civicrm/CRM/Contact/Form/DedupeRules.php b/civicrm/CRM/Contact/Form/DedupeRules.php
index 7c2040631f325538415ed5d8b9fb457a829aa1e5..1ac21b35103a55a9e7bdd7284967744c561356e3 100644
--- a/civicrm/CRM/Contact/Form/DedupeRules.php
+++ b/civicrm/CRM/Contact/Form/DedupeRules.php
@@ -27,7 +27,7 @@ class CRM_Contact_Form_DedupeRules extends CRM_Admin_Form {
   /**
    * Explicitly declare the entity api name.
    */
-  public function getDefaultEntity() {
+  public function getDefaultEntity(): string {
     return 'RuleGroup';
   }
 
@@ -192,14 +192,16 @@ class CRM_Contact_Form_DedupeRules extends CRM_Admin_Form {
 
   /**
    * Process the form submission.
+   *
+   * @throws \CRM_Core_Exception
    */
-  public function postProcess() {
+  public function postProcess(): void {
     $values = $this->exportValues();
 
     //FIXME: Handle logic to replace is_default column by usage
     // reset used column to General (since there can only
     // be one 'Supervised' or 'Unsupervised' rule)
-    if ($values['used'] != 'General') {
+    if ($values['used'] !== 'General') {
       $query = "
 UPDATE civicrm_dedupe_rule_group
    SET used = 'General'
@@ -231,11 +233,10 @@ UPDATE civicrm_dedupe_rule_group
     $ruleDao = new CRM_Dedupe_DAO_DedupeRule();
     $ruleDao->dedupe_rule_group_id = $rgDao->id;
     $ruleDao->delete();
-    $substrLenghts = [];
+    $substrLengths = [];
 
     $tables = [];
-    $daoObj = new CRM_Core_DAO();
-    $database = $daoObj->database();
+
     for ($count = 0; $count < self::RULES_COUNT; $count++) {
       if (empty($values["where_$count"])) {
         continue;
@@ -260,13 +261,13 @@ UPDATE civicrm_dedupe_rule_group
 
       // CRM-6245: we must pass table/field/length triples to the createIndexes() call below
       if ($length) {
-        if (!isset($substrLenghts[$table])) {
-          $substrLenghts[$table] = [];
+        if (!isset($substrLengths[$table])) {
+          $substrLengths[$table] = [];
         }
 
         //CRM-13417 to avoid fatal error "Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys, 1089"
         $schemaQuery = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS
-          WHERE TABLE_SCHEMA = '{$database}' AND
+          WHERE TABLE_SCHEMA = DATABASE() AND
           TABLE_NAME = '{$table}' AND COLUMN_NAME = '{$field}';";
         $dao = CRM_Core_DAO::executeQuery($schemaQuery);
 
@@ -287,14 +288,14 @@ UPDATE civicrm_dedupe_rule_group
             $length = $dao->CHARACTER_MAXIMUM_LENGTH;
           }
         }
-        $substrLenghts[$table][$field] = $length;
+        $substrLengths[$table][$field] = $length;
       }
     }
 
     // also create an index for this dedupe rule
     // CRM-3837
     CRM_Utils_Hook::dupeQuery($ruleDao, 'dedupeIndexes', $tables);
-    CRM_Core_BAO_SchemaHandler::createIndexes($tables, 'dedupe_index', $substrLenghts);
+    CRM_Core_BAO_SchemaHandler::createIndexes($tables, 'dedupe_index', $substrLengths);
 
     //need to clear cache of deduped contacts
     //based on the previous rule
diff --git a/civicrm/CRM/Contact/Form/Task/Label.php b/civicrm/CRM/Contact/Form/Task/Label.php
index e7a6d164d7802675bdb714fae7350bf5c0a8c983..ac91129e22f9367157f82bbfcebe93a4e8642aed 100644
--- a/civicrm/CRM/Contact/Form/Task/Label.php
+++ b/civicrm/CRM/Contact/Form/Task/Label.php
@@ -95,6 +95,9 @@ class CRM_Contact_Form_Task_Label extends CRM_Contact_Form_Task {
    * @param array|null $params
    */
   public function postProcess($params = NULL) {
+    if (!empty($params)) {
+      CRM_Core_Error::deprecatedWarning('params parameter is deprecated');
+    }
     $fv = $params ?: $this->controller->exportValues($this->_name);
     $locName = NULL;
 
diff --git a/civicrm/CRM/Contact/Import/Form/Preview.php b/civicrm/CRM/Contact/Import/Form/Preview.php
index c133b99c1cc2f4195fd896a33a341b38931f00a7..cfc55e5c03c4e557578553af8a22742271d9ac37 100644
--- a/civicrm/CRM/Contact/Import/Form/Preview.php
+++ b/civicrm/CRM/Contact/Import/Form/Preview.php
@@ -26,7 +26,7 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
   /**
    * Build the form object.
    */
-  public function buildQuickForm() {
+  public function buildQuickForm(): void {
     $this->addElement('text', 'newGroupName', ts('Name for new group'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Group', 'title'));
     $this->addElement('text', 'newGroupDesc', ts('Description of new group'));
     $groupTypes = CRM_Core_OptionGroup::values('group_type', TRUE);
@@ -38,11 +38,11 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
       );
     }
 
-    $groups = CRM_Core_PseudoConstant::nestedGroup();;
+    $groups = CRM_Core_PseudoConstant::nestedGroup();
 
     if (!empty($groups)) {
       $this->addElement('select', 'groups', ts('Add imported records to existing group(s)'), $groups, [
-        'multiple' => "multiple",
+        'multiple' => 'multiple',
         'class' => 'crm-select2',
       ]);
     }
@@ -70,48 +70,32 @@ class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
    * @param array $fields
    *   Posted values of the form.
    *
-   * @param $files
-   * @param self $self
-   *
    * @return array|bool
    *   list of errors to be posted back to the form
    */
-  public static function formRule($fields, $files, $self) {
+  public static function formRule(array $fields) {
     $errors = [];
-    $invalidTagName = $invalidGroupName = FALSE;
-
-    if (!empty($fields['newTagName'])) {
-      if (!CRM_Utils_Rule::objectExists(trim($fields['newTagName']),
-        ['CRM_Core_DAO_Tag']
-      )
-      ) {
-        $errors['newTagName'] = ts('Tag \'%1\' already exists.',
-          [1 => $fields['newTagName']]
-        );
-        $invalidTagName = TRUE;
-      }
+    if (!empty($fields['newTagName'])
+      && !CRM_Utils_Rule::objectExists(trim($fields['newTagName']), ['CRM_Core_DAO_Tag'])
+    ) {
+      $errors['newTagName'] = ts('Tag \'%1\' already exists.', [1 => $fields['newTagName']]);
     }
 
     if (!empty($fields['newGroupName'])) {
       $title = trim($fields['newGroupName']);
       $name = CRM_Utils_String::titleToVar($title);
       $query = 'SELECT COUNT(*) FROM civicrm_group WHERE name LIKE %1 OR title LIKE %2';
-      $grpCnt = CRM_Core_DAO::singleValueQuery(
+      if (CRM_Core_DAO::singleValueQuery(
         $query,
         [
           1 => [$name, 'String'],
           2 => [$title, 'String'],
         ]
-      );
-      if ($grpCnt) {
-        $invalidGroupName = TRUE;
+      )) {
         $errors['newGroupName'] = ts('Group \'%1\' already exists.', [1 => $fields['newGroupName']]);
       }
     }
 
-    $self->assign('invalidTagName', $invalidTagName);
-    $self->assign('invalidGroupName', $invalidGroupName);
-
     return empty($errors) ? TRUE : $errors;
   }
 
diff --git a/civicrm/CRM/Contact/Import/Parser/Contact.php b/civicrm/CRM/Contact/Import/Parser/Contact.php
index f1b7b92b0e960b5575588a400c7fe743276bf377..e86e99c39ad64ebfba1fc80cf30efba63cee7c5d 100644
--- a/civicrm/CRM/Contact/Import/Parser/Contact.php
+++ b/civicrm/CRM/Contact/Import/Parser/Contact.php
@@ -29,7 +29,7 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Import_Parser {
 
   use CRM_Contact_Import_MetadataTrait;
 
-  protected $_allExternalIdentifiers = [];
+  private $externalIdentifiers = [];
 
   /**
    * Array of successfully imported contact id's
@@ -38,13 +38,6 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Import_Parser {
    */
   protected $_newContacts = [];
 
-  /**
-   * Line count id.
-   *
-   * @var int
-   */
-  protected $_lineCount;
-
   protected $_tableName;
 
   /**
@@ -509,19 +502,7 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Import_Parser {
         }
       }
     }
-
-    //check for duplicate external Identifier
-    $externalID = $params['external_identifier'] ?? NULL;
-    if ($externalID) {
-      /* If it's a dupe,external Identifier  */
-
-      if ($externalDupe = CRM_Utils_Array::value($externalID, $this->_allExternalIdentifiers)) {
-        $errorMessage = ts('External ID conflicts with record %1', [1 => $externalDupe]);
-        throw new CRM_Core_Exception($errorMessage);
-      }
-      //otherwise, count it and move on
-      $this->_allExternalIdentifiers[$externalID] = $this->_lineCount;
-    }
+    $this->checkForDuplicateExternalIdentifiers($params['external_identifier'] ?? '');
 
     //date-format part ends
 
@@ -1734,4 +1715,21 @@ class CRM_Contact_Import_Parser_Contact extends CRM_Import_Parser {
     ][$outcome] ?? 'ERROR';
   }
 
+  /**
+   * Return an error if the csv has more than one row with the same external identifier.
+   *
+   * @param string $externalIdentifier
+   *
+   * @throws \CRM_Core_Exception
+   */
+  protected function checkForDuplicateExternalIdentifiers(string $externalIdentifier): void {
+    if ($externalIdentifier) {
+      $existingRow = array_search($externalIdentifier, $this->externalIdentifiers, TRUE);
+      if ($existingRow !== FALSE) {
+        throw new CRM_Core_Exception(ts('External ID conflicts with record %1', [1 => $existingRow + 1]));
+      }
+      $this->externalIdentifiers[] = $externalIdentifier;
+    }
+  }
+
 }
diff --git a/civicrm/CRM/Contact/Page/AJAX.php b/civicrm/CRM/Contact/Page/AJAX.php
index 793e998c60f0bc5de264cec64a4ccbbe5f0f8666..f09be9d63c1ddf6cada130a4da8acaaf5ac7002b 100644
--- a/civicrm/CRM/Contact/Page/AJAX.php
+++ b/civicrm/CRM/Contact/Page/AJAX.php
@@ -483,7 +483,7 @@ LIMIT {$offset}, {$rowCount}
   }
 
   public static function buildDedupeRules() {
-    $contactType = CRM_Utils_Request::retrieve('parentId', 'Positive');
+    $contactType = CRM_Utils_Request::retrieve('parentId', 'String');
     $dedupeRules = CRM_Dedupe_BAO_DedupeRuleGroup::getByType($contactType);
 
     CRM_Utils_JSON::output($dedupeRules);
diff --git a/civicrm/CRM/Contact/Page/Inline/Email.php b/civicrm/CRM/Contact/Page/Inline/Email.php
index 7ca97943c11b48c65e85c73f13ef80ff8fcf76ba..6e98a2cc46cea9da1afb3b1e50f98e2e09a615c7 100644
--- a/civicrm/CRM/Contact/Page/Inline/Email.php
+++ b/civicrm/CRM/Contact/Page/Inline/Email.php
@@ -27,7 +27,7 @@ class CRM_Contact_Page_Inline_Email extends CRM_Core_Page {
    */
   public function run() {
     // get the emails for this contact
-    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, TRUE);
+    $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', NULL, TRUE);
 
     $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', ['labelColumn' => 'display_name']);
 
diff --git a/civicrm/CRM/Contact/Selector.php b/civicrm/CRM/Contact/Selector.php
index cb85daa4f27ba774f6f6927cc8c59f0294c256ff..fa8d2da1f6cb571f286658b018132c86cc100b3c 100644
--- a/civicrm/CRM/Contact/Selector.php
+++ b/civicrm/CRM/Contact/Selector.php
@@ -864,7 +864,7 @@ class CRM_Contact_Selector extends CRM_Core_Selector_Base implements CRM_Core_Se
    *
    * @return string
    */
-  public function buildPrevNextCache($sort) {
+  private function buildPrevNextCache($sort) {
     $cacheKey = 'civicrm search ' . $this->_key;
 
     // We should clear the cache in following conditions:
@@ -1025,20 +1025,17 @@ class CRM_Contact_Selector extends CRM_Core_Selector_Base implements CRM_Core_Se
    * @param int $start
    * @param int $end
    *
+   * @todo - use test cover in CRM_Contact_Form_Search_BasicTest to
+   * to remove the extraneous logging that happens in the tested
+   * scenario (It does the catch & then write to the log - I was
+   * going to fix but got stalled on getting https://github.com/civicrm/civicrm-core/pull/25392
+   * merged - this comment won't conflict with that PR :-)
+   *
    * @throws \CRM_Core_Exception
    */
-  public function fillupPrevNextCache($sort, $cacheKey, $start = 0, $end = self::CACHE_SIZE) {
-    $coreSearch = TRUE;
-    // For custom searches, use the contactIDs method
-    if (is_a($this, 'CRM_Contact_Selector_Custom')) {
-      $sql = $this->_search->contactIDs($start, $end, $sort, TRUE);
-      $coreSearch = FALSE;
-    }
-    // For core searches use the searchQuery method
-    else {
-      $sql = $this->_query->getSearchSQL($start, $end, $sort, FALSE, $this->_query->_includeContactIds,
+  private function fillupPrevNextCache($sort, $cacheKey, $start = 0, $end = self::CACHE_SIZE) {
+    $sql = $this->_query->getSearchSQL($start, $end, $sort, FALSE, $this->_query->_includeContactIds,
         FALSE, TRUE);
-    }
 
     // CRM-9096
     // due to limitations in our search query writer, the above query does not work
@@ -1049,7 +1046,7 @@ class CRM_Contact_Selector extends CRM_Core_Selector_Base implements CRM_Core_Se
     // the other alternative of running the FULL query will just be incredibly inefficient
     // and slow things down way too much on large data sets / complex queries
 
-    $selectSQL = CRM_Core_DAO::composeQuery("SELECT DISTINCT %1, contact_a.id, contact_a.sort_name", [1 => [$cacheKey, 'String']]);
+    $selectSQL = CRM_Core_DAO::composeQuery('SELECT DISTINCT %1, contact_a.id, contact_a.sort_name', [1 => [$cacheKey, 'String']]);
 
     $sql = str_ireplace(['SELECT contact_a.id as contact_id', 'SELECT contact_a.id as id'], $selectSQL, $sql);
     $sql = str_ireplace('ORDER BY `contact_id`', 'ORDER BY `id`', $sql, $sql);
@@ -1058,19 +1055,9 @@ class CRM_Contact_Selector extends CRM_Core_Selector_Base implements CRM_Core_Se
       Civi::service('prevnext')->fillWithSql($cacheKey, $sql);
     }
     catch (\Exception $e) {
-      if ($coreSearch) {
-        // in the case of error, try rebuilding cache using full sql which is used for search selector display
-        // this fixes the bugs reported in CRM-13996 & CRM-14438
-        $this->rebuildPreNextCache($start, $end, $sort, $cacheKey);
-      }
-      else {
-        CRM_Core_Error::deprecatedFunctionWarning('Custom searches should return sql capable of filling the prevnext cache.');
-        // This will always show for CiviRules :-( as a) it orders by 'rule_label'
-        // which is not available in the query & b) it uses contact not contact_a
-        // as an alias.
-        // CRM_Core_Session::setStatus(ts('Query Failed'));
-        return;
-      }
+      // in the case of error, try rebuilding cache using full sql which is used for search selector display
+      // this fixes the bugs reported in CRM-13996 & CRM-14438
+      $this->rebuildPreNextCache($start, $end, $sort, $cacheKey);
     }
 
     if (Civi::service('prevnext') instanceof CRM_Core_PrevNextCache_Sql) {
@@ -1091,7 +1078,7 @@ class CRM_Contact_Selector extends CRM_Core_Selector_Base implements CRM_Core_Se
    * @param string $cacheKey
    *   Cache key.
    */
-  public function rebuildPreNextCache($start, $end, $sort, $cacheKey) {
+  private function rebuildPreNextCache($start, $end, $sort, $cacheKey): void {
     // generate full SQL
     $sql = $this->_query->searchQuery($start, $end, $sort, FALSE, $this->_query->_includeContactIds,
       FALSE, FALSE, TRUE);
diff --git a/civicrm/CRM/Contribute/BAO/Contribution.php b/civicrm/CRM/Contribute/BAO/Contribution.php
index ebc8c447f31d86911dd28bd89888f52ed72a1ddc..d1d7afe8774400c169a1858735b2eb8ff303b42b 100644
--- a/civicrm/CRM/Contribute/BAO/Contribution.php
+++ b/civicrm/CRM/Contribute/BAO/Contribution.php
@@ -11,6 +11,7 @@
 
 use Civi\Api4\Activity;
 use Civi\Api4\ActivityContact;
+use Civi\Api4\Address;
 use Civi\Api4\Contribution;
 use Civi\Api4\ContributionRecur;
 use Civi\Api4\LineItem;
@@ -1458,8 +1459,10 @@ GROUP BY p.id
    * @todo - this is a confusing function called from one place. It has a test. It would be
    * nice to deprecate it.
    *
+   * @deprecated
    */
   public static function getHonorContacts($honorId) {
+    CRM_Core_Error::deprecatedFunctionWarning('apiv4');
     $params = [];
     $honorDAO = new CRM_Contribute_DAO_ContributionSoft();
     $honorDAO->contact_id = $honorId;
@@ -1698,8 +1701,7 @@ WHERE      $condition
     $dao = CRM_Core_DAO::executeQuery($query);
 
     while ($dao->fetch()) {
-      $params = ['id' => $dao->id];
-      CRM_Core_BAO_Block::blockDelete('Address', $params);
+      Address::delete(FALSE)->addWhere('id', '=', $dao->id)->execute();
     }
   }
 
@@ -1849,6 +1851,7 @@ LEFT JOIN  civicrm_contribution contribution ON ( componentPayment.contribution_
     }
 
     $contribution->loadRelatedObjects($paymentProcessorID, $ids);
+    unset($ids);
 
     $memberships = $contribution->_relatedObjects['membership'] ?? [];
     $participant = $contribution->_relatedObjects['participant'] ?? [];
@@ -2676,11 +2679,6 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
         // This is a call we want to use less, in favour of loading related objects.
         $values = $this->addContributionPageValuesToValuesHeavyHandedly($values);
         if ($this->contribution_page_id) {
-          // This is precautionary as there are some legacy flows, but it should really be
-          // loaded by now.
-          if (!isset($this->_relatedObjects['contributionPage'])) {
-            $this->loadRelatedEntitiesByID(['contributionPage' => $this->contribution_page_id]);
-          }
           CRM_Contribute_BAO_Contribution_Utils::overrideDefaultCurrency($values);
         }
       }
@@ -4563,9 +4561,6 @@ LIMIT 1;";
     $entities = [
       'contact' => 'CRM_Contact_BAO_Contact',
       'contributionRecur' => 'CRM_Contribute_BAO_ContributionRecur',
-      'contributionType' => 'CRM_Financial_BAO_FinancialType',
-      'financialType' => 'CRM_Financial_BAO_FinancialType',
-      'contributionPage' => 'CRM_Contribute_BAO_ContributionPage',
     ];
     foreach ($entities as $entity => $bao) {
       if (!empty($ids[$entity])) {
diff --git a/civicrm/CRM/Contribute/BAO/ContributionPage.php b/civicrm/CRM/Contribute/BAO/ContributionPage.php
index 43c88cfbbcb90f7692a0e5b64af7e7c756cb2d42..9945d10286a0dee6d5f2bcb1668700aab2926609 100644
--- a/civicrm/CRM/Contribute/BAO/ContributionPage.php
+++ b/civicrm/CRM/Contribute/BAO/ContributionPage.php
@@ -946,8 +946,10 @@ LEFT JOIN  civicrm_premiums            ON ( civicrm_premiums.entity_id = civicrm
    *
    * @return bool
    *   isSeparateMembershipPayment
+   * @deprecated
    */
   public static function getIsMembershipPayment($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('api');
     $membershipBlocks = civicrm_api3('membership_block', 'get', [
       'entity_table' => 'civicrm_contribution_page',
       'entity_id' => $id,
diff --git a/civicrm/CRM/Contribute/BAO/ContributionRecur.php b/civicrm/CRM/Contribute/BAO/ContributionRecur.php
index 36f1c5b368292393af7cb91cc7be6cc730459c0f..732ce892f5918d767cb5e8eaecc356939a04ef9b 100644
--- a/civicrm/CRM/Contribute/BAO/ContributionRecur.php
+++ b/civicrm/CRM/Contribute/BAO/ContributionRecur.php
@@ -230,6 +230,8 @@ class CRM_Contribute_BAO_ContributionRecur extends CRM_Contribute_DAO_Contributi
    * @param array $ids
    *   (reference ) an array of recurring contribution ids.
    *
+   * @deprecated use the api.
+   *
    * @return array
    *   an array of recurring ids count
    */
diff --git a/civicrm/CRM/Contribute/Form/AbstractEditPayment.php b/civicrm/CRM/Contribute/Form/AbstractEditPayment.php
index 59c30d96ac0d821672e468411a52899e23da79d8..3c687182d13e1cde784c187ef395fc4f821b4135 100644
--- a/civicrm/CRM/Contribute/Form/AbstractEditPayment.php
+++ b/civicrm/CRM/Contribute/Form/AbstractEditPayment.php
@@ -86,18 +86,6 @@ class CRM_Contribute_Form_AbstractEditPayment extends CRM_Contact_Form_Task {
    */
   protected $entity;
 
-  /**
-   * The id of the premium that we are proceessing.
-   *
-   * @var int
-   */
-  public $_premiumID = NULL;
-
-  /**
-   * @var CRM_Contribute_DAO_ContributionProduct
-   */
-  public $_productDAO = NULL;
-
   /**
    * The id of the note
    *
@@ -351,23 +339,6 @@ class CRM_Contribute_Form_AbstractEditPayment extends CRM_Contact_Form_Task {
     CRM_Custom_Form_CustomData::setDefaultValues($this);
   }
 
-  /**
-   * @param int $id
-   * @todo - this function is a long way, non standard of saying $dao = new CRM_Contribute_DAO_ContributionProduct(); $dao->id = $id; $dao->find();
-   */
-  public function assignPremiumProduct($id) {
-    $sql = "
-SELECT *
-FROM   civicrm_contribution_product
-WHERE  contribution_id = {$id}
-";
-    $dao = CRM_Core_DAO::executeQuery($sql);
-    if ($dao->fetch()) {
-      $this->_premiumID = $dao->id;
-      $this->_productDAO = $dao;
-    }
-  }
-
   /**
    * @return array
    *   Array of valid processors. The array resembles the DB table but also has 'object' as a key
diff --git a/civicrm/CRM/Contribute/Form/Contribution.php b/civicrm/CRM/Contribute/Form/Contribution.php
index 36df356cebffe73cd6e275ed28424243c58cea9b..e1fe1e7aeff3ab6a1d8511e8807adb1474826730 100644
--- a/civicrm/CRM/Contribute/Form/Contribution.php
+++ b/civicrm/CRM/Contribute/Form/Contribution.php
@@ -2134,4 +2134,21 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
     return $this->getSubmittedValue('price_set_id');
   }
 
+  /**
+   * @param int $id
+   * @todo - this function is a long way, non standard of saying $dao = new CRM_Contribute_DAO_ContributionProduct(); $dao->id = $id; $dao->find();
+   */
+  private function assignPremiumProduct($id): void {
+    $sql = "
+SELECT *
+FROM   civicrm_contribution_product
+WHERE  contribution_id = {$id}
+";
+    $dao = CRM_Core_DAO::executeQuery($sql);
+    if ($dao->fetch()) {
+      $this->_premiumID = $dao->id;
+      $this->_productDAO = $dao;
+    }
+  }
+
 }
diff --git a/civicrm/CRM/Contribute/Form/Contribution/Confirm.php b/civicrm/CRM/Contribute/Form/Contribution/Confirm.php
index 891ebcbc5bc0d77d118324b919c20d70e72379fb..e93b6ae706bbc0eb5b88e356031065084f3a962a 100644
--- a/civicrm/CRM/Contribute/Form/Contribution/Confirm.php
+++ b/civicrm/CRM/Contribute/Form/Contribution/Confirm.php
@@ -568,8 +568,8 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       $this->buildCustom($this->_values['onbehalf_profile_id'], 'onbehalfProfile', TRUE, 'onbehalf', $fieldTypes);
     }
 
-    $this->_separateMembershipPayment = $this->get('separateMembershipPayment');
-    $this->assign('is_separate_payment', $this->_separateMembershipPayment);
+    $this->_separateMembershipPayment = $this->isSeparateMembershipPayment();
+    $this->assign('is_separate_payment', $this->isSeparateMembershipPayment());
 
     $this->assign('priceSetID', $this->_priceSetId);
 
@@ -1446,7 +1446,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       //enabled and contribution amount is not selected. fix for CRM-3010
       $isPaidMembership = TRUE;
     }
-    $isProcessSeparateMembershipTransaction = $this->isSeparateMembershipTransaction($this->_id);
+    $isProcessSeparateMembershipTransaction = $this->isSeparateMembershipTransaction();
 
     if ($this->isFormSupportsNonMembershipContributions()) {
       $financialTypeID = $this->_values['financial_type_id'];
@@ -1728,8 +1728,8 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
                 'is_transactional' => FALSE,
                 'fee_amount' => $result['result']['fee_amount'] ?? NULL,
                 'receive_date' => $result['result']['receive_date'] ?? NULL,
-                'card_type_id' => $result['result']['card_type_id'] ?? NULL,
-                'pan_truncation' => $result['result']['pan_truncation'] ?? NULL,
+                'card_type_id' => $paymentParams['card_type_id'] ?? NULL,
+                'pan_truncation' => $paymentParams['pan_truncation'] ?? NULL,
               ]);
             }
             catch (CRM_Core_Exception $e) {
@@ -1911,16 +1911,14 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
    * contribution
    * transaction AND a membership transaction AND the payment processor supports double financial transactions (ie. NOT doTransferCheckout style)
    *
-   * @param int $formID
+   * @todo - this is confusing - does isSeparateMembershipPayment need to
+   * check both conditions, making this redundant, or are there 2 legit
+   * variations here?
    *
    * @return bool
    */
-  protected function isSeparateMembershipTransaction($formID): bool {
-    $memBlockDetails = CRM_Member_BAO_Membership::getMembershipBlock($formID);
-    if (!empty($memBlockDetails['is_separate_payment']) && $this->isFormSupportsNonMembershipContributions()) {
-      return TRUE;
-    }
-    return FALSE;
+  protected function isSeparateMembershipTransaction(): bool {
+    return $this->isSeparateMembershipPayment() && $this->isFormSupportsNonMembershipContributions();
   }
 
   /**
@@ -2004,7 +2002,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $form->_id = $params['id'];
 
     CRM_Contribute_BAO_ContributionPage::setValues($form->_id, $form->_values);
-    $form->_separateMembershipPayment = CRM_Contribute_BAO_ContributionPage::getIsMembershipPayment($form->_id);
+    $form->_separateMembershipPayment = $form->isSeparateMembershipPayment();
     //this way the mocked up controller ignores the session stuff
     $_SERVER['REQUEST_METHOD'] = 'GET';
     $form->controller = new CRM_Contribute_Controller_Contribution();
@@ -2015,12 +2013,12 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $order = new CRM_Financial_BAO_Order();
     $order->setPriceSetIDByContributionPageID($params['id']);
     $order->setPriceSelectionFromUnfilteredInput($params);
-    if (isset($params['amount']) && !CRM_Contribute_BAO_ContributionPage::getIsMembershipPayment($form->_id)) {
+    if (isset($params['amount']) && !$form->isSeparateMembershipPayment()) {
       // @todo deprecate receiving amount, calculate on the form.
       $order->setOverrideTotalAmount((float) $params['amount']);
     }
     $amount = $order->getTotalAmount();
-    if ($form->_separateMembershipPayment) {
+    if ($form->isSeparateMembershipPayment()) {
       $amount -= $order->getMembershipTotalAmount();
     }
     $form->_amount = $params['amount'] = $form->_params['amount'] = $amount;
@@ -2409,8 +2407,8 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
               'is_transactional' => FALSE,
               'fee_amount' => $result['fee_amount'] ?? NULL,
               'receive_date' => $result['receive_date'] ?? NULL,
-              'card_type_id' => $result['card_type_id'] ?? NULL,
-              'pan_truncation' => $result['pan_truncation'] ?? NULL,
+              'card_type_id' => $paymentParams['card_type_id'] ?? NULL,
+              'pan_truncation' => $paymentParams['pan_truncation'] ?? NULL,
             ]);
           }
           catch (CRM_Core_Exception $e) {
@@ -2675,20 +2673,17 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $isRecur
   ): array {
     $form = $this;
-    CRM_Core_Payment_Form::mapParams($form->_bltID, $form->_params, $paymentParams, TRUE);
-    $isPaymentTransaction = self::isPaymentTransaction($form);
+    CRM_Core_Payment_Form::mapParams($this->_bltID, $form->_params, $paymentParams, TRUE);
+    $isPaymentTransaction = self::isPaymentTransaction($this);
 
     $financialType = new CRM_Financial_DAO_FinancialType();
     $financialType->id = $financialTypeID;
     $financialType->find(TRUE);
-    if ($financialType->is_deductible) {
-      $form->assign('is_deductible', TRUE);
-      $form->set('is_deductible', TRUE);
-    }
+    $this->assign('is_deductible', $financialType->is_deductible);
 
     // add some financial type details to the params list
     // if folks need to use it
-    $paymentParams['financial_type_id'] = $paymentParams['financialTypeID'] = $financialType->id;
+    $paymentParams['financial_type_id'] = $paymentParams['financialTypeID'] = $financialTypeID;
     //CRM-15297 - contributionType is obsolete - pass financial type as well so people can deprecate it
     $paymentParams['financialType_name'] = $paymentParams['contributionType_name'] = $form->_params['contributionType_name'] = $financialType->name;
     //CRM-11456
diff --git a/civicrm/CRM/Contribute/Form/Contribution/ThankYou.php b/civicrm/CRM/Contribute/Form/Contribution/ThankYou.php
index 8887d38f7f56affe1e323785715d529c527ae2ab..c5a510f6210adbe9feeb600482d58e9ec96ae062 100644
--- a/civicrm/CRM/Contribute/Form/Contribution/ThankYou.php
+++ b/civicrm/CRM/Contribute/Form/Contribution/ThankYou.php
@@ -41,8 +41,6 @@ class CRM_Contribute_Form_Contribution_ThankYou extends CRM_Contribute_Form_Cont
     $this->_params = $this->get('params');
     $this->_lineItem = $this->get('lineItem');
     $this->_useForMember = $this->get('useForMember');
-    $is_deductible = $this->get('is_deductible');
-    $this->assign('is_deductible', $is_deductible);
     $this->assign('thankyou_title', CRM_Utils_Array::value('thankyou_title', $this->_values));
     $this->assign('thankyou_text', CRM_Utils_Array::value('thankyou_text', $this->_values));
     $this->assign('thankyou_footer', CRM_Utils_Array::value('thankyou_footer', $this->_values));
diff --git a/civicrm/CRM/Contribute/Form/ContributionBase.php b/civicrm/CRM/Contribute/Form/ContributionBase.php
index 024e262c0b4a6fbe3d2dc8834d89d40b8712a85c..6b3b3b99f49ab72774ac3af128cdfc58cd612b46 100644
--- a/civicrm/CRM/Contribute/Form/ContributionBase.php
+++ b/civicrm/CRM/Contribute/Form/ContributionBase.php
@@ -68,7 +68,10 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
 
   /**
    * Does this form support a separate membership payment
+   *
    * @var bool
+   *
+   * @deprecated use $this->isSeparateMembershipPayment() function.
    */
   protected $_separateMembershipPayment;
 
@@ -1259,4 +1262,13 @@ class CRM_Contribute_Form_ContributionBase extends CRM_Core_Form {
     return $this->_membershipBlock;
   }
 
+  /**
+   * Is the contribution page configured for 2 payments, one being membership & one not.
+   *
+   * @return bool
+   */
+  protected function isSeparateMembershipPayment(): bool {
+    return $this->getMembershipBlock() && $this->getMembershipBlock()['is_separate_payment'];
+  }
+
 }
diff --git a/civicrm/CRM/Contribute/Form/Search.php b/civicrm/CRM/Contribute/Form/Search.php
index 90f7c9787aa53653cee8bd0f1e1230172b661738..c3f65c0c13add1c82e0b35e6e7325fc7ea304657 100644
--- a/civicrm/CRM/Contribute/Form/Search.php
+++ b/civicrm/CRM/Contribute/Form/Search.php
@@ -75,6 +75,8 @@ class CRM_Contribute_Form_Search extends CRM_Core_Form_Search {
     $this->_done = FALSE;
 
     parent::preProcess();
+    // For contributionTotals.tpl
+    $this->addExpectedSmartyVariables(['annual']);
 
     $this->_queryParams = CRM_Contact_BAO_Query::convertFormValues($this->_formValues);
     $selector = new CRM_Contribute_Selector_Search($this->_queryParams,
diff --git a/civicrm/CRM/Contribute/Form/Task/PDF.php b/civicrm/CRM/Contribute/Form/Task/PDF.php
index f2a812cee72cafcd038c7171fa1f1ba2d29317cd..df39acc994b3e45262cb2ad43ff42c4c47159548 100644
--- a/civicrm/CRM/Contribute/Form/Task/PDF.php
+++ b/civicrm/CRM/Contribute/Form/Task/PDF.php
@@ -244,14 +244,13 @@ AND    {$this->_componentClause}";
     $pdfElements = [];
     $pdfElements['details'] = self::getDetails(implode(',', $contribIds));
     $excludeContactIds = [];
+    $suppressedEmails = 0;
     if (!$isCreatePDF) {
       $contactDetails = civicrm_api3('Contact', 'get', [
         'return' => ['email', 'do_not_email', 'is_deceased', 'on_hold'],
         'id' => ['IN' => $contactIds],
         'options' => ['limit' => 0],
       ])['values'];
-      $pdfElements['suppressedEmails'] = 0;
-      $suppressedEmails = 0;
       foreach ($contactDetails as $id => $values) {
         if (empty($values['email']) ||
           (empty($params['override_privacy']) && !empty($values['do_not_email']))
@@ -259,11 +258,11 @@ AND    {$this->_componentClause}";
           || !empty($values['on_hold'])
         ) {
           $suppressedEmails++;
-          $pdfElements['suppressedEmails'] = $suppressedEmails;
           $excludeContactIds[] = $values['contact_id'];
         }
       }
     }
+    $pdfElements['suppressedEmails'] = $suppressedEmails;
     $pdfElements['excludeContactIds'] = $excludeContactIds;
 
     return $pdfElements;
diff --git a/civicrm/CRM/Contribute/Import/Form/DataSource.php b/civicrm/CRM/Contribute/Import/Form/DataSource.php
index c161cc6529150dc8bb64d04342b9c48287024901..41d9e8e80e8a63650955d300157a043fd7362538 100644
--- a/civicrm/CRM/Contribute/Import/Form/DataSource.php
+++ b/civicrm/CRM/Contribute/Import/Form/DataSource.php
@@ -20,10 +20,6 @@
  */
 class CRM_Contribute_Import_Form_DataSource extends CRM_Import_Form_DataSource {
 
-  const PATH = 'civicrm/contribute/import';
-
-  const IMPORT_ENTITY = 'Contribution';
-
   /**
    * Get the name of the type to be stored in civicrm_user_job.type_id.
    *
diff --git a/civicrm/CRM/Contribute/Import/Parser/Contribution.php b/civicrm/CRM/Contribute/Import/Parser/Contribution.php
index cd81fe127c0462224ae05a27f2b37103b2196b3c..0a4705ff79933d8d14591c82489f6712d0a87628 100644
--- a/civicrm/CRM/Contribute/Import/Parser/Contribution.php
+++ b/civicrm/CRM/Contribute/Import/Parser/Contribution.php
@@ -68,12 +68,6 @@ class CRM_Contribute_Import_Parser_Contribution extends CRM_Import_Parser {
    */
   protected $_separator;
 
-  /**
-   * Total number of lines in file
-   * @var int
-   */
-  protected $_lineCount;
-
   /**
    * Running total number of valid soft credit rows
    * @var int
@@ -238,7 +232,19 @@ class CRM_Contribute_Import_Parser_Contribution extends CRM_Import_Parser {
         $params[$entity][$this->getFieldMetadata($mappedField['name'])['name']] = $this->getTransformedFieldValue($mappedField['name'], $fieldValue);
       }
     }
-    return $params;
+    return $this->removeEmptyValues($params);
+  }
+
+  protected function removeEmptyValues($array) {
+    foreach ($array as $key => $value) {
+      if (is_array($value)) {
+        $array[$key] = $this->removeEmptyValues($value);
+      }
+      elseif ($value === '') {
+        unset($array[$key]);
+      }
+    }
+    return $array;
   }
 
   /**
@@ -465,7 +471,7 @@ class CRM_Contribute_Import_Parser_Contribution extends CRM_Import_Parser {
 
       if (!empty($softCreditParams)) {
         if (empty($contributionParams['total_amount']) || empty($contributionParams['currency'])) {
-          $contributionParams = Contribution::get()->addSelect('total_amount', 'currency')->addWhere('id', '=', $contributionID)->execute()->first();
+          $contributionParams = array_merge($contributionParams, Contribution::get()->addSelect('total_amount', 'currency')->addWhere('id', '=', $contributionID)->execute()->first());
         }
         foreach ($softCreditParams as $softCreditParam) {
           $softCreditParam['contribution_id'] = $contributionID;
diff --git a/civicrm/CRM/Contribute/Page/UserDashboard.php b/civicrm/CRM/Contribute/Page/UserDashboard.php
index 0a4c9f0fe1ced243b317bf9644608a68b5fc01b9..96b94a18d73b11e12253d577eada813b9ae0bb48 100644
--- a/civicrm/CRM/Contribute/Page/UserDashboard.php
+++ b/civicrm/CRM/Contribute/Page/UserDashboard.php
@@ -9,6 +9,9 @@
  +--------------------------------------------------------------------+
  */
 
+use Civi\Api4\Contribution;
+use Civi\Api4\ContributionRecur;
+
 /**
  *
  * @package CRM
@@ -18,112 +21,84 @@ class CRM_Contribute_Page_UserDashboard extends CRM_Contact_Page_View_UserDashBo
 
   /**
    * called when action is browse.
+   *
+   * @throws \CRM_Core_Exception
    */
-  public function listContribution() {
-    $rows = civicrm_api3('Contribution', 'get', [
-      'options' => [
-        'limit' => 12,
-        'sort' => 'receive_date DESC',
-      ],
-      'sequential' => 1,
-      'contact_id' => $this->_contactId,
-      'return' => [
-        'total_amount',
-        'contribution_recur_id',
-        'financial_type',
-        'receive_date',
-        'receipt_date',
-        'contribution_status',
-        'currency',
-        'amount_level',
-        'contact_id,',
-        'contribution_source',
-      ],
-    ])['values'];
-
-    // We want oldest first, just among the most recent contributions
-    $rows = array_reverse($rows);
+  public function listContribution(): void {
+    $contributions = $this->getContributions();
 
-    foreach ($rows as $index => &$row) {
+    foreach ($contributions as &$row) {
       // This is required for tpl logic. We should move away from hard-code this to adding an array of actions to the row
       // which the tpl can iterate through - this should allow us to cope with competing attempts to add new buttons
       // and allow extensions to assign new ones through the pageRun hook
-      $row['balance_amount'] = CRM_Contribute_BAO_Contribution::getContributionBalance($row['contribution_id']);
-      $contributionStatus = CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $row['contribution_status_id']);
-
-      if (in_array($contributionStatus, ['Pending', 'Partially paid'])) {
+      // We could check for balance_amount > 0 here? It feels more correct but this seems to be working.
+      if (in_array($row['contribution_status_id:name'], ['Pending', 'Partially paid'], TRUE)) {
         $row['buttons']['pay'] = [
           'class' => 'button',
           'label' => ts('Pay Now'),
           'url' => CRM_Utils_System::url('civicrm/contribute/transact', [
             'reset' => 1,
             'id' => Civi::settings()->get('default_invoice_page'),
-            'ccid' => $row['contribution_id'],
+            'ccid' => $row['id'],
             'cs' => $this->getUserChecksum(),
             'cid' => $row['contact_id'],
           ]),
         ];
       }
     }
+    unset($row);
 
-    $this->assign('contribute_rows', $rows);
+    $this->assign('contribute_rows', $contributions);
     $this->assign('contributionSummary', ['total_amount' => civicrm_api3('Contribution', 'getcount', ['contact_id' => $this->_contactId])]);
 
     //add honor block
-    $params = CRM_Contribute_BAO_Contribution::getHonorContacts($this->_contactId);
-
-    if (!empty($params)) {
-      // assign vars to templates
-      $this->assign('honorRows', $params);
-      $this->assign('honor', TRUE);
-    }
+    $softCreditContributions = $this->getContributions(TRUE);
+    $this->assign('soft_credit_contributions', $softCreditContributions);
 
-    $recur = new CRM_Contribute_DAO_ContributionRecur();
-    $recur->contact_id = $this->_contactId;
-    $recur->is_test = 0;
-    $recur->find();
-
-    $recurStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'label');
+    $recurringContributions = (array) ContributionRecur::get(FALSE)
+      ->addWhere('contact_id', '=', $this->_contactId)
+      ->addWhere('is_test', '=', 0)
+      ->setSelect([
+        '*',
+        'contribution_status_id:label',
+      ])->execute();
 
     $recurRow = [];
     $recurIDs = [];
-    while ($recur->fetch()) {
-      if (empty($recur->payment_processor_id)) {
+    foreach ($recurringContributions as $recur) {
+      if (empty($recur['payment_processor_id'])) {
         // it's not clear why we continue here as any without a processor id would likely
         // be imported from another system & still seem valid.
         continue;
       }
 
-      require_once 'api/v3/utils.php';
-      //@todo calling api functions directly is not supported
-      _civicrm_api3_object_to_array($recur, $values);
-
-      $values['recur_status'] = $recurStatus[$values['contribution_status_id']];
-      $recurRow[$values['id']] = $values;
+      // Cast to something Smarty-friendly.
+      $recur['recur_status'] = $recur['contribution_status_id:label'];
+      $recurRow[$recur['id']] = $recur;
 
-      $action = array_sum(array_keys(CRM_Contribute_Page_Tab::dashboardRecurLinks((int) $recur->id, (int) $recur->contact_id)));
+      $action = array_sum(array_keys(CRM_Contribute_Page_Tab::dashboardRecurLinks((int) $recur['id'], (int) $recur['contact_id'])));
 
-      $details = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($recur->id, 'recur');
+      $details = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($recur['id'], 'recur');
       $hideUpdate = $details->membership_id & $details->auto_renew;
 
       if ($hideUpdate) {
         $action -= CRM_Core_Action::UPDATE;
       }
 
-      $recurRow[$values['id']]['action'] = CRM_Core_Action::formLink(CRM_Contribute_Page_Tab::dashboardRecurLinks((int) $recur->id, (int) $this->_contactId),
+      $recurRow[$recur['id']]['action'] = CRM_Core_Action::formLink(CRM_Contribute_Page_Tab::dashboardRecurLinks((int) $recur['id'], (int) $this->_contactId),
         $action, [
           'cid' => $this->_contactId,
-          'crid' => $values['id'],
+          'crid' => $recur['id'],
           'cxt' => 'contribution',
         ],
         ts('more'),
         FALSE,
         'contribution.dashboard.recurring',
         'Contribution',
-        $values['id']
+        $recur['id']
       );
 
-      $recurIDs[] = $values['id'];
+      $recurIDs[] = $recur['id'];
     }
     if (is_array($recurIDs) && !empty($recurIDs)) {
       $getCount = CRM_Contribute_BAO_ContributionRecur::getCount($recurIDs);
@@ -136,12 +111,7 @@ class CRM_Contribute_Page_UserDashboard extends CRM_Contact_Page_View_UserDashBo
     }
 
     $this->assign('recurRows', $recurRow);
-    if (!empty($recurRow)) {
-      $this->assign('recur', TRUE);
-    }
-    else {
-      $this->assign('recur', FALSE);
-    }
+
   }
 
   /**
@@ -163,6 +133,8 @@ class CRM_Contribute_Page_UserDashboard extends CRM_Contact_Page_View_UserDashBo
   /**
    * the main function that is called when the page
    * loads, it decides the which action has to be taken for the page.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function run() {
     $this->assign('isIncludeInvoiceLinks', $this->isIncludeInvoiceLinks());
@@ -170,4 +142,59 @@ class CRM_Contribute_Page_UserDashboard extends CRM_Contact_Page_View_UserDashBo
     $this->listContribution();
   }
 
+  /**
+   * Get the contact's contributions.
+   *
+   * @param bool $isSoftCredit
+   *   Return contributions for which the contact is the soft credit contact instead.
+   *
+   * @return array
+   * @throws \CRM_Core_Exception
+   */
+  protected function getContributions(bool $isSoftCredit = FALSE): array {
+    $apiQuery = Contribution::get(FALSE)
+      ->addOrderBy('receive_date', 'DESC')
+      ->setLimit(12)
+      ->setSelect([
+        'total_amount',
+        'contribution_recur_id',
+        'receive_date',
+        'receipt_date',
+        'cancel_date',
+        'amount_level',
+        'contact_id',
+        'contact_id.display_name',
+        'contribution_status_id:name',
+        'contribution_status_id:label',
+        'financial_type_id:label',
+        'currency',
+        'amount_level',
+        'contact_id,',
+        'source',
+        'balance_amount',
+        'id',
+      ]);
+
+    if ($isSoftCredit) {
+      $apiQuery->addJoin('ContributionSoft AS contribution_soft', 'INNER');
+      $apiQuery->addWhere('contribution_soft.contact_id', '=', $this->_contactId);
+      $apiQuery->addSelect('contribution_soft.soft_credit_type_id:label');
+    }
+    else {
+      $apiQuery->addWhere('contact_id', '=', $this->_contactId);
+    }
+    $contributions = (array) $apiQuery->execute();
+    foreach ($contributions as $index => $contribution) {
+      // QuickForm can't cope with the colons & dots ... cast to a legacy or simplified key.
+      $contributions[$index]['financial_type'] = $contribution['financial_type_id:label'];
+      $contributions[$index]['contribution_status'] = $contribution['contribution_status_id:label'];
+      $contributions[$index]['contribution_status_name'] = $contribution['contribution_status_id:name'];
+      $contributions[$index]['display_name'] = $contribution['contact_id.display_name'];
+      $contributions[$index]['soft_credit_type'] = $contribution['contribution_soft.soft_credit_type_id:label'] ?? NULL;
+      // Add in the api-v3 style naming just in case any extensions are still looking for it.
+      $contributions[$index]['contribution_id'] = $contribution['id'];
+    }
+    return $contributions;
+  }
+
 }
diff --git a/civicrm/CRM/Core/BAO/ActionSchedule.php b/civicrm/CRM/Core/BAO/ActionSchedule.php
index 04c08c59a08bee02549332103bf2967cba50a47c..8307501bb60d56d50d0fb6c7e92884b0b6bbb2de 100644
--- a/civicrm/CRM/Core/BAO/ActionSchedule.php
+++ b/civicrm/CRM/Core/BAO/ActionSchedule.php
@@ -631,7 +631,7 @@ FROM civicrm_action_schedule cas
       'controller' => __CLASS__,
       'actionSchedule' => $schedule,
       'actionMapping' => $mapping,
-      'smarty' => TRUE,
+      'smarty' => Civi::settings()->get('scheduled_reminder_smarty'),
       'schema' => ['contactId'],
     ]);
     $tp->addMessage('body_text', $schedule->body_text, 'text/plain');
diff --git a/civicrm/CRM/Core/BAO/Address.php b/civicrm/CRM/Core/BAO/Address.php
index e44f9c90d2fdcdb971dec5783b6bc45c4ca33c29..004922b0d345ca5d9867c4ac187c1e8b8fda877a 100644
--- a/civicrm/CRM/Core/BAO/Address.php
+++ b/civicrm/CRM/Core/BAO/Address.php
@@ -15,6 +15,8 @@
  * @copyright CiviCRM LLC https://civicrm.org/licensing
  */
 
+use Civi\Api4\Address;
+
 /**
  * This is class to handle address related functions.
  */
@@ -33,12 +35,8 @@ class CRM_Core_BAO_Address extends CRM_Core_DAO_Address {
    * @return array|NULL|self
    *   array of created address
    */
-  public static function create(&$params, $fixAddress = TRUE) {
-    if (!isset($params['address']) || !is_array($params['address'])) {
-      return self::add($params, $fixAddress);
-    }
-    CRM_Core_Error::deprecatedFunctionWarning('Use legacyCreate if not doing a single crud action');
-    return self::legacyCreate($params, $fixAddress);
+  public static function create(array &$params, $fixAddress = TRUE) {
+    return self::add($params, $fixAddress);
   }
 
   /**
@@ -1212,6 +1210,7 @@ SELECT is_primary,
    * @return bool
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return (bool) self::deleteRecord(['id' => $id]);
   }
 
@@ -1362,8 +1361,7 @@ SELECT is_primary,
       // BUT info is not present at this time, and therefore we should be really careful when deleting the block.
       // $updateBlankLocInfo will help take appropriate decision. CRM-5969
       if (isset($value['id']) && !$addressExists && $updateBlankLocInfo) {
-        //delete the existing record
-        CRM_Core_BAO_Block::blockDelete('Address', ['id' => $value['id']]);
+        Address::delete(FALSE)->addWhere('id', '=', $value['id'])->execute();
         continue;
       }
       elseif (!$addressExists) {
diff --git a/civicrm/CRM/Core/BAO/Block.php b/civicrm/CRM/Core/BAO/Block.php
index d5082513067756ee8b6b4577711dac376e807900..506e9515763b24fae9b0dcfe9c3e31b83351201d 100644
--- a/civicrm/CRM/Core/BAO/Block.php
+++ b/civicrm/CRM/Core/BAO/Block.php
@@ -264,7 +264,7 @@ class CRM_Core_BAO_Block {
       // $updateBlankLocInfo will help take appropriate decision. CRM-5969
       if (!empty($value['id']) && !$dataExists && $updateBlankLocInfo) {
         //delete the existing record
-        $baoString::del($value['id']);
+        $baoString::deleteRecord($value);
         continue;
       }
       elseif (!$dataExists) {
@@ -307,7 +307,7 @@ class CRM_Core_BAO_Block {
     }
 
     $baoString = 'CRM_Core_BAO_' . $name;
-    $baoString::del($params['id']);
+    $baoString::deleteRecord($params);
   }
 
   /**
@@ -457,7 +457,7 @@ class CRM_Core_BAO_Block {
    * @param array $locations
    */
   public static function sortPrimaryFirst(&$locations) {
-    uasort($locations, 'self::primaryComparison');
+    uasort($locations, [__CLASS__, 'primaryComparison']);
   }
 
   /**
diff --git a/civicrm/CRM/Core/BAO/CustomField.php b/civicrm/CRM/Core/BAO/CustomField.php
index 7f4aed8d5515d61c4522aeed2dfc258e3afc84ae..cf05c109b2e9c0700f6e1a073e8eabe792da7873 100644
--- a/civicrm/CRM/Core/BAO/CustomField.php
+++ b/civicrm/CRM/Core/BAO/CustomField.php
@@ -15,6 +15,9 @@
  * @copyright CiviCRM LLC https://civicrm.org/licensing
  */
 
+use Civi\Api4\CustomField;
+use Civi\Api4\Utils\CoreUtil;
+
 /**
  * Business objects for managing custom data fields.
  */
@@ -95,6 +98,11 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         'name' => 'Contact Reference',
         'label' => ts('Contact Reference'),
       ],
+      [
+        'id' => 'EntityReference',
+        'name' => 'Entity Reference',
+        'label' => ts('Entity Reference'),
+      ],
     ];
   }
 
@@ -118,6 +126,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
       'File' => CRM_Utils_Type::T_STRING,
       'Link' => CRM_Utils_Type::T_STRING,
       'ContactReference' => CRM_Utils_Type::T_INT,
+      'EntityReference' => CRM_Utils_Type::T_INT,
       'Country' => CRM_Utils_Type::T_INT,
     ];
   }
@@ -610,6 +619,83 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     return self::$_importFields[$cacheKey];
   }
 
+  /**
+   * Get all active custom fields (cached wrapper).
+   *
+   * @param false|int $permissionType
+   *   - Either FALSE (do not check) or CRM_Core_Permission::VIEW or CRM_Core_Permission::EDIT
+   *
+   * @return array
+   *   List of customField details keyed by customFieldID
+   * @throws \CRM_Core_Exception
+   */
+  public static function getAllCustomFields($permissionType): array {
+    if ($permissionType !== FALSE && !is_int($permissionType)) {
+      throw new CRM_Core_Exception('permissionCheck must be FALSE or CRM_Core_Permission::VIEW or CRM_Core_Permission::EDIT');
+    }
+    $cacheString = __CLASS__ . __FUNCTION__ . CRM_Core_Config::domainID() . '_' . CRM_Core_I18n::getLocale();
+    if ($permissionType) {
+      $cacheString .= 'check_' . $permissionType . '_user_' . CRM_Core_Session::getLoggedInContactID();
+    }
+    if (!Civi::cache('metadata')->has($cacheString)) {
+      $apiCall = CustomField::get(FALSE)
+        ->addOrderBy('custom_group_id.title')
+        ->addOrderBy('custom_group_id.weight')
+        ->addOrderBy('weight')
+        ->addOrderBy('label')
+        ->addSelect('*')
+        ->addSelect('custom_group_id.extends')
+        ->addSelect('custom_group_id.extends_entity_column_id')
+        ->addSelect('custom_group_id.extends_entity_column_value')
+        ->addSelect('custom_group_id.is_active')
+        ->addSelect('custom_group_id.name')
+        ->addSelect('custom_group_id.table_name')
+        ->addSelect('custom_group_id.is_public');
+      if ($permissionType && !CRM_Core_Permission::customGroupAdmin()) {
+        $availableGroups = CRM_Core_Permission::customGroup($permissionType);
+        $apiCall->addWhere('custom_group_id', 'IN', empty($availableGroups) ? [0] : $availableGroups);
+      }
+
+      $types = (array) $apiCall->execute()->indexBy('id');
+
+      Civi::cache('metadata')->set($cacheString, $types);
+    }
+    return Civi::cache('metadata')->get($cacheString);
+  }
+
+  /**
+   * Get all active custom fields for the given contact type.
+   *
+   * This is formatted as an apiv4 Style array.
+   *
+   * @param string $contactType
+   * @param bool|int $permissionType
+   *  - Either FALSE (do not check) or CRM_Core_Permission::VIEW or CRM_Core_Permission::EDIT
+   * @param array $contactSubTypes
+   *
+   * @return array $fields
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public static function getCustomFieldsForContactType(string $contactType, $permissionType, array $contactSubTypes = []): array {
+    $fields = [];
+    foreach (self::getAllCustomFields($permissionType) as $field) {
+      if ($field['custom_group_id.extends'] === $contactType || $field['custom_group_id.extends'] === 'Contact') {
+        if (empty($contactSubTypes) || empty($field['custom_group_id.extends_entity_column_value'])) {
+          $fields[$field['id']] = $field;
+        }
+        else {
+          foreach ($contactSubTypes as $contactSubType) {
+            if (in_array($contactSubType, $field['custom_group_id.extends_entity_column_value'], TRUE)) {
+              $fields[$field['id']] = $field;
+            }
+          }
+        }
+      }
+    }
+    return $fields;
+  }
+
   /**
    * Return field ids and names (with groups).
    *
@@ -725,7 +811,6 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
       if (!$field->find(TRUE)) {
         throw new CRM_Core_Exception('Cannot find Custom Field ' . $fieldID);
       }
-
       $fieldValues = [];
       CRM_Core_DAO::storeValues($field, $fieldValues);
 
@@ -983,6 +1068,10 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
           );
 
         }
+        elseif ($field->data_type == 'EntityReference') {
+          $fieldAttributes['entity'] = $field->fk_entity;
+          $element = $qf->addAutocomplete($elementName, $label, $fieldAttributes, $useRequired && !$search);
+        }
         else {
           // FIXME: This won't work with customFieldOptions hook
           $fieldAttributes += [
@@ -1162,7 +1251,19 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
             $display = implode(', ', $displayNames);
           }
         }
-        elseif ($field['data_type'] == 'ContactReference') {
+        elseif ($field['data_type'] == 'EntityReference' && $value) {
+          try {
+            $result = civicrm_api4($field['fk_entity'], 'autocomplete', [
+              'checkPermissions' => FALSE,
+              'ids' => [$value],
+            ]);
+            $display = $result->single()['label'];
+          }
+          catch (CRM_Core_Exception $e) {
+            $display = '';
+          }
+        }
+        elseif (in_array($field['data_type'], ['ContactReference', 'EntityReference'])) {
           $display = $value;
         }
         elseif (is_array($value)) {
@@ -2384,7 +2485,7 @@ WHERE  option_group_id = {$optionGroupId}";
 
     if ($count < 2) {
       //delete the option group
-      CRM_Core_BAO_OptionGroup::del($optionGroupId);
+      CRM_Core_BAO_OptionGroup::deleteRecord(['id' => $optionGroupId]);
     }
   }
 
@@ -2693,7 +2794,7 @@ WHERE cf.id = %1 AND cg.is_multiple = 1";
     }
     // Do this before the "Select" string search because date fields have a "Select Date" html_type
     // and contactRef fields have an "Autocomplete-Select" html_type - contacts are an FK not an option list.
-    if (in_array($field['data_type'], ['ContactReference', 'Date'])) {
+    if (in_array($field['data_type'], ['EntityReference', 'ContactReference', 'Date'])) {
       return FALSE;
     }
     if (strpos($field['html_type'], 'Select') !== FALSE) {
@@ -2803,6 +2904,7 @@ WHERE cf.id = %1 AND cg.is_multiple = 1";
       'StateProvince' => 'civicrm_state_province',
       'ContactReference' => 'civicrm_contact',
       'File' => 'civicrm_file',
+      'EntityReference' => CoreUtil::getInfoItem((string) $field->fk_entity, 'table_name'),
     ];
     if (isset($fkFields[$field->data_type])) {
       // Serialized fields store value-separated strings which are incompatible with FK constraints
diff --git a/civicrm/CRM/Core/BAO/CustomValueTable.php b/civicrm/CRM/Core/BAO/CustomValueTable.php
index 9ba11ab1a0527db8860e8684fa06063eed60c4b7..2312c81847053fd2353411e76d993a3213f3a785 100644
--- a/civicrm/CRM/Core/BAO/CustomValueTable.php
+++ b/civicrm/CRM/Core/BAO/CustomValueTable.php
@@ -203,6 +203,10 @@ class CRM_Core_BAO_CustomValueTable {
               }
               break;
 
+            case 'EntityReference':
+              $type = 'Integer';
+              break;
+
             case 'RichTextEditor':
               $type = 'String';
               break;
@@ -315,6 +319,7 @@ class CRM_Core_BAO_CustomValueTable {
       // the below three are FK's, and have constraints added to them
 
       case 'ContactReference':
+      case 'EntityReference':
       case 'StateProvince':
       case 'Country':
       case 'File':
diff --git a/civicrm/CRM/Core/BAO/Email.php b/civicrm/CRM/Core/BAO/Email.php
index 0f5ce646710c1d2285b6592601b26f91fc18e7de..e7e4eb9a1dc3e3d3ab159b94aa83de7289ef9e26 100644
--- a/civicrm/CRM/Core/BAO/Email.php
+++ b/civicrm/CRM/Core/BAO/Email.php
@@ -355,6 +355,7 @@ AND    reset_date IS NULL
    * @return bool
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return (bool) self::deleteRecord(['id' => $id]);
   }
 
diff --git a/civicrm/CRM/Core/BAO/IM.php b/civicrm/CRM/Core/BAO/IM.php
index a410e912746a381dca16ab5320beb5949772abe1..b1a916c374d3d42982d76f51e12c20cb18d41c0c 100644
--- a/civicrm/CRM/Core/BAO/IM.php
+++ b/civicrm/CRM/Core/BAO/IM.php
@@ -163,6 +163,7 @@ ORDER BY cim.is_primary DESC, im_id ASC ";
    * @return bool
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return (bool) self::deleteRecord(['id' => $id]);
   }
 
diff --git a/civicrm/CRM/Core/BAO/Location.php b/civicrm/CRM/Core/BAO/Location.php
index 6ded7549966ef15dca4c0f61969f060998582277..628551677a26182c939372c403f3ca616ee45433 100644
--- a/civicrm/CRM/Core/BAO/Location.php
+++ b/civicrm/CRM/Core/BAO/Location.php
@@ -222,44 +222,6 @@ WHERE e.id = %1";
     ];
   }
 
-  /**
-   * Delete all the block associated with the location.
-   *
-   * Note a universe search on 1 Oct 2020 found no calls to this function.
-   *
-   * @deprecated
-   *
-   * @param int $contactId
-   *   Contact id.
-   * @param int $locationTypeId
-   *   Id of the location to delete.
-   * @throws CRM_Core_Exception
-   */
-  public static function deleteLocationBlocks($contactId, $locationTypeId) {
-    CRM_Core_Error::deprecatedFunctionWarning('Use v4 api');
-    // ensure that contactId has a value
-    if (empty($contactId) ||
-      !CRM_Utils_Rule::positiveInteger($contactId)
-    ) {
-      throw new CRM_Core_Exception('Incorrect contact id parameter passed to deleteLocationBlocks');
-    }
-
-    if (empty($locationTypeId) ||
-      !CRM_Utils_Rule::positiveInteger($locationTypeId)
-    ) {
-      // so we only delete the blocks which DO NOT have a location type Id
-      // CRM-3581
-      $locationTypeId = 'null';
-    }
-
-    static $blocks = ['Address', 'Phone', 'IM', 'OpenID', 'Email'];
-
-    $params = ['contact_id' => $contactId, 'location_type_id' => $locationTypeId];
-    foreach ($blocks as $name) {
-      CRM_Core_BAO_Block::blockDelete($name, $params);
-    }
-  }
-
   /**
    * Make sure contact should have only one primary block, CRM-5051.
    *
diff --git a/civicrm/CRM/Core/BAO/Log.php b/civicrm/CRM/Core/BAO/Log.php
index 3d10a68537a476373bf5a929cd7eefa56647b36c..e6e83aac4da5c507d03e06ad0d460ba5a91bde1d 100644
--- a/civicrm/CRM/Core/BAO/Log.php
+++ b/civicrm/CRM/Core/BAO/Log.php
@@ -89,9 +89,9 @@ class CRM_Core_BAO_Log extends CRM_Core_DAO_Log {
     }
 
     if (!$userID) {
-      $api_key = CRM_Utils_Request::retrieve('api_key', 'String', $store, FALSE, NULL, 'REQUEST');
+      $api_key = CRM_Utils_Request::retrieve('api_key', 'String');
 
-      if ($api_key && strtolower($api_key) != 'null') {
+      if ($api_key && strtolower($api_key) !== 'null') {
         $userID = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $api_key, 'id', 'api_key');
       }
     }
diff --git a/civicrm/CRM/Core/BAO/OptionGroup.php b/civicrm/CRM/Core/BAO/OptionGroup.php
index 09e21694236ceccd65f492fea276fbe500ae52c3..c7de6d7eea0550ad6364dccacd8017bdc5530b57 100644
--- a/civicrm/CRM/Core/BAO/OptionGroup.php
+++ b/civicrm/CRM/Core/BAO/OptionGroup.php
@@ -52,18 +52,14 @@ class CRM_Core_BAO_OptionGroup extends CRM_Core_DAO_OptionGroup implements \Civi
    * Add the Option Group.
    *
    * @param array $params
-   *   Reference array contains the values submitted by the form.
-   * @param array $ids
-   *   Reference array contains the id.
    *
    * @deprecated
    * @return CRM_Core_DAO_OptionGroup
    */
-  public static function add(&$params, $ids = []) {
-    if (empty($params['id']) && !empty($ids['optionGroup'])) {
-      CRM_Core_Error::deprecatedFunctionWarning('no $ids array');
-      $params['id'] = $ids['optionGroup'];
-    }
+  public static function add($params) {
+    // This is very similar to CRM_Core_DAO::makeNameFromLabel which would be
+    // called automatically via `self::writeRecord()`
+    // TODO: Check if the differences matter, then deprecate this function and switch to writeRecord.
     if (empty($params['name']) && empty($params['id'])) {
       $params['name'] = CRM_Utils_String::titleToVar(strtolower($params['title']));
     }
diff --git a/civicrm/CRM/Core/BAO/Phone.php b/civicrm/CRM/Core/BAO/Phone.php
index 3ed91d733b3c4d1c2544cc3e442b8a010094ebc9..cf095fa29a6d9c127f7474211b74b5c2e2a23fd6 100644
--- a/civicrm/CRM/Core/BAO/Phone.php
+++ b/civicrm/CRM/Core/BAO/Phone.php
@@ -236,6 +236,7 @@ ORDER BY ph.is_primary DESC, phone_id ASC ";
    * @return bool
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return (bool) self::deleteRecord(['id' => $id]);
   }
 
diff --git a/civicrm/CRM/Core/BAO/SchemaHandler.php b/civicrm/CRM/Core/BAO/SchemaHandler.php
index 0ee0b27c4cab5f0aa4ba3d251b348469fc6ce157..a58dd8c4821df6983eab09439cd57c3f533ce224 100644
--- a/civicrm/CRM/Core/BAO/SchemaHandler.php
+++ b/civicrm/CRM/Core/BAO/SchemaHandler.php
@@ -547,23 +547,23 @@ MODIFY      {$columnName} varchar( $length )
 
   /**
    * Check if a foreign key Exists
+   *
    * @param string $table_name
    * @param string $constraint_name
+   *
    * @return bool TRUE if FK is found
    */
-  public static function checkFKExists($table_name, $constraint_name) {
-    $dao = new CRM_Core_DAO();
+  public static function checkFKExists(string $table_name, string $constraint_name): bool {
     $query = "
       SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
-      WHERE TABLE_SCHEMA = %1
-      AND TABLE_NAME = %2
-      AND CONSTRAINT_NAME = %3
+      WHERE TABLE_SCHEMA = DATABASE()
+      AND TABLE_NAME = %1
+      AND CONSTRAINT_NAME = %2
       AND CONSTRAINT_TYPE = 'FOREIGN KEY'
     ";
     $params = [
-      1 => [$dao->_database, 'String'],
-      2 => [$table_name, 'String'],
-      3 => [$constraint_name, 'String'],
+      1 => [$table_name, 'String'],
+      2 => [$constraint_name, 'String'],
     ];
     $dao = CRM_Core_DAO::executeQuery($query, $params, TRUE, NULL, FALSE, FALSE);
 
diff --git a/civicrm/CRM/Core/BAO/UFField.php b/civicrm/CRM/Core/BAO/UFField.php
index e43bc4a25e34f044e075785a662da7b305b6fa59..ae8a9dd907544f44821c9e3df3c70b62d218eaf5 100644
--- a/civicrm/CRM/Core/BAO/UFField.php
+++ b/civicrm/CRM/Core/BAO/UFField.php
@@ -285,12 +285,15 @@ WHERE cf.id IN (" . $customFieldIds . ") AND is_multiple = 1 LIMIT 0,1";
    * Copy existing profile fields to
    * new profile from the already built profile
    *
+   * @deprecated
+   *
    * @param int $old_id
    *   From which we need to copy.
    * @param bool $new_id
    *   In which to copy.
    */
   public static function copy($old_id, $new_id) {
+    CRM_Core_Error::deprecatedFunctionWarning('');
     $ufField = new CRM_Core_DAO_UFField();
     $ufField->uf_group_id = $old_id;
     $ufField->find();
diff --git a/civicrm/CRM/Core/BAO/UFMatch.php b/civicrm/CRM/Core/BAO/UFMatch.php
index 831a70ee70cbb32bff2a8272b8dc0062951a246e..5734a9c2398494708e0bae96affc330348468c62 100644
--- a/civicrm/CRM/Core/BAO/UFMatch.php
+++ b/civicrm/CRM/Core/BAO/UFMatch.php
@@ -47,10 +47,10 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
   /**
    * Given a UF user object, make sure there is a contact
    * object for this user. If the user has new values, we need
-   * to update the CRM DB with the new values
+   * to update the CRM DB with the new values.
    *
    * @param Object $user
-   *   The drupal user object.
+   *   The user object.
    * @param bool $update
    *   Has the user object been edited.
    * @param $uf
@@ -71,9 +71,8 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
     $userSystemID = $userSystem->getBestUFID($user);
     $uniqId = $userSystem->getBestUFUniqueIdentifier($user);
 
-    // if the id of the object is zero (true for anon users in drupal)
-    // have we already processed this user, if so early
-    // return.
+    // If the id of the object is zero (true for anon users in Drupal),
+    // have we already processed this user? If so return early.
     $userID = $session->get('userID');
     $ufID = $session->get('ufID');
 
@@ -81,14 +80,14 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
       return;
     }
 
-    //check do we have logged in user.
+    // Check do we have logged in user.
     $isUserLoggedIn = CRM_Utils_System::isUserLoggedIn();
 
-    // reset the session if we are a different user
+    // Reset the session if we are a different user.
     if ($ufID && $ufID != $userSystemID) {
       $session->reset();
 
-      //get logged in user ids, and set to session.
+      // Get logged in user ids, and set to session.
       if ($isUserLoggedIn) {
         $userIds = self::getUFValues();
         $session->set('ufID', CRM_Utils_Array::value('uf_id', $userIds, ''));
@@ -96,7 +95,7 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
       }
     }
 
-    // return early
+    // Return early.
     if ($userSystemID == 0) {
       return;
     }
@@ -106,12 +105,12 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
       return;
     }
 
-    //make sure we have session w/ consistent ids.
+    // Make sure we have session w/ consistent ids.
     $ufID = $ufmatch->uf_id;
     $userID = $ufmatch->contact_id;
     if ($isUserLoggedIn) {
       $loggedInUserUfID = CRM_Utils_System::getLoggedInUfID();
-      //are we processing logged in user.
+      // Are we processing logged in user.
       if ($loggedInUserUfID && $loggedInUserUfID != $ufID) {
         $userIds = self::getUFValues($loggedInUserUfID);
         $ufID = CRM_Utils_Array::value('uf_id', $userIds, '');
@@ -119,11 +118,11 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
       }
     }
 
-    //set user ids to session.
+    // Set user ids to session.
     $session->set('ufID', $ufID);
     $session->set('userID', $userID);
 
-    // add current contact to recently viewed
+    // Add current contact to recently viewed.
     if ($ufmatch->contact_id) {
       [$displayName, $contactImage, $contactType, $contactSubtype, $contactImageUrl]
         = CRM_Contact_BAO_Contact::getDisplayAndImage($ufmatch->contact_id, TRUE, TRUE);
@@ -146,11 +145,10 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
   }
 
   /**
-   * Synchronize the object with the UF Match entry. Can be called stand-alone from
-   * the drupalUsers script
+   * Synchronize the object with the UF Match entry.
    *
    * @param Object $user
-   *   The drupal user object.
+   *   The user object.
    * @param string $userKey
    *   The id of the user from the uf object.
    * @param string $uniqId
@@ -169,7 +167,7 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
     $config = CRM_Core_Config::singleton();
     $newContact = FALSE;
 
-    // make sure that a contact id exists for this user id
+    // Make sure that a contact id exists for this user id.
     $ufmatch = new CRM_Core_DAO_UFMatch();
     $ufmatch->domain_id = CRM_Core_Config::domainID();
     $ufmatch->uf_id = $userKey;
@@ -181,9 +179,12 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
       if (!empty($_POST) && !$isLogin) {
         $dedupeParameters = $_POST;
         $dedupeParameters['email'] = $uniqId;
-        // dev/core#1858 Ensure that if we have a contactID parameter which is set in the Create user Record contact task form
-        // That this contactID value is passed through as the contact_id to the get duplicate contacts function. This is necessary because for Drupal 8 this function gets invoked
-        // Before the civicrm_uf_match record is added where as in D7 it isn't called until the user tries to actually login.
+        // dev/core#1858 Ensure that if we have a contactID parameter which is
+        // set in the Create user Record contact task form. That this contactID
+        // value is passed through as the contact_id to the get duplicate
+        // contacts function. This is necessary because for Drupal 8 this
+        // function gets invoked. Before the civicrm_uf_match record is added
+        // where as in D7 it isn't called until the user tries to actually login.
         if (!empty($dedupeParameters['contactID'])) {
           $dedupeParameters['contact_id'] = $dedupeParameters['contactID'];
         }
@@ -191,14 +192,14 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
         $ids = CRM_Contact_BAO_Contact::getDuplicateContacts($dedupeParameters, 'Individual', 'Unsupervised', [], FALSE);
 
         if (!empty($ids) && Civi::settings()->get('uniq_email_per_site')) {
-          // restrict dupeIds to ones that belong to current domain/site.
+          // Restrict dupeIds to ones that belong to current domain/site.
           $siteContacts = CRM_Core_BAO_Domain::getContactList();
           foreach ($ids as $index => $dupeId) {
             if (!in_array($dupeId, $siteContacts)) {
               unset($ids[$index]);
             }
           }
-          // re-index the array
+          // Re-index the array.
           $ids = array_values($ids);
         }
         if (!empty($ids)) {
@@ -212,9 +213,8 @@ class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
 
       $found = FALSE;
       if ($dao) {
-        // ensure there does not exists a contact_id / uf_id pair
-        // in the DB. This might be due to multiple emails per contact
-        // CRM-9091
+        // Ensure there does not exists a contact_id / uf_id pair in the DB.
+        // This might be due to multiple emails per contact CRM-9091.
         $sql = '
 SELECT id
 FROM   civicrm_uf_match
@@ -235,24 +235,10 @@ AND    domain_id = %2
       }
 
       if (!$found) {
-        $contactParameters = [];
-        // Not sure why we're testing for this. Is there ever a case
-        // in which $user is not an object?
-        if (is_object($user)) {
-          if ($config->userSystem->is_drupal) {
-            $primary_email = $uniqId;
-          }
-          elseif ($uf === 'WordPress') {
-            $primary_email = $user->user_email;
-          }
-          else {
-            $primary_email = $user->email;
-          }
-          $contactParameters['email'] = $primary_email;
-        }
-        else {
-          CRM_Core_Error::deprecatedWarning('please log how you hit this...');
-        }
+        $contactParameters = $config->userSystem->getContactDetailsFromUser([
+          'user' => $user,
+          'uniqId' => $uniqId,
+        ]);
 
         if ($ctype === 'Organization') {
           $contactParameters['organization_name'] = $uniqId;
@@ -263,30 +249,15 @@ AND    domain_id = %2
 
         $contactParameters['contact_type'] = $ctype ?? 'Individual';
 
-        // extract first / middle / last name
-        // for joomla
-        if ($uf === 'Joomla' && $user->name) {
-          CRM_Utils_String::extractName($user->name, $contactParameters);
-        }
-
-        if ($uf === 'WordPress') {
-          if ($user->first_name) {
-            $contactParameters['first_name'] = $user->first_name;
-          }
-
-          if ($user->last_name) {
-            $contactParameters['last_name'] = $user->last_name;
-          }
-        }
-
         $contactID = civicrm_api3('Contact', 'create', $contactParameters)['id'];
         $ufmatch->contact_id = $contactID;
         $ufmatch->uf_name = $uniqId;
       }
 
-      // check that there are not two CMS IDs matching the same CiviCRM contact - this happens when a civicrm
-      // user has two e-mails and there is a cms match for each of them
-      // the gets rid of the nasty fata error but still reports the error
+      // Check that there are not two CMS IDs matching the same CiviCRM contact.
+      // This happens when a CiviCRM user has two e-mails and there is a cms
+      // match for each of them the gets rid of the nasty fata error but still
+      // reports the error.
       $sql = "
 SELECT uf_id
 FROM   civicrm_uf_match
@@ -340,7 +311,7 @@ AND    domain_id    = %4
       return;
     }
 
-    // 1.do check for contact Id.
+    // 1. Do check for contact Id.
     $ufmatch = new CRM_Core_DAO_UFMatch();
     $ufmatch->contact_id = $contactId;
     $ufmatch->domain_id = CRM_Core_Config::domainID();
@@ -362,7 +333,7 @@ AND    domain_id    = %4
     }
 
     // CRM-6928
-    // 2.do check for duplicate ufName.
+    // 2. Do check for duplicate ufName.
     $ufDupeName = new CRM_Core_DAO_UFMatch();
     $ufDupeName->uf_name = $ufName;
     $ufDupeName->domain_id = CRM_Core_Config::domainID();
@@ -376,7 +347,7 @@ AND    domain_id    = %4
       return;
     }
 
-    // save the updated ufmatch object
+    // Save the updated ufmatch object.
     $ufmatch->uf_name = $ufName;
     $ufmatch->save();
     $config->userSystem->updateCMSName($ufmatch->uf_id, $ufName);
@@ -398,22 +369,23 @@ AND    domain_id    = %4
     $ufmatch->contact_id = $contactId;
     $ufmatch->domain_id = CRM_Core_Config::domainID();
     if ($ufmatch->find(TRUE)) {
-      // Save the email in UF Match table
+      // Save the email in UF Match table.
       $ufmatch->uf_name = $emailAddress;
       CRM_Core_BAO_UFMatch::create((array) $ufmatch);
 
-      // If CMS integration is disabled skip Civi email update if CMS user email is changed
+      // If CMS integration is disabled skip Civi email update if CMS user email
+      // is changed.
       if (Civi::settings()->get('syncCMSEmail') == FALSE) {
         return;
       }
 
-      //check if the primary email for the contact exists
-      //$contactDetails[1] - email
-      //$contactDetails[3] - email id
+      // Check if the primary email for the contact exists.
+      // $contactDetails[1] - email
+      // $contactDetails[3] - email id
       $contactDetails = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactId);
 
       if (trim($contactDetails[1])) {
-        //update if record is found but different
+        // Update if record is found but different.
         $emailID = $contactDetails[3];
         if (trim($contactDetails[1]) != $emailAddress) {
           civicrm_api3('Email', 'create', [
@@ -423,7 +395,7 @@ AND    domain_id    = %4
         }
       }
       else {
-        //else insert a new email record
+        // Else insert a new email record.
         $result = civicrm_api3('Email', 'create', [
           'contact_id' => $contactId,
           'email' => $emailAddress,
@@ -452,7 +424,7 @@ AND    domain_id    = %4
     $ufmatch->domain_id = $domainId = CRM_Core_Config::domainID();
     $ufmatch->delete();
 
-    // Flush cache
+    // Flush cache.
     Civi::$statics[__CLASS__][$domainId] = [];
   }
 
@@ -463,7 +435,7 @@ AND    domain_id    = %4
    *   Id of UF for which related contact_id is required.
    *
    * @return int|null
-   *   contact_id on success, null otherwise
+   *   contact_id on success, null otherwise.
    */
   public static function getContactId($ufID) {
     if (!$ufID) {
@@ -496,7 +468,7 @@ AND    domain_id    = %4
    *   ID of the contact for which related uf_id is required.
    *
    * @return int|null
-   *   uf_id of the given contact_id on success, null otherwise
+   *   uf_id of the given contact_id on success, null otherwise.
    */
   public static function getUFId($contactID) {
     if (!$contactID) {
@@ -532,7 +504,7 @@ AND    domain_id    = %4
    *
    * @deprecated
    * @return int
-   *   contact_id on success, null otherwise
+   *   contact_id on success, null otherwise.
    */
   public static function getContactIDs() {
     CRM_Core_Error::deprecatedFunctionWarning('unused function to be removed');
@@ -567,11 +539,11 @@ AND    domain_id    = %4
 
   /**
    * Get the next unused uf_id value, since the standalone UF doesn't
-   * have id's (it uses OpenIDs, which go in a different field)
+   * have id's (it uses OpenIDs, which go in a different field).
    *
    * @deprecated
    * @return int
-   *   next highest unused value for uf_id
+   *   Next highest unused value for uf_id.
    */
   public static function getNextUfIdValue() {
     CRM_Core_Error::deprecatedFunctionWarning('unused function to be removed');
@@ -588,7 +560,9 @@ AND    domain_id    = %4
   }
 
   /**
-   * @param $email
+   * Is duplicate user
+   *
+   * @param string $email
    * @deprecated
    * @return bool
    */
@@ -644,7 +618,7 @@ AND    domain_id    = %4
    * @inheritDoc
    */
   public function addSelectWhereClause() {
-    // Prevent default behavior of joining ACLs onto the contact_id field
+    // Prevent default behavior of joining ACLs onto the contact_id field.
     $clauses = [];
     CRM_Utils_Hook::selectWhereClause($this, $clauses);
     return $clauses;
diff --git a/civicrm/CRM/Core/BAO/Website.php b/civicrm/CRM/Core/BAO/Website.php
index aafd8e8a5181f8d0b639507b9e29eed18e8efa47..14c740d694fb596c6945408f09bee875db04a917 100644
--- a/civicrm/CRM/Core/BAO/Website.php
+++ b/civicrm/CRM/Core/BAO/Website.php
@@ -31,6 +31,7 @@ class CRM_Core_BAO_Website extends CRM_Core_DAO_Website {
    * @throws \CRM_Core_Exception
    */
   public static function create($params) {
+    CRM_Core_Error::deprecatedFunctionWarning('writeRecord');
     return self::writeRecord($params);
   }
 
@@ -81,7 +82,7 @@ class CRM_Core_BAO_Website extends CRM_Core_DAO_Website {
       }
       if (!empty($values['url'])) {
         $values['contact_id'] = $contactID;
-        self::create($values);
+        self::writeRecord($values);
       }
       elseif ($skipDelete && !empty($values['id'])) {
         static::deleteRecord($values);
@@ -99,6 +100,7 @@ class CRM_Core_BAO_Website extends CRM_Core_DAO_Website {
    * @deprecated
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return (bool) static::deleteRecord(['id' => $id]);
   }
 
diff --git a/civicrm/CRM/Core/ClassLoader.php b/civicrm/CRM/Core/ClassLoader.php
index 0501e6bdac061c9ee25164cd361374acce32ecc4..b92512ad36ffb8b8baecc91d7f8348ab59317c2c 100644
--- a/civicrm/CRM/Core/ClassLoader.php
+++ b/civicrm/CRM/Core/ClassLoader.php
@@ -65,6 +65,7 @@ class CRM_Core_ClassLoader {
       'CiviTestSuite',
       'CiviUnitTestCase',
       'CiviEndToEndTestCase',
+      'CiviSimpleCacheTest',
       'Contact',
       'ContributionPage',
       'Custom',
diff --git a/civicrm/CRM/Core/Config.php b/civicrm/CRM/Core/Config.php
index 795f95576e451b06d9604e24332bf6ce0162386c..415762ce4afbd2aeb9d4bf23819626178ceffe73 100644
--- a/civicrm/CRM/Core/Config.php
+++ b/civicrm/CRM/Core/Config.php
@@ -363,12 +363,10 @@ class CRM_Core_Config extends CRM_Core_Config_MagicMerge {
    *   tables created recently from being deleted.
    */
   public static function clearTempTables($timeInterval = FALSE): void {
-
-    $dao = new CRM_Core_DAO();
     $query = "
       SELECT TABLE_NAME as tableName
       FROM   INFORMATION_SCHEMA.TABLES
-      WHERE  TABLE_SCHEMA = %1
+      WHERE  TABLE_SCHEMA = DATABASE()
       AND TABLE_NAME LIKE 'civicrm_tmp_d%'
     ";
 
@@ -376,7 +374,7 @@ class CRM_Core_Config extends CRM_Core_Config_MagicMerge {
       $query .= " AND CREATE_TIME < DATE_SUB(NOW(), INTERVAL {$timeInterval})";
     }
 
-    $tableDAO = CRM_Core_DAO::executeQuery($query, [1 => [$dao->database(), 'String']]);
+    $tableDAO = CRM_Core_DAO::executeQuery($query);
     $tables = [];
     while ($tableDAO->fetch()) {
       $tables[] = $tableDAO->tableName;
diff --git a/civicrm/CRM/Core/DAO.php b/civicrm/CRM/Core/DAO.php
index eb84c1651ca9286d5f9912a503ba771ae12a7285..f63391c46d2821ba820e314d128c5c39093b0edc 100644
--- a/civicrm/CRM/Core/DAO.php
+++ b/civicrm/CRM/Core/DAO.php
@@ -1080,25 +1080,6 @@ class CRM_Core_DAO extends DB_DataObject {
     }
   }
 
-  /**
-   * Check if there is a given column in a specific table.
-   *
-   * @deprecated
-   * @see CRM_Core_BAO_SchemaHandler::checkIfFieldExists
-   *
-   * @param string $tableName
-   * @param string $columnName
-   * @param bool $i18nRewrite
-   *   Whether to rewrite the query on multilingual setups.
-   *
-   * @return bool
-   *   true if exists, else false
-   */
-  public static function checkFieldExists($tableName, $columnName, $i18nRewrite = TRUE) {
-    CRM_Core_Error::deprecatedFunctionWarning('CRM_Core_BAO_SchemaHandler::checkIfFieldExists');
-    return CRM_Core_BAO_SchemaHandler::checkIfFieldExists($tableName, $columnName, $i18nRewrite);
-  }
-
   /**
    * Scans all the tables using a slow query and table name.
    *
@@ -1108,7 +1089,7 @@ class CRM_Core_DAO extends DB_DataObject {
     $dao = CRM_Core_DAO::executeQuery(
       "SELECT TABLE_NAME
        FROM information_schema.TABLES
-       WHERE TABLE_SCHEMA = '" . CRM_Core_DAO::getDatabaseName() . "'
+       WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME LIKE 'civicrm_%'
          AND TABLE_NAME NOT LIKE '%_tmp%'
       ");
@@ -1130,7 +1111,7 @@ class CRM_Core_DAO extends DB_DataObject {
       "SELECT count(*)
        FROM information_schema.TABLES
        WHERE ENGINE = 'MyISAM'
-         AND TABLE_SCHEMA = '" . CRM_Core_DAO::getDatabaseName() . "'
+         AND TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME LIKE 'civicrm_%'
          AND TABLE_NAME NOT LIKE 'civicrm_tmp_%'
       ");
@@ -1139,11 +1120,12 @@ class CRM_Core_DAO extends DB_DataObject {
   /**
    * Get the name of the CiviCRM database.
    *
+   * @deprecated use mysql DATABASE() within the query.
+   *
    * @return string
    */
-  public static function getDatabaseName() {
-    $daoObj = new CRM_Core_DAO();
-    return $daoObj->database();
+  public static function getDatabaseName(): string {
+    return (new CRM_Core_DAO())->database();
   }
 
   /**
@@ -2440,30 +2422,6 @@ SELECT contact_id
     }
   }
 
-  /**
-   * @param string $prefix
-   * @param bool $addRandomString
-   * @param null $string
-   *
-   * @return string
-   * @deprecated
-   * @see CRM_Utils_SQL_TempTable
-   */
-  public static function createTempTableName($prefix = 'civicrm', $addRandomString = TRUE, $string = NULL) {
-    CRM_Core_Error::deprecatedFunctionWarning('Use CRM_Utils_SQL_TempTable interface to create temporary tables');
-    $tableName = $prefix . "_temp";
-
-    if ($addRandomString) {
-      if ($string) {
-        $tableName .= "_" . $string;
-      }
-      else {
-        $tableName .= "_" . md5(uniqid('', TRUE));
-      }
-    }
-    return $tableName;
-  }
-
   /**
    * @param bool $view
    * @param bool $trigger
diff --git a/civicrm/CRM/Core/DAO/CustomField.php b/civicrm/CRM/Core/DAO/CustomField.php
index 8faf7f472841dae228fa23a920043d7c0130da7f..0fd63f551b9aa8f79c857cba125c93d6fb61552e 100644
--- a/civicrm/CRM/Core/DAO/CustomField.php
+++ b/civicrm/CRM/Core/DAO/CustomField.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Core/CustomField.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:f102cd2c666ba101ff0933c9fdac92ce)
+ * (GenCodeChecksum:74a1b749ab865c09722b5e8904c382a9)
  */
 
 /**
@@ -320,6 +320,15 @@ class CRM_Core_DAO_CustomField extends CRM_Core_DAO {
    */
   public $in_selector;
 
+  /**
+   * Name of entity being referenced.
+   *
+   * @var string|null
+   *   (SQL type: varchar(255))
+   *   Note that values will be retrieved from the database as a string.
+   */
+  public $fk_entity;
+
   /**
    * Class constructor.
    */
@@ -812,6 +821,21 @@ class CRM_Core_DAO_CustomField extends CRM_Core_DAO {
           'localizable' => 0,
           'add' => '4.5',
         ],
+        'fk_entity' => [
+          'name' => 'fk_entity',
+          'type' => CRM_Utils_Type::T_STRING,
+          'title' => ts('Entity'),
+          'description' => ts('Name of entity being referenced.'),
+          'maxlength' => 255,
+          'size' => CRM_Utils_Type::HUGE,
+          'where' => 'civicrm_custom_field.fk_entity',
+          'default' => NULL,
+          'table_name' => 'civicrm_custom_field',
+          'entity' => 'CustomField',
+          'bao' => 'CRM_Core_BAO_CustomField',
+          'localizable' => 0,
+          'add' => '5.60',
+        ],
       ];
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
     }
diff --git a/civicrm/CRM/Core/DAO/JobLog.php b/civicrm/CRM/Core/DAO/JobLog.php
index 3b0e98bb13ff8fd736a4be130041a3b6a9247223..c38d1e777042ebf1b53f439183c5c43978bd1c9c 100644
--- a/civicrm/CRM/Core/DAO/JobLog.php
+++ b/civicrm/CRM/Core/DAO/JobLog.php
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Core/JobLog.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:258a172d38252619a97c1d74663bdd87)
+ * (GenCodeChecksum:98cdc7ac1580bf4029404b0e69fa401c)
  */
 
 /**
@@ -58,7 +58,7 @@ class CRM_Core_DAO_JobLog extends CRM_Core_DAO {
   public $run_time;
 
   /**
-   * Pointer to job id - not a FK though, just for logging purposes
+   * Pointer to job id
    *
    * @var int|string|null
    *   (SQL type: int unsigned)
@@ -130,6 +130,7 @@ class CRM_Core_DAO_JobLog extends CRM_Core_DAO {
     if (!isset(Civi::$statics[__CLASS__]['links'])) {
       Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
       Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'domain_id', 'civicrm_domain', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'job_id', 'civicrm_job', 'id');
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
     }
     return Civi::$statics[__CLASS__]['links'];
@@ -198,12 +199,16 @@ class CRM_Core_DAO_JobLog extends CRM_Core_DAO {
           'name' => 'job_id',
           'type' => CRM_Utils_Type::T_INT,
           'title' => ts('Job ID'),
-          'description' => ts('Pointer to job id - not a FK though, just for logging purposes'),
+          'description' => ts('Pointer to job id'),
           'where' => 'civicrm_job_log.job_id',
           'table_name' => 'civicrm_job_log',
           'entity' => 'JobLog',
           'bao' => 'CRM_Core_DAO_JobLog',
           'localizable' => 0,
+          'FKClassName' => 'CRM_Core_DAO_Job',
+          'html' => [
+            'type' => 'Number',
+          ],
           'add' => '4.1',
         ],
         'name' => [
diff --git a/civicrm/CRM/Core/Error.php b/civicrm/CRM/Core/Error.php
index bb39511f4a41f12cd041d428cbaff48f4e675f8b..92f14f1d9a62a8bbb83826fc56c37158c2444ea2 100644
--- a/civicrm/CRM/Core/Error.php
+++ b/civicrm/CRM/Core/Error.php
@@ -566,6 +566,10 @@ class CRM_Core_Error extends PEAR_ErrorStack {
    * Provided the user has the 'view debug output' the output should be displayed. In all
    * cases it should be logged.
    *
+   * @deprecated see https://docs.civicrm.org/dev/en/latest/framework/logging/
+   *
+   * Use (e.g) `Civi::log()->error()` (priority dependent).
+   *
    * @param string $message
    * @param bool $out
    *   Should we log or return the output.
@@ -598,13 +602,7 @@ class CRM_Core_Error extends PEAR_ErrorStack {
     }
 
     if (!empty(\Civi::$statics[__CLASS__]['userFrameworkLogging'])) {
-      // should call $config->userSystem->logger($message) here - but I got a situation where userSystem was not an object - not sure why
-      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]);
-      }
+      $config->userSystem->logger($message, $priority);
     }
 
     return $str;
diff --git a/civicrm/CRM/Core/Form.php b/civicrm/CRM/Core/Form.php
index f71fb5335bb5b712050b08c6bb78e85e3e6a1feb..c3682d97b43185a5ddb52ee95aaa98d6bd3c4775 100644
--- a/civicrm/CRM/Core/Form.php
+++ b/civicrm/CRM/Core/Form.php
@@ -508,6 +508,11 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
       unset($extra['option_context']);
     }
 
+    // Allow disabled to be a boolean
+    if (isset($attributes['disabled']) && $attributes['disabled'] === FALSE) {
+      unset($attributes['disabled']);
+    }
+
     $element = $this->addElement($type, $name, CRM_Utils_String::purifyHTML($label), $attributes, $extra);
     if (HTML_QuickForm::isError($element)) {
       CRM_Core_Error::statusBounce(HTML_QuickForm::errorMessage($element));
@@ -1151,7 +1156,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    *
    * If renderer is not set create one and initialize it.
    *
-   * @return object
+   * @return CRM_Core_Form_Renderer
    */
   public function &getRenderer() {
     if (!isset($this->_renderer)) {
@@ -2203,6 +2208,33 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     }
   }
 
+  /**
+   * @param string $name
+   * @param string $label
+   * @param array $props
+   * @param bool $required
+   *
+   * @return HTML_QuickForm_Element
+   */
+  public function addAutocomplete(string $name, string $label = '', array $props = [], bool $required = FALSE) {
+    $props += [
+      'entity' => 'Contact',
+      'api' => [],
+      'select' => [],
+    ];
+    $props['api'] += [
+      'formName' => 'qf:' . get_class($this),
+      'fieldName' => $name,
+    ];
+    $props['class'] = ltrim(($props['class'] ?? '') . ' crm-form-autocomplete');
+    $props['placeholder'] = $props['placeholder'] ?? self::selectOrAnyPlaceholder($props, $required);
+    $props['data-select-params'] = json_encode($props['select']);
+    $props['data-api-params'] = json_encode($props['api']);
+    $props['data-api-entity'] = $props['entity'];
+    CRM_Utils_Array::remove($props, 'select', 'api', 'entity');
+    return $this->add('text', $name, $label, $props, $required);
+  }
+
   /**
    * Create a single or multiple entity ref field.
    * @param string $name
@@ -2615,6 +2647,17 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     }
   }
 
+  /**
+   * Push the current url to the userContext.
+   *
+   * This is like a save point :-). The next status bounce will
+   * return the browser to this url unless another is added.
+   */
+  protected function pushUrlToUserContext(): void {
+    CRM_Core_Session::singleton()
+      ->pushUserContext(CRM_Utils_System::url(CRM_Utils_System::currentPath(), 'reset=1'));
+  }
+
   /**
    * Set options and attributes for chain select fields based on the controlling field's value
    */
diff --git a/civicrm/CRM/Core/Form/Search.php b/civicrm/CRM/Core/Form/Search.php
index aa2e13b5b1954914c250cd09aacc8ce753e55fa3..693af70456425f5199dd30231644bd1290988cae 100644
--- a/civicrm/CRM/Core/Form/Search.php
+++ b/civicrm/CRM/Core/Form/Search.php
@@ -121,8 +121,8 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
       $this->handleForcedSearch();
     }
     $this->_formValues = $this->getFormValues();
-    // For searchResultsTasks.tpl.
-    $this->addExpectedSmartyVariables(['savedSearch', 'selectorLabel']);
+    // For searchResultsTasks.tpl & displaySearchCriteria.tpl
+    $this->addExpectedSmartyVariables(['savedSearch', 'selectorLabel', 'operator']);
   }
 
   /**
diff --git a/civicrm/CRM/Core/InnoDBIndexer.php b/civicrm/CRM/Core/InnoDBIndexer.php
index d10c4c2e18b1c320ba4ea2133923eabebe8205e1..18532d452aeaa209278c59085b5b8dfe5cb0c118 100644
--- a/civicrm/CRM/Core/InnoDBIndexer.php
+++ b/civicrm/CRM/Core/InnoDBIndexer.php
@@ -163,7 +163,7 @@ class CRM_Core_InnoDBIndexer {
     $dao = CRM_Core_DAO::executeQuery("
   SELECT index_name as index_name
   FROM information_Schema.STATISTICS
-  WHERE table_schema = '" . CRM_Core_DAO::getDatabaseName() . "'
+  WHERE table_schema = DATABASE()
     AND table_name = '$table'
     AND index_type = 'FULLTEXT'
   GROUP BY index_name
diff --git a/civicrm/CRM/Core/Lock.php b/civicrm/CRM/Core/Lock.php
index 3a3c220e190d37e2937801006b6296ae02e8c39e..67adc99c022363231e74c0f0cfea07e1c371dfcd 100644
--- a/civicrm/CRM/Core/Lock.php
+++ b/civicrm/CRM/Core/Lock.php
@@ -171,9 +171,6 @@ class CRM_Core_Lock implements \Civi\Core\Lock\LockInterface {
    */
   public function acquire($timeout = NULL) {
     if (!$this->_hasLock) {
-      if (!CRM_Utils_SQL::supportsMultipleLocks() && self::$jobLog && CRM_Core_DAO::singleValueQuery("SELECT IS_USED_LOCK( '" . self::$jobLog . "')")) {
-        return $this->hackyHandleBrokenCode(self::$jobLog);
-      }
 
       $query = "SELECT GET_LOCK( %1, %2 )";
       $params = [
@@ -235,29 +232,4 @@ class CRM_Core_Lock implements \Civi\Core\Lock\LockInterface {
     return $this->_hasLock;
   }
 
-  /**
-   * CRM-12856 locks were originally set up for jobs, but the concept was extended to caching & groups without
-   * understanding that would undermine the job locks (because grabbing a lock implicitly releases existing ones)
-   * this is all a big hack to mitigate the impact of that - but should not be seen as a fix. Not sure correct fix
-   * but maybe locks should be used more selectively? Or else we need to handle is some cool way that Tim is yet to write :-)
-   * if we are running in the context of the cron log then we would rather die (or at least let our process die)
-   * than release that lock - so if the attempt is being made by setCache or something relatively trivial
-   * we'll just return TRUE, but if it's another job then we will crash as that seems 'safer'
-   *
-   * @param string $jobLog
-   * @throws CRM_Core_Exception
-   * @return bool
-   */
-  public function hackyHandleBrokenCode($jobLog) {
-    if (stristr($this->_name, 'job')) {
-      \Civi::log()->debug('lock acquisition for ' . $this->_name . '(' . $this->_id . ')' . ' attempted when ' . $jobLog . ' is not released');
-      throw new CRM_Core_Exception('lock acquisition for ' . $this->_name . '(' . $this->_id . ')' . ' attempted when ' . $jobLog . ' is not released');
-    }
-    if (defined('CIVICRM_LOCK_DEBUG')) {
-      \Civi::log()->debug('(CRM-12856) faking lock for ' . $this->_name . '(' . $this->_id . ')');
-    }
-    $this->_hasLock = TRUE;
-    return TRUE;
-  }
-
 }
diff --git a/civicrm/CRM/Core/OptionGroup.php b/civicrm/CRM/Core/OptionGroup.php
index 04e989436c426b0f97dbae1df19de24040c93b0b..b0701cc5f2cb2211713464f9f7289267df8326dd 100644
--- a/civicrm/CRM/Core/OptionGroup.php
+++ b/civicrm/CRM/Core/OptionGroup.php
@@ -328,94 +328,6 @@ WHERE  v.option_group_id = g.id
     }
   }
 
-  /**
-   * @deprecated - use CRM_Core_PseudoConstant::getLabel
-   *
-   * @param string $groupName
-   * @param $value
-   * @param bool $onlyActiveValue
-   *
-   * @return null
-   */
-  public static function getLabel($groupName, $value, $onlyActiveValue = TRUE) {
-    CRM_Core_Error::deprecatedFunctionWarning('CRM_Core_PseudoConstant::getLabel');
-    if (empty($groupName) ||
-      empty($value)
-    ) {
-      return NULL;
-    }
-
-    $query = "
-SELECT  v.label as label ,v.value as value
-FROM   civicrm_option_value v,
-       civicrm_option_group g
-WHERE  v.option_group_id = g.id
-  AND  g.name            = %1
-  AND  g.is_active       = 1
-  AND  v.value           = %2
-";
-    if ($onlyActiveValue) {
-      $query .= " AND  v.is_active = 1 ";
-    }
-    $p = [
-      1 => [$groupName, 'String'],
-      2 => [$value, 'Integer'],
-    ];
-    $dao = CRM_Core_DAO::executeQuery($query, $p);
-    if ($dao->fetch()) {
-      return $dao->label;
-    }
-    return NULL;
-  }
-
-  /**
-   * @deprecated
-   *
-   * This function is not cached.
-   *
-   * @param string $groupName
-   * @param $label
-   * @param string $labelField
-   * @param string $labelType
-   * @param string $valueField
-   *
-   * @return null
-   */
-  public static function getValue(
-    $groupName,
-    $label,
-    $labelField = 'label',
-    $labelType = 'String',
-    $valueField = 'value'
-  ) {
-    if (empty($label)) {
-      return NULL;
-    }
-
-    CRM_Core_Error::deprecatedFunctionWarning('CRM_Core_PseudoConstant::getKey');
-
-    $query = "
-SELECT  v.label as label ,v.{$valueField} as value
-FROM   civicrm_option_value v,
-       civicrm_option_group g
-WHERE  v.option_group_id = g.id
-  AND  g.name            = %1
-  AND  v.is_active       = 1
-  AND  g.is_active       = 1
-  AND  v.$labelField     = %2
-";
-
-    $p = [
-      1 => [$groupName, 'String'],
-      2 => [$label, $labelType],
-    ];
-    $dao = CRM_Core_DAO::executeQuery($query, $p);
-    if ($dao->fetch()) {
-      return $dao->value;
-    }
-    return NULL;
-  }
-
   /**
    * Get option_value.value from default option_value row for an option group
    *
diff --git a/civicrm/CRM/Core/Payment/PayPalProIPN.php b/civicrm/CRM/Core/Payment/PayPalProIPN.php
index 4dc98b5c31585e46259c502cdfd7397bba850247..6f9fdeb59b827aced6e32caf241c213067ef02f9 100644
--- a/civicrm/CRM/Core/Payment/PayPalProIPN.php
+++ b/civicrm/CRM/Core/Payment/PayPalProIPN.php
@@ -291,7 +291,7 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN {
 
           // In future moving to create pending & then complete, but this OK for now.
           // Also consider accepting 'Failed' like other processors.
-          $input['contribution_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', 'Completed');
+          $input['contribution_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
           $input['invoice_id'] = md5(uniqid(rand(), TRUE));
           $input['original_contribution_id'] = $this->getContributionID();
           $input['contribution_recur_id'] = $this->getContributionRecurID();
diff --git a/civicrm/CRM/Core/PseudoConstant.php b/civicrm/CRM/Core/PseudoConstant.php
index 4e3b97c0c18cf29e600b9526406421014aa10ec2..3159f677257838a17739cb9ff777f3fa104e38b2 100644
--- a/civicrm/CRM/Core/PseudoConstant.php
+++ b/civicrm/CRM/Core/PseudoConstant.php
@@ -1500,6 +1500,10 @@ WHERE  id = %1
     $from = 'FROM %3';
     $wheres = [];
     $order = 'ORDER BY %2';
+    if (in_array('id', $availableFields, TRUE)) {
+      // Example: 'ORDER BY abbreviation, id' because `abbreviation`s are not unique.
+      $order .= ', id';
+    }
 
     // Use machine name in certain contexts
     if ($context === 'validate' || $context === 'match') {
diff --git a/civicrm/CRM/Core/SelectValues.php b/civicrm/CRM/Core/SelectValues.php
index 5490d0562a69c7eda3a0b628713e95c86b37a56b..43383eda42cc247b609dc76a5d2a0927c68ed5f6 100644
--- a/civicrm/CRM/Core/SelectValues.php
+++ b/civicrm/CRM/Core/SelectValues.php
@@ -540,9 +540,12 @@ class CRM_Core_SelectValues {
   /**
    * Different type of Membership Tokens.
    *
+   * @deprecated
+   *
    * @return array
    */
   public static function membershipTokens(): array {
+    CRM_Core_Error::deprecatedFunctionWarning('token processor');
     return [
       '{membership.id}' => ts('Membership ID'),
       '{membership.status_id:label}' => ts('Status'),
@@ -562,6 +565,7 @@ class CRM_Core_SelectValues {
    * @return array
    */
   public static function eventTokens(): array {
+    CRM_Core_Error::deprecatedFunctionWarning('token processor');
     $tokenProcessor = new TokenProcessor(Civi::dispatcher(), ['schema' => ['eventId']]);
     $allTokens = $tokenProcessor->listTokens();
     foreach (array_keys($allTokens) as $token) {
@@ -580,6 +584,7 @@ class CRM_Core_SelectValues {
    * @return array
    */
   public static function contributionTokens(): array {
+    CRM_Core_Error::deprecatedFunctionWarning('use the token processor');
     $tokenProcessor = new TokenProcessor(Civi::dispatcher(), ['schema' => ['contributionId']]);
     $allTokens = $tokenProcessor->listTokens();
     foreach (array_keys($allTokens) as $token) {
diff --git a/civicrm/CRM/Core/Session.php b/civicrm/CRM/Core/Session.php
index f1edba894758545daf0f0f4441c1ab8b1e2e7b87..3f0011cc78cc8ddfa7689226d19e8db1064fa33c 100644
--- a/civicrm/CRM/Core/Session.php
+++ b/civicrm/CRM/Core/Session.php
@@ -431,13 +431,13 @@ class CRM_Core_Session {
    * @param bool $reset
    *   Should we reset the status variable?.
    *
-   * @return string
+   * @return array
    *   the status message if any
    */
-  public function getStatus($reset = FALSE) {
+  public function getStatus($reset = FALSE) : array {
     $this->initialize();
 
-    $status = NULL;
+    $status = [];
     if (array_key_exists('status', $this->_session[$this->_key])) {
       $status = $this->_session[$this->_key]['status'];
     }
diff --git a/civicrm/CRM/Core/Smarty.php b/civicrm/CRM/Core/Smarty.php
index 5c0d1d884a7ffe6266e83d93950c9870cf5bbb75..769bb813efec1f4d4cdcfc50dac9ac6f5d0320ab 100644
--- a/civicrm/CRM/Core/Smarty.php
+++ b/civicrm/CRM/Core/Smarty.php
@@ -131,7 +131,6 @@ class CRM_Core_Smarty extends Smarty {
       $this->assign('langSwitch', CRM_Core_I18n::uiLanguages());
     }
 
-    $this->register_function('crmURL', ['CRM_Utils_System', 'crmURL']);
     if (CRM_Utils_Constant::value('CIVICRM_SMARTY_DEFAULT_ESCAPE')) {
       // When default escape is enabled if the core escape is called before
       // any custom escaping is done the modifier_escape function is not
@@ -144,6 +143,7 @@ class CRM_Core_Smarty extends Smarty {
       $this->default_modifiers[] = 'escape:"htmlall"';
     }
     $this->load_filter('pre', 'resetExtScope');
+    $this->load_filter('pre', 'htxtFilter');
 
     $this->assign('crmPermissions', new CRM_Core_Smarty_Permissions());
 
@@ -301,16 +301,28 @@ class CRM_Core_Smarty extends Smarty {
   }
 
   /**
-   * @param $path
+   * Add template directory(s).
+   *
+   * @param string|array $template_dir directory(s) of template sources
+   * @param string $key (Smarty3+) of the array element to assign the template dir to
+   * @param bool $isConfig (Smarty3+) true for config_dir
+   *
+   * @return Smarty          current Smarty instance for chaining
    */
-  public function addTemplateDir($path) {
+  public function addTemplateDir($template_dir, $key = NULL, $isConfig = FALSE) {
+    if (method_exists('parent', 'addTemplateDir')) {
+      // More recent versions of Smarty have this method.
+      return parent::addTemplateDir($template_dir, $key, $isConfig);
+    }
     if (is_array($this->template_dir)) {
-      array_unshift($this->template_dir, $path);
+      if (!in_array($template_dir, $this->template_dir)) {
+        array_unshift($this->template_dir, $template_dir);
+      }
     }
     else {
-      $this->template_dir = [$path, $this->template_dir];
+      $this->template_dir = [$template_dir, $this->template_dir];
     }
-
+    return $this;
   }
 
   /**
@@ -473,7 +485,7 @@ class CRM_Core_Smarty extends Smarty {
 
     $value = smarty_modifier_escape($string, $esc_type, $char_set);
     if ($value !== $string) {
-      Civi::log()->debug('smarty escaping original {original}, escaped {escaped} type {type} charset {charset}', [
+      Civi::log('smarty')->debug('smarty escaping original {original}, escaped {escaped} type {type} charset {charset}', [
         'original' => $string,
         'escaped' => $value,
         'type' => $esc_type,
diff --git a/civicrm/CRM/Core/Smarty/plugins/block.ts.php b/civicrm/CRM/Core/Smarty/plugins/block.ts.php
index f3cbd36fe95276980d7d399a15152d825f897d5f..6c1faeffd010c62eb04c91554d40b4e1e2261cfa 100644
--- a/civicrm/CRM/Core/Smarty/plugins/block.ts.php
+++ b/civicrm/CRM/Core/Smarty/plugins/block.ts.php
@@ -34,8 +34,8 @@
  *   the string, translated by gettext
  */
 function smarty_block_ts($params, $text, &$smarty) {
-  if (!isset($params['domain'])) {
-    $params['domain'] = $smarty->get_template_vars('extensionKey');
+  if (!isset($params['domain']) && $extensionKey = $smarty->get_template_vars('extensionKey')) {
+    $params['domain'] = is_array($extensionKey) ? $extensionKey : [$extensionKey, NULL];
   }
   return ts($text, $params);
 }
diff --git a/civicrm/CRM/Core/Smarty/plugins/function.crmURL.php b/civicrm/CRM/Core/Smarty/plugins/function.crmURL.php
new file mode 100644
index 0000000000000000000000000000000000000000..810e3b4bc1ca715ec9167247a1c5256fd0e9365a
--- /dev/null
+++ b/civicrm/CRM/Core/Smarty/plugins/function.crmURL.php
@@ -0,0 +1,50 @@
+<?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       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC
+ *
+ */
+
+/**
+ * Generate a URL.
+ *
+ * Ex: `{crmURL p='civicrm/acl/entityrole' q='reset=1'}`
+ * Ex: `{crmURL p='civicrm/profile/create' q='id=123&reset=1' fe=1}`
+ *
+ * Each URL component uses an abbreviation (e.g. "p"<=>"path"; "q"<=>"query").
+ *
+ * @param array $params
+ *   List of URL properties.
+ *   - "p" (string $path)
+ *     The path being linked to, such as "civicrm/add".
+ *   - "q" (array|string $query)
+ *     A query string to append to the link, or an array of key-value pairs.
+ *   - "a" (bool $absolute)
+ *     Whether to force the output to be an absolute link (beginning with a
+ *     URI-scheme such as 'http:'). Useful for links that will be displayed
+ *     outside the site, such as in an RSS feed.
+ *   - "f" (string $fragment)
+ *     A "#" fragment to append to the link. This could a named anchor (as
+ *     in `#section2`) or a client-side route (as in `#/mailing/new`).
+ *   - "h" (bool $htmlize)
+ *     Whether to encode special html characters such as &.
+ *   - "fe" (bool $frontend)
+ *     This link should be to the CMS front end (applies to WP & Joomla).
+ *   - "fb" (bool $forceBackend)
+ *     This link should be to the CMS back end (applies to WP & Joomla).
+ * @return string
+ */
+function smarty_function_crmURL($params) {
+  return CRM_Utils_System::crmURL($params);
+}
diff --git a/civicrm/CRM/Core/Smarty/plugins/modifier.mb_truncate.php b/civicrm/CRM/Core/Smarty/plugins/modifier.mb_truncate.php
index a58909ff0592fa25ce9a5033783fa901558233f2..e5416bc2bb9d90cdbe766bafbd0908eac1706bbb 100644
--- a/civicrm/CRM/Core/Smarty/plugins/modifier.mb_truncate.php
+++ b/civicrm/CRM/Core/Smarty/plugins/modifier.mb_truncate.php
@@ -58,7 +58,7 @@ function smarty_modifier_mb_truncate($string, $length = 80, $etc = '...',
 
   }
 
-  if ($strlen($string) > $length) {
+  if ($string !== NULL && $strlen($string) > $length) {
     $length -= $strlen($etc);
     if (!$break_words) {
       $string = preg_replace('/\s+?(\S+)?$/', '', $substr($string, 0, $length + 1));
diff --git a/civicrm/CRM/Core/Smarty/plugins/prefilter.htxtFilter.php b/civicrm/CRM/Core/Smarty/plugins/prefilter.htxtFilter.php
new file mode 100644
index 0000000000000000000000000000000000000000..1a2a56a085709193c9fed5dde5d0e3816b5d59f3
--- /dev/null
+++ b/civicrm/CRM/Core/Smarty/plugins/prefilter.htxtFilter.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * The content of an "{htxt}" block should not be evaluated unless
+ * the active request is relevant. Otherwise, it will try to
+ * evaluate unassigned variables.
+ *
+ * @param string $tpl_source
+ * @param $smarty
+ * @return string
+ */
+function smarty_prefilter_htxtFilter($tpl_source, &$smarty) {
+  if (strpos($tpl_source, '{htxt') === FALSE) {
+    return $tpl_source;
+  }
+
+  $htxts = 0;
+  $_htxts = 0;
+
+  $result = preg_replace_callback_array([
+    '/\{htxt id=([\'\"][^\'\"]+[\'\"])/' => function ($m) use (&$htxts) {
+      $htxts++;
+      return sprintf('{if $id == %s}%s', $m[1], $m[0]);
+    },
+    ';\{/htxt\};' => function($m) use (&$_htxts) {
+      $_htxts++;
+      return '{/htxt}{/if}';
+    },
+  ], $tpl_source);
+
+  if ($htxts !== $_htxts) {
+    throw new \RuntimeException(sprintf('Invalid {htxt} tag. Wrapped %d opening-tags and %d closing-tags.', $htxts, $_htxts));
+  }
+
+  return $result;
+}
diff --git a/civicrm/CRM/Custom/Form/Field.php b/civicrm/CRM/Custom/Form/Field.php
index 0ae4d84ce06fe29459ae77fc50a90e340d1cd104..8da250a92cafd290e53a8100faac8c25533f6588 100644
--- a/civicrm/CRM/Custom/Form/Field.php
+++ b/civicrm/CRM/Custom/Form/Field.php
@@ -228,6 +228,14 @@ class CRM_Custom_Form_Field extends CRM_Core_Form {
 
     $this->add('checkbox', 'serialize', ts('Multi-Select'));
 
+    $this->addAutocomplete('fk_entity', ts('Entity'), [
+      'class' => 'twenty',
+      // Don't allow entity to be changed once field is created
+      'disabled' => $this->_action == CRM_Core_Action::UPDATE && !empty($this->_values['fk_entity']),
+      'entity' => 'Entity',
+      'select' => ['minimumInputLength' => 0],
+    ]);
+
     if ($this->_action == CRM_Core_Action::UPDATE) {
       $this->freeze('data_type');
       if (!empty($this->_values['option_group_id'])) {
@@ -613,6 +621,12 @@ SELECT count(*)
       }
     }
 
+    if ($dataType === 'EntityReference') {
+      if (empty($fields['fk_entity'])) {
+        $errors['fk_entity'] = ts('Selecting an entity is required');
+      }
+    }
+
     if ($dataType == 'Date') {
       if (!$fields['date_format']) {
         $errors['date_format'] = ts('Please select a date format.');
diff --git a/civicrm/CRM/Custom/Form/Group.php b/civicrm/CRM/Custom/Form/Group.php
index be0ac00fdd6120b25a284442513175af72e34888..07499e96da14e43bcdd6f23f2253439434a54f08 100644
--- a/civicrm/CRM/Custom/Form/Group.php
+++ b/civicrm/CRM/Custom/Form/Group.php
@@ -401,7 +401,7 @@ class CRM_Custom_Form_Group extends CRM_Core_Form {
     // prompt Drupal Views users to update $db_prefix in settings.php, if necessary
     global $db_prefix;
     $config = CRM_Core_Config::singleton();
-    if (is_array($db_prefix) && $config->userSystem->is_drupal && module_exists('views')) {
+    if (is_array($db_prefix) && $config->userSystem->viewsExists()) {
       // get table_name for each custom group
       $tables = [];
       $sql = "SELECT table_name FROM civicrm_custom_group WHERE is_active = 1";
diff --git a/civicrm/CRM/Custom/Import/Form/DataSource.php b/civicrm/CRM/Custom/Import/Form/DataSource.php
index 4f780de882a6ad4dee245e6c85eca30224c32887..90f5174e42e993b608d96437ab0484638b1b6344 100644
--- a/civicrm/CRM/Custom/Import/Form/DataSource.php
+++ b/civicrm/CRM/Custom/Import/Form/DataSource.php
@@ -22,10 +22,6 @@ use Civi\Api4\CustomGroup;
  */
 class CRM_Custom_Import_Form_DataSource extends CRM_Import_Form_DataSource {
 
-  const PATH = 'civicrm/import/custom';
-
-  const IMPORT_ENTITY = 'Multi value custom data';
-
   /**
    * Get the name of the type to be stored in civicrm_user_job.type_id.
    *
diff --git a/civicrm/CRM/Dedupe/Merger.php b/civicrm/CRM/Dedupe/Merger.php
index 5e352409b0214ec94372ed2e32a86c62b6119d82..4071797bdf784a15d46ff2d094f2662793d201e9 100644
--- a/civicrm/CRM/Dedupe/Merger.php
+++ b/civicrm/CRM/Dedupe/Merger.php
@@ -1706,9 +1706,6 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    *   api - through which it is properly tested - so can be refactored with some comfort.)
    * @param bool|int $checkPermission
    *   Either a CRM_Core_Permission constant or FALSE to disable checks
-   * @param string|int $singleRecord
-   *   holds 'new' or id if view/edit/copy form for a single record is being loaded.
-   * @param bool $showPublicOnly
    *
    * @return array
    *   Custom field 'tree'.
@@ -1724,7 +1721,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    *
    * @throws \CRM_Core_Exception
    */
-  public static function getTree(
+  private static function getTree(
     $entityType,
     $toReturn = [],
     $entityID = NULL,
@@ -1734,9 +1731,7 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
     $fromCache = TRUE,
     $onlySubType = NULL,
     $returnAll = FALSE,
-    $checkPermission = CRM_Core_Permission::EDIT,
-    $singleRecord = NULL,
-    $showPublicOnly = FALSE
+    $checkPermission = CRM_Core_Permission::EDIT
   ) {
     if ($checkPermission === TRUE) {
       CRM_Core_Error::deprecatedWarning('Unexpected TRUE passed to CustomGroup::getTree $checkPermission param.');
@@ -1905,10 +1900,6 @@ WHERE civicrm_custom_group.is_active = 1
         );
     }
 
-    if ($showPublicOnly && $is_public_version) {
-      $strWhere .= "AND civicrm_custom_group.is_public = 1";
-    }
-
     $orderBy = "
 ORDER BY civicrm_custom_group.weight,
          civicrm_custom_group.title,
@@ -1951,7 +1942,7 @@ ORDER BY civicrm_custom_group.weight,
     // add info to groupTree
 
     if (isset($groupTree['info']) && !empty($groupTree['info']) &&
-      !empty($groupTree['info']['tables']) && $singleRecord != 'new'
+      !empty($groupTree['info']['tables'])
     ) {
       $select = $from = $where = [];
       $groupTree['info']['where'] = NULL;
@@ -1985,7 +1976,7 @@ ORDER BY civicrm_custom_group.weight,
       }
       $multipleFieldTablesWithEntityData = array_keys($entityMultipleSelectClauses);
       if (!empty($multipleFieldTablesWithEntityData)) {
-        CRM_Core_BAO_CustomGroup::buildEntityTreeMultipleFields($groupTree, $entityID, $entityMultipleSelectClauses, $multipleFieldTablesWithEntityData, $singleRecord);
+        CRM_Core_BAO_CustomGroup::buildEntityTreeMultipleFields($groupTree, $entityID, $entityMultipleSelectClauses, $multipleFieldTablesWithEntityData);
       }
 
     }
@@ -2112,34 +2103,29 @@ ORDER BY civicrm_custom_group.weight,
       'groupName' => 'postal_greeting',
     ];
     CRM_Core_OptionGroup::lookupValues($submitted, $names, TRUE);
-    // fix custom fields so they're edible by createProfileContact()
-    $cFields = self::getCustomFieldMetadata($contactType);
 
     if (!isset($submitted)) {
       $submitted = [];
     }
+
+    // Move view only custom fields CRM-5362
+    $viewOnlyCustomFields = [];
     foreach ($submitted as $key => $value) {
       if (strpos($key, 'custom_') === 0) {
         $fieldID = (int) substr($key, 7);
-        if (empty($cFields[$fieldID])) {
-          $htmlType = (string) $cFields[$fieldID]['attributes']['html_type'];
-          $isSerialized = CRM_Core_BAO_CustomField::isSerialized($cFields[$fieldID]['attributes']);
-          $isView = (bool) $cFields[$fieldID]['attributes']['is_view'];
+        $fieldMetadata = CRM_Core_BAO_CustomField::getCustomFieldsForContactType($contactType, FALSE)[$fieldID] ?? NULL;
+        if ($fieldMetadata) {
+          $htmlType = (string) $fieldMetadata['html_type'];
+          $isSerialized = CRM_Core_BAO_CustomField::isSerialized($fieldMetadata);
+          $isView = (bool) $fieldMetadata['is_view'];
+          if ($isView) {
+            $viewOnlyCustomFields[$key] = $value;
+          }
           $submitted = self::processCustomFields($mainId, $key, $submitted, $value, $fieldID, $isView, $htmlType, $isSerialized);
-
         }
       }
     }
 
-    // move view only custom fields CRM-5362
-    $viewOnlyCustomFields = [];
-    foreach ($submitted as $key => $value) {
-      $fid = CRM_Core_BAO_CustomField::getKeyID($key);
-      if ($fid && array_key_exists($fid, $cFields) && !empty($cFields[$fid]['attributes']['is_view'])
-      ) {
-        $viewOnlyCustomFields[$key] = $value;
-      }
-    }
     // special case to set values for view only, CRM-5362
     if (!empty($viewOnlyCustomFields)) {
       $viewOnlyCustomFields['entityID'] = $mainId;
@@ -2156,7 +2142,7 @@ ORDER BY civicrm_custom_group.weight,
       'return' => ['created_date'],
     ])['created_date'];
     if ($otherCreatedDate < $mainCreatedDate && !empty($otherCreatedDate)) {
-      CRM_Core_DAO::executeQuery("UPDATE civicrm_contact SET created_date = %1 WHERE id = %2", [
+      CRM_Core_DAO::executeQuery('UPDATE civicrm_contact SET created_date = %1 WHERE id = %2', [
         1 => [$otherCreatedDate, 'String'],
         2 => [$mainId, 'Positive'],
       ]);
@@ -2619,43 +2605,6 @@ ORDER BY civicrm_custom_group.weight,
     return $submitted;
   }
 
-  /**
-   * Get metadata for the custom fields for the merge.
-   *
-   * @param string $contactType
-   *
-   * @return array
-   * @throws \CRM_Core_Exception
-   */
-  protected static function getCustomFieldMetadata($contactType) {
-    $treeCache = [];
-    if (!array_key_exists($contactType, $treeCache)) {
-      $treeCache[$contactType] = CRM_Core_BAO_CustomGroup::getTree(
-        $contactType,
-        NULL,
-        NULL,
-        -1,
-        [],
-        NULL,
-        TRUE,
-        NULL,
-        FALSE,
-        FALSE
-      );
-    }
-
-    $cFields = [];
-    foreach ($treeCache[$contactType] as $key => $group) {
-      if (!isset($group['fields'])) {
-        continue;
-      }
-      foreach ($group['fields'] as $fid => $field) {
-        $cFields[$fid]['attributes'] = $field;
-      }
-    }
-    return $cFields;
-  }
-
   /**
    * Get conflicts for proposed merge pair.
    *
@@ -3018,9 +2967,6 @@ ORDER BY civicrm_custom_group.weight,
    */
   protected static function getLocksOnContacts($contacts):array {
     $locks = [];
-    if (!CRM_Utils_SQL::supportsMultipleLocks()) {
-      return $locks;
-    }
     foreach ($contacts as $contactID) {
       $lock = Civi::lockManager()->acquire("data.core.contact.{$contactID}");
       if ($lock->isAcquired()) {
diff --git a/civicrm/CRM/Event/ActionMapping.php b/civicrm/CRM/Event/ActionMapping.php
index a14785f48b1eb72d338307b630c435692fcc235c..c474a8dba6aaf87b5afd7ffaf7a5533fd66bdebd 100644
--- a/civicrm/CRM/Event/ActionMapping.php
+++ b/civicrm/CRM/Event/ActionMapping.php
@@ -37,9 +37,6 @@ class CRM_Event_ActionMapping extends \Civi\ActionSchedule\Mapping {
    * @param \Civi\ActionSchedule\Event\MappingRegisterEvent $registrations
    */
   public static function onRegisterActionMappings(\Civi\ActionSchedule\Event\MappingRegisterEvent $registrations) {
-    if (!CRM_Core_Component::isEnabled('CiviEvent')) {
-      return;
-    }
     $registrations->register(CRM_Event_ActionMapping::create([
       'id' => CRM_Event_ActionMapping::EVENT_TYPE_MAPPING_ID,
       'entity' => 'civicrm_participant',
diff --git a/civicrm/CRM/Event/BAO/Event.php b/civicrm/CRM/Event/BAO/Event.php
index ce018aadf52a3d3dcea47e4e1f57713492cfc99f..55503aad232d2bca1253b28b5d22addf2e1a2086 100644
--- a/civicrm/CRM/Event/BAO/Event.php
+++ b/civicrm/CRM/Event/BAO/Event.php
@@ -177,6 +177,7 @@ class CRM_Event_BAO_Event extends CRM_Event_DAO_Event implements \Civi\Core\Hook
    * @deprecated
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return (bool) static::deleteRecord(['id' => $id]);
   }
 
diff --git a/civicrm/CRM/Event/BAO/Participant.php b/civicrm/CRM/Event/BAO/Participant.php
index e67401c82abbb5d81e1d4de5c9f10844eda2599f..97869fe7f812ed4fa19f7259a9a4d367ea1c0704 100644
--- a/civicrm/CRM/Event/BAO/Participant.php
+++ b/civicrm/CRM/Event/BAO/Participant.php
@@ -181,7 +181,7 @@ class CRM_Event_BAO_Participant extends CRM_Event_DAO_Participant implements \Ci
     ) {
       // Default status if not specified
       $participant->status_id = $participant->status_id ?: self::fields()['participant_status_id']['default'];
-      CRM_Activity_BAO_Activity::addActivity($participant, 'Event Registration');
+      CRM_Activity_BAO_Activity::addActivity($participant, 'Event Registration', $participant->contact_id);
     }
 
     //CRM-5403
diff --git a/civicrm/CRM/Event/Form/ManageEvent/Delete.php b/civicrm/CRM/Event/Form/ManageEvent/Delete.php
index 6260b2639ab41896f94a4bdc513081d51db3ab4f..8b6511aed7427935fe4d0ac57706b7a6bc560e53 100644
--- a/civicrm/CRM/Event/Form/ManageEvent/Delete.php
+++ b/civicrm/CRM/Event/Form/ManageEvent/Delete.php
@@ -81,7 +81,7 @@ class CRM_Event_Form_ManageEvent_Delete extends CRM_Event_Form_ManageEvent {
       ), ts('Deletion Error'), 'error');
       return;
     }
-    CRM_Event_BAO_Event::del($this->_id);
+    CRM_Event_BAO_Event::deleteRecord(['id' => $this->_id]);
     if ($this->_isTemplate) {
       CRM_Core_Session::setStatus(ts("'%1' has been deleted.", [1 => $this->_title]), ts('Template Deleted'), 'success');
       CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/eventTemplate', 'reset=1'));
diff --git a/civicrm/CRM/Event/Form/Registration.php b/civicrm/CRM/Event/Form/Registration.php
index b22ac78543e319f32503c7188872c05dc67f820e..a6cf7139aa927496d17f8d44a8d9128e1f47c803 100644
--- a/civicrm/CRM/Event/Form/Registration.php
+++ b/civicrm/CRM/Event/Form/Registration.php
@@ -586,7 +586,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     else {
       $priceSetId = CRM_Price_BAO_PriceSet::getFor('civicrm_event', $eventID);
     }
-    self::initSet($form, 'civicrm_event', $doNotIncludeExpiredFields, $priceSetId);
+    self::initSet($form, $doNotIncludeExpiredFields, $priceSetId);
 
     if (property_exists($form, '_context') && ($form->_context == 'standalone'
         || $form->_context == 'participant')
@@ -643,15 +643,13 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
    *
    * @param CRM_Core_Form $form
    *   Form entity id.
-   * @param string $entityTable
    * @param bool $doNotIncludeExpiredFields
    * @param int $priceSetId
    *   Price Set ID
    *
    * @todo - removed unneeded code from previously-shared function
    */
-  private static function initSet(&$form, $entityTable = 'civicrm_event', $doNotIncludeExpiredFields = FALSE, $priceSetId = NULL) {
-
+  private static function initSet($form, $doNotIncludeExpiredFields = FALSE, $priceSetId = NULL) {
     //check if price set is is_config
     if (is_numeric($priceSetId)) {
       if (CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceSetId, 'is_quick_config') && $form->getVar('_name') != 'Participant') {
@@ -661,28 +659,11 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
     // get price info
     if ($priceSetId) {
       if ($form->_action & CRM_Core_Action::UPDATE) {
-        $entityId = $entity = NULL;
-
-        switch ($entityTable) {
-          case 'civicrm_event':
-            $entity = 'participant';
-            if (in_array(CRM_Utils_System::getClassName($form), ['CRM_Event_Form_Participant', 'CRM_Event_Form_Task_Register'])) {
-              $entityId = $form->_id;
-            }
-            else {
-              $entityId = $form->_participantId;
-            }
-            break;
-
-          case 'civicrm_contribution_page':
-          case 'civicrm_contribution':
-            $entity = 'contribution';
-            $entityId = $form->_id;
-            break;
+        if (in_array(CRM_Utils_System::getClassName($form), ['CRM_Event_Form_Participant', 'CRM_Event_Form_Task_Register'])) {
+          $form->_values['line_items'] = CRM_Price_BAO_LineItem::getLineItems($form->_id, 'participant');
         }
-
-        if ($entityId && $entity) {
-          $form->_values['line_items'] = CRM_Price_BAO_LineItem::getLineItems($entityId, $entity);
+        else {
+          $form->_values['line_items'] = CRM_Price_BAO_LineItem::getLineItems($form->_participantId, 'participant');
         }
         $required = FALSE;
       }
@@ -696,41 +677,40 @@ class CRM_Event_Form_Registration extends CRM_Core_Form {
       $form->_values['fee'] = $form->_priceSet['fields'] ?? NULL;
 
       //get the price set fields participant count.
-      if ($entityTable == 'civicrm_event') {
-        //get option count info.
-        $form->_priceSet['optionsCountTotal'] = CRM_Price_BAO_PriceSet::getPricesetCount($priceSetId);
-        if ($form->_priceSet['optionsCountTotal']) {
-          $optionsCountDetails = [];
-          if (!empty($form->_priceSet['fields'])) {
-            foreach ($form->_priceSet['fields'] as $field) {
-              foreach ($field['options'] as $option) {
-                $count = CRM_Utils_Array::value('count', $option, 0);
-                $optionsCountDetails['fields'][$field['id']]['options'][$option['id']] = $count;
-              }
-            }
-          }
-          $form->_priceSet['optionsCountDetails'] = $optionsCountDetails;
-        }
-
-        //get option max value info.
-        $optionsMaxValueTotal = 0;
-        $optionsMaxValueDetails = [];
-
+      //get option count info.
+      $form->_priceSet['optionsCountTotal'] = CRM_Price_BAO_PriceSet::getPricesetCount($priceSetId);
+      if ($form->_priceSet['optionsCountTotal']) {
+        $optionsCountDetails = [];
         if (!empty($form->_priceSet['fields'])) {
           foreach ($form->_priceSet['fields'] as $field) {
             foreach ($field['options'] as $option) {
-              $maxVal = CRM_Utils_Array::value('max_value', $option, 0);
-              $optionsMaxValueDetails['fields'][$field['id']]['options'][$option['id']] = $maxVal;
-              $optionsMaxValueTotal += $maxVal;
+              $count = CRM_Utils_Array::value('count', $option, 0);
+              $optionsCountDetails['fields'][$field['id']]['options'][$option['id']] = $count;
             }
           }
         }
+        $form->_priceSet['optionsCountDetails'] = $optionsCountDetails;
+      }
 
-        $form->_priceSet['optionsMaxValueTotal'] = $optionsMaxValueTotal;
-        if ($optionsMaxValueTotal) {
-          $form->_priceSet['optionsMaxValueDetails'] = $optionsMaxValueDetails;
+      //get option max value info.
+      $optionsMaxValueTotal = 0;
+      $optionsMaxValueDetails = [];
+
+      if (!empty($form->_priceSet['fields'])) {
+        foreach ($form->_priceSet['fields'] as $field) {
+          foreach ($field['options'] as $option) {
+            $maxVal = CRM_Utils_Array::value('max_value', $option, 0);
+            $optionsMaxValueDetails['fields'][$field['id']]['options'][$option['id']] = $maxVal;
+            $optionsMaxValueTotal += $maxVal;
+          }
         }
       }
+
+      $form->_priceSet['optionsMaxValueTotal'] = $optionsMaxValueTotal;
+      if ($optionsMaxValueTotal) {
+        $form->_priceSet['optionsMaxValueDetails'] = $optionsMaxValueDetails;
+      }
+
       $form->set('priceSetId', $form->_priceSetId);
       $form->set('priceSet', $form->_priceSet);
     }
diff --git a/civicrm/CRM/Event/Form/Registration/Confirm.php b/civicrm/CRM/Event/Form/Registration/Confirm.php
index 184bcda8aa2f1bb1536ad796e0d14d4ef812dbab..3e01e84dcd2574036ad097676165bd3bbc67972c 100644
--- a/civicrm/CRM/Event/Form/Registration/Confirm.php
+++ b/civicrm/CRM/Event/Form/Registration/Confirm.php
@@ -1156,7 +1156,7 @@ class CRM_Event_Form_Registration_Confirm extends CRM_Event_Form_Registration {
    *
    * @param CRM_Event_Form_Registration_Confirm $form
    *
-   * @throws \Exception
+   * @throws \CRM_Core_Exception
    */
   public static function assignProfiles($form) {
     $participantParams = $form->_params;
diff --git a/civicrm/CRM/Event/Form/Registration/ThankYou.php b/civicrm/CRM/Event/Form/Registration/ThankYou.php
index f7f7e9386df1d442e320953f607f88f98f5d309b..9464ebacce93b568c91ac9f7206e8656e2a9c05f 100644
--- a/civicrm/CRM/Event/Form/Registration/ThankYou.php
+++ b/civicrm/CRM/Event/Form/Registration/ThankYou.php
@@ -177,6 +177,7 @@ class CRM_Event_Form_Registration_ThankYou extends CRM_Event_Form_Registration {
     }
 
     $this->assign('iCal', CRM_Event_BAO_Event::getICalLinks($this->_eventId));
+    $this->assign('isShowICalIconsInline', TRUE);
 
     $this->freeze();
 
diff --git a/civicrm/CRM/Event/Import/Form/DataSource.php b/civicrm/CRM/Event/Import/Form/DataSource.php
index 4817f0540083a130123bb407d70d285f1260c34f..e187c522b51d6518d7559d13bec4b5bcb6d08d94 100644
--- a/civicrm/CRM/Event/Import/Form/DataSource.php
+++ b/civicrm/CRM/Event/Import/Form/DataSource.php
@@ -20,10 +20,6 @@
  */
 class CRM_Event_Import_Form_DataSource extends CRM_Import_Form_DataSource {
 
-  const PATH = 'civicrm/event/import';
-
-  const IMPORT_ENTITY = 'Participant';
-
   /**
    * Get the name of the type to be stored in civicrm_user_job.type_id.
    *
diff --git a/civicrm/CRM/Event/Import/Parser/Participant.php b/civicrm/CRM/Event/Import/Parser/Participant.php
index 86bc1f0a4bfa2859aa304208103d02a71eb3367f..923143d14c47233f38f6977bcccd14cdd91211d2 100644
--- a/civicrm/CRM/Event/Import/Parser/Participant.php
+++ b/civicrm/CRM/Event/Import/Parser/Participant.php
@@ -45,13 +45,6 @@ class CRM_Event_Import_Parser_Participant extends CRM_Import_Parser {
    */
   protected $_separator;
 
-  /**
-   * Total number of lines in file.
-   *
-   * @var int
-   */
-  protected $_lineCount;
-
   /**
    * Whether the file has a column header or not
    *
diff --git a/civicrm/CRM/Event/Page/DashBoard.php b/civicrm/CRM/Event/Page/DashBoard.php
index 9f86809e13c49c2731e46e1454a5993e965f85e0..bc4af9c32a363ace4a582ff96d9ff4c72cfb1887 100644
--- a/civicrm/CRM/Event/Page/DashBoard.php
+++ b/civicrm/CRM/Event/Page/DashBoard.php
@@ -42,6 +42,7 @@ class CRM_Event_Page_DashBoard extends CRM_Core_Page {
     $this->assign('actionColumn', $actionColumn);
     $this->assign('eventSummary', $eventSummary);
     $this->assign('iCal', CRM_Event_BAO_Event::getICalLinks());
+    $this->assign('isShowICalIconsInline', FALSE);
   }
 
   /**
diff --git a/civicrm/CRM/Event/Page/EventInfo.php b/civicrm/CRM/Event/Page/EventInfo.php
index 8790c171bf5ea142626f31cea31cdef9e3bda408..c279ea153b8ea6e76d73235d03cd837c5e8bd00c 100644
--- a/civicrm/CRM/Event/Page/EventInfo.php
+++ b/civicrm/CRM/Event/Page/EventInfo.php
@@ -47,6 +47,7 @@ class CRM_Event_Page_EventInfo extends CRM_Core_Page {
     $this->assign('context', $context);
 
     $this->assign('iCal', CRM_Event_BAO_Event::getICalLinks($this->_id));
+    $this->assign('isShowICalIconsInline', TRUE);
 
     // Sometimes we want to suppress the Event Full msg
     $noFullMsg = CRM_Utils_Request::retrieve('noFullMsg', 'String', $this, FALSE, 'false');
diff --git a/civicrm/CRM/Event/Page/ManageEvent.php b/civicrm/CRM/Event/Page/ManageEvent.php
index 657b1d037ac50c883c6f6703cf353513d9de14f1..c981862362ef5963652b48c437ef6e06d1b30e59 100644
--- a/civicrm/CRM/Event/Page/ManageEvent.php
+++ b/civicrm/CRM/Event/Page/ManageEvent.php
@@ -227,6 +227,7 @@ class CRM_Event_Page_ManageEvent extends CRM_Core_Page {
     // assign vars to templates
     $this->assign('action', $action);
     $this->assign('iCal', CRM_Event_BAO_Event::getICalLinks());
+    $this->assign('isShowICalIconsInline', FALSE);
     $id = CRM_Utils_Request::retrieve('id', 'Positive',
       $this, FALSE, 0, 'REQUEST'
     );
diff --git a/civicrm/CRM/Import/DataSource/CSV.php b/civicrm/CRM/Import/DataSource/CSV.php
index bc87dcbba4ff05231f5a34cfe941954d1beecf2b..b1f5edf7f55d8840c506ed0758690f7f091ea39e 100644
--- a/civicrm/CRM/Import/DataSource/CSV.php
+++ b/civicrm/CRM/Import/DataSource/CSV.php
@@ -23,7 +23,7 @@ class CRM_Import_DataSource_CSV extends CRM_Import_DataSource {
    *
    * @var string[]
    */
-  protected $submittableFields = ['skipColumnHeader', 'uploadField'];
+  protected $submittableFields = ['skipColumnHeader', 'uploadField', 'fieldSeparator'];
 
   /**
    * Provides information about the data source.
diff --git a/civicrm/CRM/Import/DataSource/SQL.php b/civicrm/CRM/Import/DataSource/SQL.php
index ad6ac14ea095b091336fb28a0cc025f84e4a5632..d9fa015d90b2da4ac282b564a5b20db7c253e176 100644
--- a/civicrm/CRM/Import/DataSource/SQL.php
+++ b/civicrm/CRM/Import/DataSource/SQL.php
@@ -81,7 +81,12 @@ class CRM_Import_DataSource_SQL extends CRM_Import_DataSource {
   public function initialize(): void {
     $table = CRM_Utils_SQL_TempTable::build()->setDurable();
     $tableName = $table->getName();
-    $table->createWithQuery($this->getSubmittedValue('sqlQuery'));
+    try {
+      $table->createWithQuery($this->getSubmittedValue('sqlQuery'));
+    }
+    catch (PEAR_Exception $e) {
+      throw new CRM_Core_Exception($e->getMessage(), 0, ['exception' => $e]);
+    }
 
     // Get the names of the fields to be imported.
     $columnsResult = CRM_Core_DAO::executeQuery(
diff --git a/civicrm/CRM/Import/Form/DataSource.php b/civicrm/CRM/Import/Form/DataSource.php
index 29008b234a030a9da4be318ead32a938a047e5f9..95b1bf21ad2b2f72de9c3d2a862ce631aa05547e 100644
--- a/civicrm/CRM/Import/Form/DataSource.php
+++ b/civicrm/CRM/Import/Form/DataSource.php
@@ -25,6 +25,7 @@ abstract class CRM_Import_Form_DataSource extends CRM_Import_Forms {
    * Set variables up before form is built.
    */
   public function preProcess(): void {
+    $this->pushUrlToUserContext();
     // check for post max size
     CRM_Utils_Number::formatUnitSize(ini_get('post_max_size'), TRUE);
     $this->assign('importEntity', $this->getTranslatedEntity());
@@ -176,7 +177,12 @@ abstract class CRM_Import_Form_DataSource extends CRM_Import_Forms {
       $this->flushDataSource();
       $this->updateUserJobMetadata('submitted_values', $this->getSubmittedValues());
     }
-    $this->instantiateDataSource();
+    try {
+      $this->instantiateDataSource();
+    }
+    catch (CRM_Core_Exception $e) {
+      CRM_Core_Error::statusBounce($e->getMessage());
+    }
   }
 
   /**
diff --git a/civicrm/CRM/Mailing/BAO/Mailing.php b/civicrm/CRM/Mailing/BAO/Mailing.php
index e772bac2701acd2682b2f4e39053d14bf782616d..2ca7e67f9c0747634b7955a203f93b813d750bde 100644
--- a/civicrm/CRM/Mailing/BAO/Mailing.php
+++ b/civicrm/CRM/Mailing/BAO/Mailing.php
@@ -2405,6 +2405,7 @@ LEFT JOIN civicrm_mailing_group g ON g.mailing_id   = m.id
    * @deprecated
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     static::deleteRecord(['id' => $id]);
   }
 
@@ -2436,7 +2437,7 @@ LEFT JOIN civicrm_mailing_group g ON g.mailing_id   = m.id
 
     CRM_Core_Error::deprecatedWarning('This function is deprecated, use CRM_Mailing_BAO_MailingJob::del instead');
 
-    CRM_Mailing_BAO_MailingJob::del($id);
+    CRM_Mailing_BAO_MailingJob::deleteRecord(['id' => $id]);
   }
 
   /**
diff --git a/civicrm/CRM/Mailing/BAO/MailingAB.php b/civicrm/CRM/Mailing/BAO/MailingAB.php
index 73ce87482176132855e9333ee5c9c847fe05acfe..681f90d4ce712ba837d06cb368b171ba9efaa9b9 100644
--- a/civicrm/CRM/Mailing/BAO/MailingAB.php
+++ b/civicrm/CRM/Mailing/BAO/MailingAB.php
@@ -52,6 +52,7 @@ class CRM_Mailing_BAO_MailingAB extends CRM_Mailing_DAO_MailingAB implements \Ci
    * @deprecated
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     static::deleteRecord(['id' => $id]);
   }
 
diff --git a/civicrm/CRM/Mailing/BAO/MailingJob.php b/civicrm/CRM/Mailing/BAO/MailingJob.php
index 2522030c6a30ba254f1f2de2d5457b477d566d8d..d8de39f04ca23a2fab5fc06d1f9721b236a2dacc 100644
--- a/civicrm/CRM/Mailing/BAO/MailingJob.php
+++ b/civicrm/CRM/Mailing/BAO/MailingJob.php
@@ -1102,6 +1102,7 @@ AND    record_type_id = $targetRecordID
    * @return bool
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return (bool) self::deleteRecord(['id' => $id]);
   }
 
diff --git a/civicrm/CRM/Mailing/Form/Approve.php b/civicrm/CRM/Mailing/Form/Approve.php
index 1cd4140b037a38eda4af09fe6d96abf055f5a75b..a777d4f5b00c4f1dd28935bbc6ddc5581800f758 100644
--- a/civicrm/CRM/Mailing/Form/Approve.php
+++ b/civicrm/CRM/Mailing/Form/Approve.php
@@ -155,7 +155,7 @@ class CRM_Mailing_Form_Approve extends CRM_Core_Form {
       $job = new CRM_Mailing_BAO_MailingJob();
       $job->mailing_id = $params['id'];
       while ($job->fetch()) {
-        CRM_Mailing_BAO_MailingJob::del($job->id);
+        CRM_Mailing_BAO_MailingJob::deleteRecord(['id' => $job->id]);
       }
     }
     else {
diff --git a/civicrm/CRM/Mailing/Form/Browse.php b/civicrm/CRM/Mailing/Form/Browse.php
index 9b221b4de85cc7f9ac06539d01172c114fc575b9..a8936bdd951142b2421cd72ae8e5bd6c7f661a46 100644
--- a/civicrm/CRM/Mailing/Form/Browse.php
+++ b/civicrm/CRM/Mailing/Form/Browse.php
@@ -61,7 +61,7 @@ class CRM_Mailing_Form_Browse extends CRM_Core_Form {
 
   public function postProcess() {
     if ($this->_action & CRM_Core_Action::DELETE) {
-      CRM_Mailing_BAO_Mailing::del($this->_mailingId);
+      CRM_Mailing_BAO_Mailing::deleteRecord(['id' => $this->_mailingId]);
       CRM_Core_Session::setStatus(ts('Selected mailing has been deleted.'), ts('Deleted'), 'success');
     }
     elseif ($this->_action & CRM_Core_Action::DISABLE) {
diff --git a/civicrm/CRM/Mailing/Info.php b/civicrm/CRM/Mailing/Info.php
index e30a0dee84f4f2ec3d629da577906e197368571f..bffbd7ca798ef4b1dbb25b0dd92b8ffeb615c621 100644
--- a/civicrm/CRM/Mailing/Info.php
+++ b/civicrm/CRM/Mailing/Info.php
@@ -159,21 +159,7 @@ class CRM_Mailing_Info extends CRM_Core_Component_Info {
    */
   public static function workflowEnabled() {
     $config = CRM_Core_Config::singleton();
-
-    // early exit, since not true for most
-    if (!$config->userSystem->is_drupal ||
-      !function_exists('module_exists')
-    ) {
-      return FALSE;
-    }
-
-    if (!module_exists('rules')) {
-      return FALSE;
-    }
-
-    $enableWorkflow = Civi::settings()->get('civimail_workflow');
-
-    return $enableWorkflow && $config->userSystem->is_drupal;
+    return $config->userSystem->mailingWorkflowIsEnabled();
   }
 
   /**
diff --git a/civicrm/CRM/Mailing/Page/Browse.php b/civicrm/CRM/Mailing/Page/Browse.php
index ff8fde09ee2c46986f456c883a44335760da67bd..501e56e1736fbf2cca3a7e77e7244a3d9ff47cd4 100644
--- a/civicrm/CRM/Mailing/Page/Browse.php
+++ b/civicrm/CRM/Mailing/Page/Browse.php
@@ -199,7 +199,7 @@ class CRM_Mailing_Page_Browse extends CRM_Core_Page {
           CRM_Core_Error::statusBounce(ts('You do not have permission to access this page.'));
         }
 
-        CRM_Mailing_BAO_Mailing::del($this->_mailingId);
+        CRM_Mailing_BAO_Mailing::deleteRecord(['id' => $this->_mailingId]);
         CRM_Core_Session::setStatus(ts('Selected mailing has been deleted.'), ts('Deleted'), 'success');
         CRM_Utils_System::redirect($context);
       }
diff --git a/civicrm/CRM/Member/BAO/MembershipBlock.php b/civicrm/CRM/Member/BAO/MembershipBlock.php
index 6c208c9b4bc63a40f92973a56b61c922e443fddf..d89eae0d2a443cc20e80c0c1a90b2671d95edc98 100644
--- a/civicrm/CRM/Member/BAO/MembershipBlock.php
+++ b/civicrm/CRM/Member/BAO/MembershipBlock.php
@@ -24,6 +24,7 @@ class CRM_Member_BAO_MembershipBlock extends CRM_Member_DAO_MembershipBlock {
    * @return CRM_Member_DAO_MembershipBlock
    */
   public static function create($params) {
+    CRM_Core_Error::deprecatedFunctionWarning('writeRecord');
     return self::writeRecord($params);
   }
 
@@ -35,6 +36,7 @@ class CRM_Member_BAO_MembershipBlock extends CRM_Member_DAO_MembershipBlock {
    * @return bool
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return (bool) self::deleteRecord(['id' => $id]);
   }
 
diff --git a/civicrm/CRM/Member/BAO/MembershipPayment.php b/civicrm/CRM/Member/BAO/MembershipPayment.php
index a87adb5f43f3fd81d6e6912846694db7f83b0c16..ae949586bd014a40a1cac641c271bf8d3b350399 100644
--- a/civicrm/CRM/Member/BAO/MembershipPayment.php
+++ b/civicrm/CRM/Member/BAO/MembershipPayment.php
@@ -81,6 +81,7 @@ class CRM_Member_BAO_MembershipPayment extends CRM_Member_DAO_MembershipPayment
    * @return bool
    */
   public static function del($id) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     return (bool) self::deleteRecord(['id' => $id]);
   }
 
diff --git a/civicrm/CRM/Member/BAO/MembershipStatus.php b/civicrm/CRM/Member/BAO/MembershipStatus.php
index 5f02f8c598e4d30000273f71a689f4cc776d191c..7c791d5812fa630421cc314ddda8cf3dc8d13eaa 100644
--- a/civicrm/CRM/Member/BAO/MembershipStatus.php
+++ b/civicrm/CRM/Member/BAO/MembershipStatus.php
@@ -151,6 +151,7 @@ class CRM_Member_BAO_MembershipStatus extends CRM_Member_DAO_MembershipStatus im
    * @throws CRM_Core_Exception
    */
   public static function del($membershipStatusId) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     static::deleteRecord(['id' => $membershipStatusId]);
   }
 
diff --git a/civicrm/CRM/Member/BAO/MembershipType.php b/civicrm/CRM/Member/BAO/MembershipType.php
index 6d935c9453dd265e699899276a6d543f66eecfce..789eebe74dd1ecc83d50bc03abe32c962b6be380 100644
--- a/civicrm/CRM/Member/BAO/MembershipType.php
+++ b/civicrm/CRM/Member/BAO/MembershipType.php
@@ -61,50 +61,38 @@ class CRM_Member_BAO_MembershipType extends CRM_Member_DAO_MembershipType implem
    *
    * @param array $params
    *   Reference array contains the values submitted by the form.
-   * @param array $ids
-   *   Array contains the id (deprecated).
    *
    * @return \CRM_Member_DAO_MembershipType
    * @throws \CRM_Core_Exception
    */
-  public static function add(&$params, $ids = []) {
-    // DEPRECATED Check if membershipType ID was passed in via $ids
-    if (empty($params['id'])) {
-      if (isset($ids['membershipType'])) {
-        Civi::log()->warning('Deprecated: Passing membershipType by $ids array in CRM_Member_BAO_MembershipType::add');
-      }
-      $params['id'] = $ids['membershipType'] ?? NULL;
-    }
-
+  public static function add(&$params) {
     $hook = empty($params['id']) ? 'create' : 'edit';
-    CRM_Utils_Hook::pre($hook, 'MembershipType', CRM_Utils_Array::value('id', $params), $params);
-
-    $membershipTypeId = $params['id'] ?? NULL;
+    $membershipTypeID = $params['id'] ?? NULL;
+    CRM_Utils_Hook::pre($hook, 'MembershipType', $membershipTypeID, $params);
 
-    if (!$membershipTypeId && !isset($params['domain_id'])) {
+    if (!$membershipTypeID && !isset($params['domain_id'])) {
       $params['domain_id'] = CRM_Core_Config::domainID();
     }
 
     // $previousID is the old organization id for membership type i.e 'member_of_contact_id'. This is used when an organization is changed.
     $previousID = NULL;
-    if ($membershipTypeId) {
-      $previousID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $membershipTypeId, 'member_of_contact_id');
+    if ($membershipTypeID) {
+      $previousID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $membershipTypeID, 'member_of_contact_id');
     }
 
-    // action is taken depending upon the mode
     $membershipType = new CRM_Member_DAO_MembershipType();
     $membershipType->copyValues($params);
     $membershipType->save();
 
-    if ($membershipTypeId) {
+    if ($membershipTypeID) {
       // on update we may need to retrieve some details for the price field function - otherwise we get e-notices on attempts to retrieve
       // name etc - the presence of previous id tells us this is an update
       $params = array_merge(civicrm_api3('membership_type', 'getsingle', ['id' => $membershipType->id]), $params);
     }
     self::createMembershipPriceField($params, $previousID, $membershipType->id);
     // update all price field value for quick config when membership type is set CRM-11718
-    if ($membershipTypeId) {
-      self::updateAllPriceFieldValue($membershipTypeId, $params);
+    if ($membershipTypeID) {
+      self::updateAllPriceFieldValue($membershipTypeID, $params);
     }
 
     CRM_Utils_Hook::post($hook, 'MembershipType', $membershipType->id, $membershipType);
@@ -135,6 +123,7 @@ class CRM_Member_BAO_MembershipType extends CRM_Member_DAO_MembershipType implem
    * @return bool
    */
   public static function del($membershipTypeId) {
+    CRM_Core_Error::deprecatedFunctionWarning('deleteRecord');
     try {
       static::deleteRecord(['id' => $membershipTypeId]);
       return TRUE;
diff --git a/civicrm/CRM/Member/Form/MembershipBlock.php b/civicrm/CRM/Member/Form/MembershipBlock.php
index a7a3abfac8947dd6acebe16e752da211df49526b..60ed1248bb2ec7255c7b95f40db4db190f92a882 100644
--- a/civicrm/CRM/Member/Form/MembershipBlock.php
+++ b/civicrm/CRM/Member/Form/MembershipBlock.php
@@ -151,7 +151,7 @@ class CRM_Member_Form_MembershipBlock extends CRM_Contribute_Form_ContributionPa
       //CRM-15573
       if (!empty($params['id'])) {
         $params['membership_types'] = serialize($membershipRequired);
-        CRM_Member_BAO_MembershipBlock::create($params);
+        CRM_Member_BAO_MembershipBlock::writeRecord($params);
       }
       $this->add('hidden', "mem_price_field_id", '', ['id' => "mem_price_field_id"]);
       $this->assign('is_recur', $isRecur);
diff --git a/civicrm/CRM/Member/Form/MembershipStatus.php b/civicrm/CRM/Member/Form/MembershipStatus.php
index 3791cbeaf39437b9ae84fe045334e87d49324be9..967e81bfd268f481d4f05e64144b72add2ffbe2c 100644
--- a/civicrm/CRM/Member/Form/MembershipStatus.php
+++ b/civicrm/CRM/Member/Form/MembershipStatus.php
@@ -150,7 +150,7 @@ class CRM_Member_Form_MembershipStatus extends CRM_Core_Form {
   public function postProcess() {
     if ($this->_action & CRM_Core_Action::DELETE) {
       try {
-        CRM_Member_BAO_MembershipStatus::del($this->_id);
+        CRM_Member_BAO_MembershipStatus::deleteRecord(['id' => $this->_id]);
       }
       catch (CRM_Core_Exception $e) {
         CRM_Core_Error::statusBounce($e->getMessage(), NULL, ts('Delete Failed'));
diff --git a/civicrm/CRM/Member/Form/MembershipType.php b/civicrm/CRM/Member/Form/MembershipType.php
index da1ee84059b021a45d3291b0df65c245765d3096..686e2f36f9cf6064a24a50663d4703ccf7f64bf3 100644
--- a/civicrm/CRM/Member/Form/MembershipType.php
+++ b/civicrm/CRM/Member/Form/MembershipType.php
@@ -383,7 +383,7 @@ class CRM_Member_Form_MembershipType extends CRM_Member_Form_MembershipConfig {
   public function postProcess() {
     if ($this->_action & CRM_Core_Action::DELETE) {
       try {
-        CRM_Member_BAO_MembershipType::del($this->_id);
+        CRM_Member_BAO_MembershipType::deleteRecord(['id' => $this->_id]);
       }
       catch (CRM_Core_Exception $e) {
         CRM_Core_Error::statusBounce($e->getMessage(), NULL, ts('Membership Type Not Deleted'));
diff --git a/civicrm/CRM/Member/Import/Form/DataSource.php b/civicrm/CRM/Member/Import/Form/DataSource.php
index d8a7da6d5aa1fb434583556ddeb26a4c251bcd88..cd35e7cf6dea30d5c5a1a29863a0d027217f9c32 100644
--- a/civicrm/CRM/Member/Import/Form/DataSource.php
+++ b/civicrm/CRM/Member/Import/Form/DataSource.php
@@ -20,10 +20,6 @@
  */
 class CRM_Member_Import_Form_DataSource extends CRM_Import_Form_DataSource {
 
-  const PATH = 'civicrm/member/import';
-
-  const IMPORT_ENTITY = 'Membership';
-
   /**
    * Get the name of the type to be stored in civicrm_user_job.type_id.
    *
diff --git a/civicrm/CRM/Member/Import/Parser/Membership.php b/civicrm/CRM/Member/Import/Parser/Membership.php
index 92cb027c6da2eff1257aa44667d9ec0ad31b262e..944119a186f42e12d2a91ae7ecf9a67d17f3ea1b 100644
--- a/civicrm/CRM/Member/Import/Parser/Membership.php
+++ b/civicrm/CRM/Member/Import/Parser/Membership.php
@@ -48,12 +48,6 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser {
    */
   protected $_separator;
 
-  /**
-   * Total number of lines in file
-   * @var int
-   */
-  protected $_lineCount;
-
   /**
    * Get information about the provided job.
    *  - name
diff --git a/civicrm/CRM/Pledge/BAO/Pledge.php b/civicrm/CRM/Pledge/BAO/Pledge.php
index 20cc0154300d0807565eaea39d1978ead9c9cb8e..546a9aa29088ffc84bd5e6e7fce615a6e341ce98 100644
--- a/civicrm/CRM/Pledge/BAO/Pledge.php
+++ b/civicrm/CRM/Pledge/BAO/Pledge.php
@@ -870,14 +870,8 @@ SELECT  pledge.contact_id              as contact_id,
     }
 
     if ($sendReminders) {
-      // retrieve domain tokens
-      $tokens = [
-        'domain' => ['name', 'phone', 'address', 'email'],
-        'contact' => CRM_Core_SelectValues::contactTokens(),
-      ];
 
       // retrieve contact tokens
-
       // this function does NOT return Deceased contacts since we don't want to send them email
       $contactDetails = civicrm_api3('Contact', 'get', [
         'is_deceased' => 0,
diff --git a/civicrm/CRM/Pledge/Page/UserDashboard.php b/civicrm/CRM/Pledge/Page/UserDashboard.php
index a30b8bcf9cf2af532253156e537f6042a4cc568f..0c8b74375d19ff68196c699fc47f46443da51274 100644
--- a/civicrm/CRM/Pledge/Page/UserDashboard.php
+++ b/civicrm/CRM/Pledge/Page/UserDashboard.php
@@ -19,7 +19,7 @@ class CRM_Pledge_Page_UserDashboard extends CRM_Contact_Page_View_UserDashBoard
   /**
    * called when action is browse.
    */
-  public function listPledges() {
+  public function listPledges(): void {
     $controller = new CRM_Core_Controller_Simple(
       'CRM_Pledge_Form_Search',
       ts('Pledges'),
@@ -35,25 +35,20 @@ class CRM_Pledge_Page_UserDashboard extends CRM_Contact_Page_View_UserDashBoard
     $controller->process();
     $controller->run();
 
-    // add honor block.
-    $honorParams = [];
+    // Add honor block.
     $honorParams = CRM_Pledge_BAO_Pledge::getHonorContacts($this->_contactId);
-    if (!empty($honorParams)) {
-      // assign vars to templates
-      $this->assign('pledgeHonorRows', $honorParams);
-      $this->assign('pledgeHonor', TRUE);
-    }
-    $session = CRM_Core_Session::singleton();
-    $loggedUserID = $session->get('userID');
-    $this->assign('loggedUserID', $loggedUserID);
+    $this->assign('pledgeHonorRows', $honorParams);
+    $this->assign('pledgeHonor', !empty($honorParams));
+    $this->assign('loggedUserID', CRM_Core_Session::getLoggedInContactID());
   }
 
   /**
-   * the main function that is called when the page
-   * loads, it decides the which action has to be taken for the page.
+   * The main function that is called when the page loads.
+   *
+   * @throws \CRM_Core_Exception
    */
-  public function run() {
-    parent::preProcess();
+  public function run(): void {
+    $this->preProcess();
     $this->listPledges();
   }
 
diff --git a/civicrm/CRM/Report/Form/Contribute/Detail.php b/civicrm/CRM/Report/Form/Contribute/Detail.php
index 70965b4bd5e43df9ba400bfb248d271e51a90503..5ca489a3926b16d845efc71223fd11c7292a85d2 100644
--- a/civicrm/CRM/Report/Form/Contribute/Detail.php
+++ b/civicrm/CRM/Report/Form/Contribute/Detail.php
@@ -255,6 +255,14 @@ class CRM_Report_Form_Contribute_Detail extends CRM_Report_Form {
           ],
           'grouping' => 'contri-fields',
         ],
+        'civicrm_pledge_payment' => [
+          'dao' => 'CRM_Pledge_DAO_PledgePayment',
+          'filters' => [
+            'contribution_id' => [
+              'title' => ts('Contribution is a pledge payment'),
+            ],
+          ],
+        ],
         'civicrm_contribution_soft' => [
           'dao' => 'CRM_Contribute_DAO_ContributionSoft',
           'fields' => [
@@ -986,6 +994,13 @@ WHERE  civicrm_contribution_contribution_id={$row['civicrm_contribution_contribu
     $this->joinAddressFromContact();
     $this->joinEmailFromContact();
 
+    //for pledge payment
+    if ($this->isTableSelected('civicrm_pledge_payment')) {
+      $this->_from .= "
+        LEFT JOIN civicrm_pledge_payment {$this->_aliases['civicrm_pledge_payment']} ON {$this->_aliases['civicrm_contribution']}.id = {$this->_aliases['civicrm_pledge_payment']}.contribution_id
+      ";
+    }
+
     // include contribution note
     if (!empty($this->_params['fields']['contribution_note']) ||
       !empty($this->_params['note_value'])
diff --git a/civicrm/CRM/Report/Form/Contribute/Summary.php b/civicrm/CRM/Report/Form/Contribute/Summary.php
index cb7a89af498b5a43237f793c9e8605c19dfa751e..a9c0877878da71265145c505c475767cf010b46c 100644
--- a/civicrm/CRM/Report/Form/Contribute/Summary.php
+++ b/civicrm/CRM/Report/Form/Contribute/Summary.php
@@ -284,6 +284,14 @@ class CRM_Report_Form_Contribute_Summary extends CRM_Report_Form {
           'batch_id' => ['title' => ts('Batch Title')],
         ],
       ],
+      'civicrm_pledge_payment' => [
+        'dao' => 'CRM_Pledge_DAO_PledgePayment',
+        'filters' => [
+          'contribution_id' => [
+            'title' => ts('Contribution is a pledge payment'),
+          ],
+        ],
+      ],
       'civicrm_contribution_soft' => [
         'dao' => 'CRM_Contribute_DAO_ContributionSoft',
         'fields' => [
@@ -527,6 +535,13 @@ class CRM_Report_Form_Contribute_Summary extends CRM_Report_Form {
     $this->joinPhoneFromContact();
     $this->joinEmailFromContact();
 
+    //for pledge payment
+    if ($this->isTableSelected('civicrm_pledge_payment')) {
+      $this->_from .= "
+        LEFT JOIN civicrm_pledge_payment {$this->_aliases['civicrm_pledge_payment']} ON {$this->_aliases['civicrm_contribution']}.id = {$this->_aliases['civicrm_pledge_payment']}.contribution_id
+      ";
+    }
+
     //for contribution batches
     if ($this->isTableSelected('civicrm_batch')) {
       $this->_from .= "
diff --git a/civicrm/CRM/Report/Form/Mailing/Summary.php b/civicrm/CRM/Report/Form/Mailing/Summary.php
index a5b40355316f5ef78cc2d23ce155ec309c7781ff..61a6424aae4a339b5e6fcb01b3685847b90b22b3 100644
--- a/civicrm/CRM/Report/Form/Mailing/Summary.php
+++ b/civicrm/CRM/Report/Form/Mailing/Summary.php
@@ -334,9 +334,11 @@ class CRM_Report_Form_Mailing_Summary extends CRM_Report_Form {
     foreach ($this->_columns as $tableName => $table) {
       if (array_key_exists('fields', $table)) {
         foreach ($table['fields'] as $fieldName => $field) {
+          if (!empty($field['pseudofield'])) {
+            continue;
+          }
           if (!empty($field['required']) || !empty($this->_params['fields'][$fieldName])) {
-
-            # for statistics
+            // For statistics
             if (!empty($field['statistics'])) {
               switch ($field['statistics']['calc']) {
                 case 'PERCENTAGE':
diff --git a/civicrm/CRM/Upgrade/Incremental/php/FiveSixty.php b/civicrm/CRM/Upgrade/Incremental/php/FiveSixty.php
new file mode 100644
index 0000000000000000000000000000000000000000..6b9a4dedeab33c529ad944b1f3ec1b74c9995157
--- /dev/null
+++ b/civicrm/CRM/Upgrade/Incremental/php/FiveSixty.php
@@ -0,0 +1,75 @@
+<?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 the 5.60.x series.
+ *
+ * Each minor version in the series is handled by either a `5.60.x.mysql.tpl` file,
+ * or a function in this class named `upgrade_5_60_x`.
+ * If only a .tpl file exists for a version, it will be run automatically.
+ * If the function exists, it must explicitly add the 'runSql' task if there is a corresponding .mysql.tpl.
+ *
+ * This class may also implement `setPreUpgradeMessage()` and `setPostUpgradeMessage()` functions.
+ */
+class CRM_Upgrade_Incremental_php_FiveSixty extends CRM_Upgrade_Incremental_Base {
+
+  /**
+   * Upgrade step; adds tasks including 'runSql'.
+   *
+   * @param string $rev
+   *   The version number matching this function name
+   */
+  public function upgrade_5_60_alpha1($rev): void {
+    $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev);
+    $this->addTask('Add scheduled_reminder_smarty setting', 'addScheduledReminderSmartySetting');
+    $this->addTask('Add column civicrm_custom_field.fk_entity', 'addColumn', 'civicrm_custom_field', 'fk_entity', "varchar(255) DEFAULT NULL COMMENT 'Name of entity being referenced.'");
+    $this->addTask('Add foreign key from civicrm_job_log to civicrm_job', 'addJobLogForeignKey');
+  }
+
+  public function setPostUpgradeMessage(&$postUpgradeMessage, $rev): void {
+    if ($rev === '5.60.alpha1') {
+      $postUpgradeMessage .= '<p>' . ts('You can now choose whether to use Smarty in Scheduled Reminders at <em>Administer >> CiviMail >> CiviMail Component Settings</em>. The setting is disabled by default on new installations but we have enabled it during this upgrade to preserve the existing behavior. More information <a %1>in this lab ticket</a>.', [1 => 'href="https://lab.civicrm.org/dev/core/-/issues/4100" target="_blank"']) . '<p>';
+    }
+  }
+
+  public static function addScheduledReminderSmartySetting(): bool {
+    Civi::settings()->set('scheduled_reminder_smarty', TRUE);
+    return TRUE;
+  }
+
+  /**
+   * Add FK to civicrm_job_log.job_id
+   *
+   * @return bool
+   */
+  public static function addJobLogForeignKey(): bool {
+    // Update the comment for the job_id column
+    $commentQuery = 'ALTER TABLE civicrm_job_log MODIFY COLUMN `job_id` int(10) unsigned DEFAULT NULL COMMENT \'FK to civicrm_job.id\'';
+    CRM_Core_DAO::executeQuery($commentQuery);
+
+    // Set job_id = NULL for any that don't have matching jobs (ie. job was deleted).
+    $updateQuery = 'UPDATE civicrm_job_log job_log LEFT JOIN civicrm_job job ON job.id = job_log.id SET job_id = NULL WHERE job.id IS NULL';
+    CRM_Core_DAO::executeQuery($updateQuery);
+
+    // Add the foreign key
+    if (!CRM_Core_BAO_SchemaHandler::checkFKExists('civicrm_job_log', 'FK_civicrm_job_log_job_id')) {
+      $sql = CRM_Core_BAO_SchemaHandler::buildForeignKeySQL([
+        'fk_table_name' => 'civicrm_job',
+        'fk_field_name' => 'id',
+        'name' => 'job_id',
+        'fk_attributes' => ' ON DELETE SET NULL',
+      ], "\n", " ADD ", 'civicrm_job_log');
+      CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_job_log " . $sql, [], TRUE, NULL, FALSE, FALSE);
+    }
+    return TRUE;
+  }
+
+}
diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.60.alpha1.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.60.alpha1.mysql.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..d1ecc1174da49972dcb100a2b72e5d3d91a6ddee
--- /dev/null
+++ b/civicrm/CRM/Upgrade/Incremental/sql/5.60.alpha1.mysql.tpl
@@ -0,0 +1 @@
+{* file to handle db changes in 5.60.alpha1 during upgrade *}
diff --git a/civicrm/CRM/Upgrade/Snapshot.php b/civicrm/CRM/Upgrade/Snapshot.php
index 941f1fe127c523924922d42a3efec16f434f1338..90b66e418bf09a02f980d2f2eb6e799fda0be534 100644
--- a/civicrm/CRM/Upgrade/Snapshot.php
+++ b/civicrm/CRM/Upgrade/Snapshot.php
@@ -207,12 +207,11 @@ class CRM_Upgrade_Snapshot {
     $query = '
       SELECT TABLE_NAME as tableName
       FROM   INFORMATION_SCHEMA.TABLES
-      WHERE  TABLE_SCHEMA = %1
-      AND TABLE_NAME LIKE %2
+      WHERE  TABLE_SCHEMA = DATABASE()
+      AND TABLE_NAME LIKE %1
     ';
     $tables = CRM_Core_DAO::executeQuery($query, [
-      1 => [$dao->database(), 'String'],
-      2 => ["snap_{$owner}_v%", 'String'],
+      1 => ["snap_{$owner}_v%", 'String'],
     ])->fetchMap('tableName', 'tableName');
 
     $oldTables = array_filter($tables, function($table) use ($owner, $cutoff) {
diff --git a/civicrm/CRM/Utils/Address/BatchUpdate.php b/civicrm/CRM/Utils/Address/BatchUpdate.php
index d26d35941b12f972650377710d741e727c862780..e00460b9900f3aa882f2caf5b06f79f3168abea3 100644
--- a/civicrm/CRM/Utils/Address/BatchUpdate.php
+++ b/civicrm/CRM/Utils/Address/BatchUpdate.php
@@ -134,7 +134,10 @@ class CRM_Utils_Address_BatchUpdate {
       $clause[] = '( a.country_id is not null )';
     }
 
-    $whereClause = implode(' AND ', $clause);
+    $whereClause = '';
+    if (!empty($clause)) {
+      $whereClause = 'WHERE ' . implode(' AND ', $clause);
+    }
 
     $query = "
       SELECT c.id,
@@ -152,7 +155,7 @@ class CRM_Utils_Address_BatchUpdate {
         ON a.country_id = o.id
       LEFT JOIN civicrm_state_province s
         ON a.state_province_id = s.id
-      WHERE {$whereClause}
+      {$whereClause}
       ORDER BY a.id
     ";
 
diff --git a/civicrm/CRM/Utils/Array.php b/civicrm/CRM/Utils/Array.php
index cf03d320b0f851774600f647f62bbdc0fd7119bc..da71c1197120b25a5c2ea76cbda83e57348c21b0 100644
--- a/civicrm/CRM/Utils/Array.php
+++ b/civicrm/CRM/Utils/Array.php
@@ -580,6 +580,10 @@ class CRM_Utils_Array {
       $collator = new Collator($lcMessages . '.utf8');
       $collator->asort($array);
     }
+    elseif (version_compare(PHP_VERSION, '8', '<') && class_exists('Collator')) {
+      $collator = new Collator('en_US.utf8');
+      $collator->asort($array);
+    }
     else {
       // This calls PHP's built-in asort().
       asort($array);
diff --git a/civicrm/CRM/Utils/FakeObject.php b/civicrm/CRM/Utils/FakeObject.php
index a20246fe6b200ecc87f11514c4e31c375de1e637..a5d653f5a6fcab0a3b02df5385c6b9158236ef87 100644
--- a/civicrm/CRM/Utils/FakeObject.php
+++ b/civicrm/CRM/Utils/FakeObject.php
@@ -29,7 +29,12 @@
 class CRM_Utils_FakeObject {
 
   /**
-   * @param $array
+   * @var array
+   */
+  protected $array;
+
+  /**
+   * @param array $array
    */
   public function __construct($array) {
     $this->array = $array;
diff --git a/civicrm/CRM/Utils/Mail/Incoming.php b/civicrm/CRM/Utils/Mail/Incoming.php
index d5911ac0156ed746ecd1dd966a92caf56d4d5a52..86b80dd7335a38f31a585b780eb44ecd84908a11 100644
--- a/civicrm/CRM/Utils/Mail/Incoming.php
+++ b/civicrm/CRM/Utils/Mail/Incoming.php
@@ -400,7 +400,7 @@ class CRM_Utils_Mail_Incoming {
    * @param $mail
    * @param $createContact
    */
-  public static function parseAddress(&$address, &$params, &$subParam, &$mail, $createContact = TRUE) {
+  private static function parseAddress(&$address, &$params, &$subParam, &$mail, $createContact = TRUE) {
     // CRM-9484
     if (empty($address->email)) {
       return;
diff --git a/civicrm/CRM/Utils/Mail/Logger.php b/civicrm/CRM/Utils/Mail/Logger.php
index 7290609c21a1e4894afde4db50e660d4f807f7a1..66e75b704b73c9a59d9be4fc72315d96933e67c5 100644
--- a/civicrm/CRM/Utils/Mail/Logger.php
+++ b/civicrm/CRM/Utils/Mail/Logger.php
@@ -44,7 +44,7 @@ class CRM_Utils_Mail_Logger {
    * @param string[] $headers
    * @param string $message
    */
-  public static function log(&$to, &$headers, &$message) {
+  private static function log($to, $headers, $message) {
     if (is_array($to)) {
       $toString = implode(', ', $to);
       $fileName = $to[0];
@@ -52,7 +52,7 @@ class CRM_Utils_Mail_Logger {
     else {
       $toString = $fileName = $to;
     }
-    $content = "To: " . $toString . "\n";
+    $content = 'To: ' . $toString . "\n";
     foreach ($headers as $key => $val) {
       $content .= "$key: $val\n";
     }
diff --git a/civicrm/CRM/Utils/REST.php b/civicrm/CRM/Utils/REST.php
index 4c6edc9c660656111c4d874a14ba87fd061f7ba5..0500b0d0c9ba3f4a5aeb3e7174ea7b5371b3161d 100644
--- a/civicrm/CRM/Utils/REST.php
+++ b/civicrm/CRM/Utils/REST.php
@@ -193,7 +193,6 @@ class CRM_Utils_REST {
       if ((count($args) != 3) && ($args[1] != 'ping')) {
         return self::error('Unknown function invocation.');
       }
-      $store = NULL;
 
       if ($args[1] == 'ping') {
         return self::ping();
@@ -222,7 +221,7 @@ class CRM_Utils_REST {
     // At this point we know we are not calling ping which does not require authentication.
     // Therefore we now need a valid server key and API key.
     // Check and see if a valid secret API key is provided.
-    $api_key = CRM_Utils_Request::retrieve('api_key', 'String', $store, FALSE, NULL, 'REQUEST');
+    $api_key = CRM_Utils_Request::retrieve('api_key', 'String');
     if (!$api_key || strtolower($api_key) == 'null') {
       return self::error("FATAL: mandatory param 'api_key' (user key) missing");
     }
diff --git a/civicrm/CRM/Utils/Request.php b/civicrm/CRM/Utils/Request.php
index b5f22a3f0f89dc411c46ce95ec0acbd1dfe39937..0c60dd710d7e38af35d7d687c0cffd515ff86b16 100644
--- a/civicrm/CRM/Utils/Request.php
+++ b/civicrm/CRM/Utils/Request.php
@@ -69,7 +69,7 @@ class CRM_Utils_Request {
    *
    * @throws \CRM_Core_Exception
    */
-  public static function retrieve($name, $type, &$store = NULL, $abort = FALSE, $default = NULL, $method = 'REQUEST') {
+  public static function retrieve($name, $type, $store = NULL, $abort = FALSE, $default = NULL, $method = 'REQUEST') {
 
     $value = NULL;
     switch ($method) {
diff --git a/civicrm/CRM/Utils/SQL.php b/civicrm/CRM/Utils/SQL.php
index 1ca298e96ad37d881c25c64cde4aeed53709bb33..981d7cb6e550b8f479cf2d18248e82d83beefd00 100644
--- a/civicrm/CRM/Utils/SQL.php
+++ b/civicrm/CRM/Utils/SQL.php
@@ -135,30 +135,6 @@ class CRM_Utils_SQL {
     return TRUE;
   }
 
-  /**
-   * Does the DB version support mutliple locks per
-   *
-   * https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock
-   *
-   * This is a conservative measure to introduce the change which we expect to deprecate later.
-   *
-   * @todo we only check mariadb & mysql right now but maybe can add percona.
-   */
-  public static function supportsMultipleLocks() {
-    static $isSupportLocks = NULL;
-    if (!isset($isSupportLocks)) {
-      $version = self::getDatabaseVersion();
-      if (stripos($version, 'mariadb') !== FALSE) {
-        $isSupportLocks = version_compare($version, '10.0.2', '>=');
-      }
-      else {
-        $isSupportLocks = version_compare($version, '5.7.5', '>=');
-      }
-    }
-
-    return $isSupportLocks;
-  }
-
   /**
    * Get the version string for the database.
    *
diff --git a/civicrm/CRM/Utils/String.php b/civicrm/CRM/Utils/String.php
index 69f74151a05f7cc96dc65dc35839c2e3a7ab7bee..7cb44d0fc18ce9121d7e0bdf8e4e8b28e4f3bbb9 100644
--- a/civicrm/CRM/Utils/String.php
+++ b/civicrm/CRM/Utils/String.php
@@ -692,18 +692,22 @@ class CRM_Utils_String {
    * @param string $string
    *   E.g. "view all contacts". Syntax: "[prefix:]name".
    * @param string|null $defaultPrefix
+   * @param string $validPrefixPattern
+   *   A regular expression used to determine if a prefix is valid.
+   *   To wit: Prefixes MUST be strictly alphanumeric.
    *
    * @return array
    *   (0 => string|NULL $prefix, 1 => string $value)
    */
-  public static function parsePrefix($delim, $string, $defaultPrefix = NULL) {
+  public static function parsePrefix($delim, $string, $defaultPrefix = NULL, $validPrefixPattern = '/^[A-Za-z0-9]+$/') {
     $pos = strpos($string, $delim);
     if ($pos === FALSE) {
       return [$defaultPrefix, $string];
     }
-    else {
-      return [substr($string, 0, $pos), substr($string, 1 + $pos)];
-    }
+
+    $lhs = substr($string, 0, $pos);
+    $rhs = substr($string, 1 + $pos);
+    return preg_match($validPrefixPattern, $lhs) ? [$lhs, $rhs] : [$defaultPrefix, $string];
   }
 
   /**
diff --git a/civicrm/CRM/Utils/System.php b/civicrm/CRM/Utils/System.php
index 10c0672734db23b13d19b315fa3342c773478a40..a280cf539ae6e7a3bbc59d7e915f0d82f7e28694 100644
--- a/civicrm/CRM/Utils/System.php
+++ b/civicrm/CRM/Utils/System.php
@@ -445,13 +445,14 @@ class CRM_Utils_System {
   }
 
   /**
-   * Called from a template to compose a url.
+   * Compose a URL. This is a wrapper for `url()` which is optimized for use in Smarty.
    *
+   * @see \smarty_function_crmURL()
    * @param array $params
-   *   List of parameters.
-   *
+   *   URL properties. Keys are abbreviated ("p"<=>"path").
+   *   See Smarty doc for full details.
    * @return string
-   *   url
+   *   URL
    */
   public static function crmURL($params) {
     $p = $params['p'] ?? NULL;
@@ -1285,14 +1286,8 @@ class CRM_Utils_System {
    *   IP address of logged in user.
    */
   public static function ipAddress($strictIPV4 = TRUE) {
-    $address = $_SERVER['REMOTE_ADDR'] ?? NULL;
-
     $config = CRM_Core_Config::singleton();
-    if ($config->userSystem->is_drupal && function_exists('ip_address')) {
-      // drupal function handles the server being behind a proxy securely. We still have legacy ipn methods
-      // that reach this point without bootstrapping hence the check that the fn exists
-      $address = ip_address();
-    }
+    $address = $config->userSystem->ipAddress();
 
     // hack for safari
     if ($address == '::1') {
diff --git a/civicrm/CRM/Utils/System/Backdrop.php b/civicrm/CRM/Utils/System/Backdrop.php
index 014144bc0f9290b80823eaff5939fb9b940986cb..2b60e638e7270cdb9d099fcc2d36599143091ab7 100644
--- a/civicrm/CRM/Utils/System/Backdrop.php
+++ b/civicrm/CRM/Utils/System/Backdrop.php
@@ -1116,4 +1116,84 @@ AND    u.status = 1
     ];
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function viewsIntegration(): string {
+    global $databases;
+    $config = CRM_Core_Config::singleton();
+    $text = '';
+    $backdrop_prefix = '';
+    if (isset($databases['default']['default']['prefix'])) {
+      if (is_array($databases['default']['default']['prefix'])) {
+        $backdrop_prefix = $databases['default']['default']['prefix']['default'];
+      }
+      else {
+        $backdrop_prefix = $databases['default']['default']['prefix'];
+      }
+    }
+
+    if ($this->viewsExists() &&
+      (
+        $config->dsn != $config->userFrameworkDSN || !empty($backdrop_prefix)
+      )
+    ) {
+      $text = '<div>' . ts('To enable CiviCRM Views integration, add or update the following item in the <code>settings.php</code> file:') . '</div>';
+
+      $tableNames = CRM_Core_DAO::getTableNames();
+      asort($tableNames);
+
+      $text .= '<pre>$database_prefix = [';
+
+      // Add default prefix.
+      $text .= "\n  'default' => '$backdrop_prefix',";
+      $prefix = $this->getCRMDatabasePrefix();
+      foreach ($tableNames as $tableName) {
+        $text .= "\n  '" . str_pad($tableName . "'", 41) . " => '{$prefix}',";
+      }
+      $text .= "\n];</pre>";
+    }
+
+    return $text;
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function theme(&$content, $print = FALSE, $maintenance = FALSE) {
+    $ret = FALSE;
+
+    if (!$print) {
+      if ($maintenance) {
+        backdrop_set_breadcrumb('');
+        backdrop_maintenance_theme();
+        if ($region = CRM_Core_Region::instance('html-header', FALSE)) {
+          CRM_Utils_System::addHTMLHead($region->render(''));
+        }
+        print theme('maintenance_page', ['content' => $content]);
+        exit();
+      }
+      $ret = TRUE;
+    }
+    $out = $content;
+
+    if ($ret) {
+      return $out;
+    }
+    else {
+      print $out;
+      return NULL;
+    }
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function ipAddress():?string {
+    // Backdrop function handles the server being behind a proxy securely. We
+    // still have legacy ipn methods that reach this point without bootstrapping
+    // hence the check that the fn exists.
+    return function_exists('ip_address') ? ip_address() : ($_SERVER['REMOTE_ADDR'] ?? NULL);
+  }
+
 }
diff --git a/civicrm/CRM/Utils/System/Base.php b/civicrm/CRM/Utils/System/Base.php
index 78e5f646013c61c1eeba2e1765d07cb70e699138..0fe6c55f247c8571cd89e9f8e94a6a61b429129e 100644
--- a/civicrm/CRM/Utils/System/Base.php
+++ b/civicrm/CRM/Utils/System/Base.php
@@ -274,55 +274,8 @@ abstract class CRM_Utils_System_Base {
    * @todo Better to always return, and never print.
    */
   public function theme(&$content, $print = FALSE, $maintenance = FALSE) {
-    $ret = FALSE;
-
-    // TODO: Split up; this was copied verbatim from CiviCRM 4.0's multi-UF theming function
-    // but the parts should be copied into cleaner subclass implementations
-    $config = CRM_Core_Config::singleton();
-    if (
-      $config->userSystem->is_drupal &&
-      function_exists('theme') &&
-      !$print
-    ) {
-      if ($maintenance) {
-        drupal_set_breadcrumb('');
-        drupal_maintenance_theme();
-        if ($region = CRM_Core_Region::instance('html-header', FALSE)) {
-          CRM_Utils_System::addHTMLHead($region->render(''));
-        }
-        print theme('maintenance_page', ['content' => $content]);
-        exit();
-      }
-      // TODO: Figure out why D7 returns but everyone else prints
-      $ret = TRUE;
-    }
-    $out = $content;
-
-    if (
-      !$print &&
-      CRM_Core_Config::singleton()->userFramework == 'WordPress'
-    ) {
-      if (!function_exists('is_admin')) {
-        throw new \Exception('Function "is_admin()" is missing, even though WordPress is the user framework.');
-      }
-      if (!defined('ABSPATH')) {
-        throw new \Exception('Constant "ABSPATH" is not defined, even though WordPress is the user framework.');
-      }
-      if (is_admin()) {
-        require_once ABSPATH . 'wp-admin/admin-header.php';
-      }
-      else {
-        // FIXME: we need to figure out to replace civicrm content on the frontend pages
-      }
-    }
-
-    if ($ret) {
-      return $out;
-    }
-    else {
-      print $out;
-      return NULL;
-    }
+    print $content;
+    return NULL;
   }
 
   /**
@@ -696,58 +649,11 @@ abstract class CRM_Utils_System_Base {
   /**
    * Determine the location of the CiviCRM source tree.
    *
-   * FIXME:
-   *  1. This was pulled out from a bigger function. It should be split
-   *     into even smaller pieces and marked abstract.
-   *  2. This would be easier to compute by a calling a CMS API, but
-   *     for whatever reason we take the hard way.
-   *
    * @return array
    *   - url: string. ex: "http://example.com/sites/all/modules/civicrm"
    *   - path: string. ex: "/var/www/sites/all/modules/civicrm"
    */
-  public function getCiviSourceStorage() {
-    global $civicrm_root;
-    $config = CRM_Core_Config::singleton();
-
-    // Don't use $config->userFrameworkBaseURL; it has garbage on it.
-    // More generally, w shouldn't be using $config here.
-    if (!defined('CIVICRM_UF_BASEURL')) {
-      throw new RuntimeException('Undefined constant: CIVICRM_UF_BASEURL');
-    }
-    $baseURL = CRM_Utils_File::addTrailingSlash(CIVICRM_UF_BASEURL, '/');
-    if (CRM_Utils_System::isSSL()) {
-      $baseURL = str_replace('http://', 'https://', $baseURL);
-    }
-
-    // @todo this function is only called / code is only reached when is_drupal is true - move this to the drupal classes
-    // and don't implement here.
-    if ($this->is_drupal) {
-      // Drupal setting
-      // check and see if we are installed in sites/all (for D5 and above)
-      // we dont use checkURL since drupal generates an error page and throws
-      // the system for a loop on lobo's macosx box
-      // or in modules
-      $cmsPath = $config->userSystem->cmsRootPath();
-      $userFrameworkResourceURL = $baseURL . str_replace("$cmsPath/", '',
-          str_replace('\\', '/', $civicrm_root)
-        );
-
-      $siteName = $config->userSystem->parseDrupalSiteNameFromRoot($civicrm_root);
-      if ($siteName) {
-        $civicrmDirName = trim(basename($civicrm_root));
-        $userFrameworkResourceURL = $baseURL . "sites/$siteName/modules/$civicrmDirName/";
-      }
-    }
-    else {
-      $userFrameworkResourceURL = '';
-    }
-
-    return [
-      'url' => CRM_Utils_File::addTrailingSlash($userFrameworkResourceURL, '/'),
-      'path' => CRM_Utils_File::addTrailingSlash($civicrm_root),
-    ];
-  }
+  abstract public function getCiviSourceStorage():array;
 
   /**
    * Perform any post login activities required by the CMS.
@@ -769,7 +675,7 @@ abstract class CRM_Utils_System_Base {
     $timeZoneOffset = $this->getTimeZoneOffset();
     if ($timeZoneOffset) {
       $sql = "SET time_zone = '$timeZoneOffset'";
-      CRM_Core_DAO::executequery($sql);
+      CRM_Core_DAO::executeQuery($sql);
     }
   }
 
@@ -970,8 +876,9 @@ abstract class CRM_Utils_System_Base {
    * Log error to CMS.
    *
    * @param string $message
+   * @param string|NULL $priority
    */
-  public function logger($message) {
+  public function logger($message, $priority = NULL) {
   }
 
   /**
@@ -1159,4 +1066,70 @@ abstract class CRM_Utils_System_Base {
   public function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') {
   }
 
+  /**
+   * Has CMS users table
+   *
+   * @return bool
+   */
+  public function hasUsersTable():bool {
+    return FALSE;
+  }
+
+  /**
+   * CiviCRM Table prefixes
+   *   To display for CMS integration.
+   *
+   * @return string
+   */
+  public function viewsIntegration():string {
+    return '';
+  }
+
+  /**
+   * Can set base page for CiviCRM
+   *   By default, CiviCRM will generate front-facing pages
+   *   using the home page. This allows a different template
+   *   for CiviCRM pages.
+   * @return bool
+   */
+  public function canSetBasePage():bool {
+    return FALSE;
+  }
+
+  /**
+   * Get the client's IP address.
+   *
+   * @return string
+   *   IP address
+   */
+  public function ipAddress():?string {
+    return $_SERVER['REMOTE_ADDR'] ?? NULL;
+  }
+
+  /**
+   * Check if mailing workflow is enabled
+   *
+   * @return bool
+   */
+  public function mailingWorkflowIsEnabled():bool {
+    return FALSE;
+  }
+
+  /**
+   * Get Contact details from User
+   *   The contact parameters here will be used to create a Contact
+   *   record to match the user record.
+   *
+   * @param array $uf_match
+   *   Array of user object, unique ID.
+   * @return array
+   *   Array of contact parameters.
+   */
+  public function getContactDetailsFromUser($uf_match):array {
+    $contactParameters = [];
+    $contactParameters['email'] = $uf_match['user']->email;
+
+    return $contactParameters;
+  }
+
 }
diff --git a/civicrm/CRM/Utils/System/Drupal.php b/civicrm/CRM/Utils/System/Drupal.php
index ee2c36eaad077a6a0aef7389139ae913dc3d07da..a645c8b722642f48e6faf7c8bd7dbbcb896b21fd 100644
--- a/civicrm/CRM/Utils/System/Drupal.php
+++ b/civicrm/CRM/Utils/System/Drupal.php
@@ -852,4 +852,83 @@ AND    u.status = 1
     }
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function viewsIntegration(): string {
+    global $databases;
+    $config = CRM_Core_Config::singleton();
+    $text = '';
+    $drupal_prefix = '';
+    if (isset($databases['default']['default']['prefix'])) {
+      if (is_array($databases['default']['default']['prefix'])) {
+        $drupal_prefix = $databases['default']['default']['prefix']['default'];
+      }
+      else {
+        $drupal_prefix = $databases['default']['default']['prefix'];
+      }
+    }
+
+    if ($this->viewsExists() &&
+      (
+        $config->dsn != $config->userFrameworkDSN || !empty($drupal_prefix)
+      )
+    ) {
+      $text = '<div>' . ts('To enable CiviCRM Views integration, add or update the following item in the <code>settings.php</code> file:') . '</div>';
+
+      $tableNames = CRM_Core_DAO::getTableNames();
+      asort($tableNames);
+      $text .= '<pre>$databases[\'default\'][\'default\'][\'prefix\']= [';
+
+      // Add default prefix.
+      $text .= "\n  'default' => '$drupal_prefix',";
+      $prefix = $this->getCRMDatabasePrefix();
+      foreach ($tableNames as $tableName) {
+        $text .= "\n  '" . str_pad($tableName . "'", 41) . " => '{$prefix}',";
+      }
+      $text .= "\n];</pre>";
+    }
+
+    return $text;
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function theme(&$content, $print = FALSE, $maintenance = FALSE) {
+    $ret = FALSE;
+
+    if (!$print) {
+      if ($maintenance) {
+        drupal_set_breadcrumb('');
+        drupal_maintenance_theme();
+        if ($region = CRM_Core_Region::instance('html-header', FALSE)) {
+          CRM_Utils_System::addHTMLHead($region->render(''));
+        }
+        print theme('maintenance_page', ['content' => $content]);
+        exit();
+      }
+      $ret = TRUE;
+    }
+    $out = $content;
+
+    if ($ret) {
+      return $out;
+    }
+    else {
+      print $out;
+      return NULL;
+    }
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function ipAddress():?string {
+    // Drupal function handles the server being behind a proxy securely. We
+    // still have legacy ipn methods that reach this point without bootstrapping
+    // hence the check that the fn exists.
+    return function_exists('ip_address') ? ip_address() : ($_SERVER['REMOTE_ADDR'] ?? NULL);
+  }
+
 }
diff --git a/civicrm/CRM/Utils/System/Drupal8.php b/civicrm/CRM/Utils/System/Drupal8.php
index c65589d53acda2d892c3cb20df6b77b29556ac20..e72f65eeb08c6839f24b86b74c0de439341870d2 100644
--- a/civicrm/CRM/Utils/System/Drupal8.php
+++ b/civicrm/CRM/Utils/System/Drupal8.php
@@ -537,6 +537,15 @@ class CRM_Utils_System_Drupal8 extends CRM_Utils_System_DrupalBase {
     return 'sidebar_first';
   }
 
+  /**
+   * @inheritDoc
+   */
+  public function logger($message, $priority = NULL) {
+    if (CRM_Core_Config::singleton()->userFrameworkLogging) {
+      \Drupal::logger('civicrm')->log($priority ?? \Drupal\Core\Logger\RfcLogLevel::DEBUG, '%message', ['%message' => $message]);
+    }
+  }
+
   /**
    * @inheritDoc
    */
@@ -908,4 +917,41 @@ class CRM_Utils_System_Drupal8 extends CRM_Utils_System_DrupalBase {
     return FALSE;
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function viewsIntegration(): string {
+    return '<p><strong>' . ts('To enable CiviCRM Views integration, install the <a %1>CiviCRM Entity</a> module.', [1 => 'href="https://www.drupal.org/project/civicrm_entity"']) . '</strong></p>';
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function theme(&$content, $print = FALSE, $maintenance = FALSE) {
+    // @todo use Drupal "maintenance page" template and theme during installation
+    // or upgrade.
+    print $content;
+    return NULL;
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function ipAddress():?string {
+    return class_exists('Drupal') ? \Drupal::request()->getClientIp() : ($_SERVER['REMOTE_ADDR'] ?? NULL);
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function mailingWorkflowIsEnabled():bool {
+    if (!\Drupal::moduleHandler()->moduleExists('rules')) {
+      return FALSE;
+    }
+
+    $enableWorkflow = Civi::settings()->get('civimail_workflow');
+
+    return (bool) $enableWorkflow;
+  }
+
 }
diff --git a/civicrm/CRM/Utils/System/DrupalBase.php b/civicrm/CRM/Utils/System/DrupalBase.php
index d3360c2de01f0754773e7028a3b15c5da757667d..a59bf97f6e88facec745c65fc4deee137ed8e01b 100644
--- a/civicrm/CRM/Utils/System/DrupalBase.php
+++ b/civicrm/CRM/Utils/System/DrupalBase.php
@@ -269,9 +269,9 @@ abstract class CRM_Utils_System_DrupalBase extends CRM_Utils_System_Base {
   /**
    * @inheritDoc
    */
-  public function logger($message) {
-    if (CRM_Core_Config::singleton()->userFrameworkLogging && function_exists('watchdog')) {
-      watchdog('civicrm', '%message', ['%message' => $message], NULL, WATCHDOG_DEBUG);
+  public function logger($message, $priority = NULL) {
+    if (CRM_Core_Config::singleton()->userFrameworkLogging) {
+      watchdog('civicrm', '%message', ['%message' => $message], $priority ?? WATCHDOG_DEBUG);
     }
   }
 
@@ -321,6 +321,44 @@ abstract class CRM_Utils_System_DrupalBase extends CRM_Utils_System_Base {
     }
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function getCiviSourceStorage():array {
+    global $civicrm_root;
+    $config = CRM_Core_Config::singleton();
+
+    // Don't use $config->userFrameworkBaseURL; it has garbage on it.
+    // More generally, w shouldn't be using $config here.
+    if (!defined('CIVICRM_UF_BASEURL')) {
+      throw new RuntimeException('Undefined constant: CIVICRM_UF_BASEURL');
+    }
+    $baseURL = CRM_Utils_File::addTrailingSlash(CIVICRM_UF_BASEURL, '/');
+    if (CRM_Utils_System::isSSL()) {
+      $baseURL = str_replace('http://', 'https://', $baseURL);
+    }
+
+    // Check and see if we are installed in sites/all (for D5 and above).
+    // We dont use checkURL since drupal generates an error page and throws
+    // the system for a loop on lobo's macosx box
+    // or in modules.
+    $cmsPath = $config->userSystem->cmsRootPath();
+    $userFrameworkResourceURL = $baseURL . str_replace("$cmsPath/", '',
+        str_replace('\\', '/', $civicrm_root)
+      );
+
+    $siteName = $config->userSystem->parseDrupalSiteNameFromRoot($civicrm_root);
+    if ($siteName) {
+      $civicrmDirName = trim(basename($civicrm_root));
+      $userFrameworkResourceURL = $baseURL . "sites/$siteName/modules/$civicrmDirName/";
+    }
+
+    return [
+      'url' => CRM_Utils_File::addTrailingSlash($userFrameworkResourceURL, '/'),
+      'path' => CRM_Utils_File::addTrailingSlash($civicrm_root),
+    ];
+  }
+
   /**
    * @inheritDoc
    */
@@ -528,6 +566,13 @@ abstract class CRM_Utils_System_DrupalBase extends CRM_Utils_System_Base {
     return $this->url($_GET['q']);
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function hasUsersTable():bool {
+    return TRUE;
+  }
+
   /**
    * Get an array of user details for a contact, containing at minimum the user ID & name.
    *
@@ -756,4 +801,27 @@ abstract class CRM_Utils_System_DrupalBase extends CRM_Utils_System_Base {
     return $emailName;
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function mailingWorkflowIsEnabled():bool {
+    if (!module_exists('rules')) {
+      return FALSE;
+    }
+
+    $enableWorkflow = Civi::settings()->get('civimail_workflow');
+
+    return (bool) $enableWorkflow;
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function getContactDetailsFromUser($uf_match):array {
+    $contactParameters = [];
+    $contactParameters['email'] = $uf_match['uniqId'];
+
+    return $contactParameters;
+  }
+
 }
diff --git a/civicrm/CRM/Utils/System/Joomla.php b/civicrm/CRM/Utils/System/Joomla.php
index f5b2bb007bf297e3941f2c3c42c4081cad1e3b29..d91cc5f7f7bb2f4d4c2364de3ab7000dd8be5673 100644
--- a/civicrm/CRM/Utils/System/Joomla.php
+++ b/civicrm/CRM/Utils/System/Joomla.php
@@ -968,7 +968,7 @@ class CRM_Utils_System_Joomla extends CRM_Utils_System_Base {
    *   - url: string. ex: "http://example.com/sites/all/modules/civicrm"
    *   - path: string. ex: "/var/www/sites/all/modules/civicrm"
    */
-  public function getCiviSourceStorage() {
+  public function getCiviSourceStorage():array {
     global $civicrm_root;
     if (!defined('CIVICRM_UF_BASEURL')) {
       throw new RuntimeException('Undefined constant: CIVICRM_UF_BASEURL');
@@ -1017,4 +1017,18 @@ class CRM_Utils_System_Joomla extends CRM_Utils_System_Base {
     ];
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function getContactDetailsFromUser($uf_match):array {
+    $contactParameters = [];
+    $user = $uf_match['user'];
+    $contactParameters['email'] = $user->email;
+    if ($user->name) {
+      CRM_Utils_String::extractName($user->name, $contactParameters);
+    }
+
+    return $contactParameters;
+  }
+
 }
diff --git a/civicrm/CRM/Utils/System/Soap.php b/civicrm/CRM/Utils/System/Soap.php
index 4c9c6335071a28992ecd291c840f5a8bffa95bae..28242abec16b98fbbedabedfcfeed04f25f5ed0d 100644
--- a/civicrm/CRM/Utils/System/Soap.php
+++ b/civicrm/CRM/Utils/System/Soap.php
@@ -122,4 +122,20 @@ class CRM_Utils_System_Soap extends CRM_Utils_System_Base {
     throw new \RuntimeException("Not implemented");
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function getCiviSourceStorage():array {
+    global $civicrm_root;
+
+    if (!defined('CIVICRM_UF_BASEURL')) {
+      throw new RuntimeException('Undefined constant: CIVICRM_UF_BASEURL');
+    }
+
+    return [
+      'url' => CRM_Utils_File::addTrailingSlash('', '/'),
+      'path' => CRM_Utils_File::addTrailingSlash($civicrm_root),
+    ];
+  }
+
 }
diff --git a/civicrm/CRM/Utils/System/Standalone.php b/civicrm/CRM/Utils/System/Standalone.php
index c973ac838139ef04e69549317027d6b2ae48bb82..e9b7355a2837f6494831ae1fcaea74fec3959a3b 100644
--- a/civicrm/CRM/Utils/System/Standalone.php
+++ b/civicrm/CRM/Utils/System/Standalone.php
@@ -301,6 +301,22 @@ class CRM_Utils_System_Standalone extends CRM_Utils_System_Base {
     return TRUE;
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function getCiviSourceStorage(): array {
+    global $civicrm_root;
+
+    if (!defined('CIVICRM_UF_BASEURL')) {
+      throw new RuntimeException('Undefined constant: CIVICRM_UF_BASEURL');
+    }
+
+    return [
+      'url' => CRM_Utils_File::addTrailingSlash('', '/'),
+      'path' => CRM_Utils_File::addTrailingSlash($civicrm_root),
+    ];
+  }
+
   /**
    * Determine the location of the CMS root.
    *
diff --git a/civicrm/CRM/Utils/System/UnitTests.php b/civicrm/CRM/Utils/System/UnitTests.php
index 868367da48571d9aa87ba5f7444ac1c0fa636ebf..089d4e0325cde6e62618c4b889c1c5ce93087dc2 100644
--- a/civicrm/CRM/Utils/System/UnitTests.php
+++ b/civicrm/CRM/Utils/System/UnitTests.php
@@ -60,6 +60,22 @@ class CRM_Utils_System_UnitTests extends CRM_Utils_System_Base {
     return TRUE;
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function getCiviSourceStorage(): array {
+    global $civicrm_root;
+
+    if (!defined('CIVICRM_UF_BASEURL')) {
+      throw new RuntimeException('Undefined constant: CIVICRM_UF_BASEURL');
+    }
+
+    return [
+      'url' => CRM_Utils_File::addTrailingSlash('', '/'),
+      'path' => CRM_Utils_File::addTrailingSlash($civicrm_root),
+    ];
+  }
+
   /**
    * @inheritDoc
    */
diff --git a/civicrm/CRM/Utils/System/WordPress.php b/civicrm/CRM/Utils/System/WordPress.php
index 2856631fc8413d63c311e827d9b3bdd5bbd8264d..4d20f8949525e2cbd9afdc32c229e9aa9eaa26eb 100644
--- a/civicrm/CRM/Utils/System/WordPress.php
+++ b/civicrm/CRM/Utils/System/WordPress.php
@@ -200,7 +200,7 @@ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base {
    *   - url: string. ex: "http://example.com/sites/all/modules/civicrm"
    *   - path: string. ex: "/var/www/sites/all/modules/civicrm"
    */
-  public function getCiviSourceStorage() {
+  public function getCiviSourceStorage():array {
     global $civicrm_root;
 
     // Don't use $config->userFrameworkBaseURL; it has garbage on it.
@@ -1624,4 +1624,52 @@ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base {
 
   }
 
+  /**
+   * @inheritdoc
+   */
+  public function canSetBasePage():bool {
+    return TRUE;
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function theme(&$content, $print = FALSE, $maintenance = FALSE) {
+    if (!$print) {
+      if (!function_exists('is_admin')) {
+        throw new \Exception('Function "is_admin()" is missing, even though WordPress is the user framework.');
+      }
+      if (!defined('ABSPATH')) {
+        throw new \Exception('Constant "ABSPATH" is not defined, even though WordPress is the user framework.');
+      }
+      if (is_admin()) {
+        require_once ABSPATH . 'wp-admin/admin-header.php';
+      }
+      else {
+        // FIXME: we need to figure out to replace civicrm content on the frontend pages
+      }
+    }
+
+    print $content;
+    return NULL;
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function getContactDetailsFromUser($uf_match):array {
+    $contactParameters = [];
+
+    $user = $uf_match['user'];
+    $contactParameters['email'] = $user->user_email;
+    if ($user->first_name) {
+      $contactParameters['first_name'] = $user->first_name;
+    }
+    if ($user->last_name) {
+      $contactParameters['last_name'] = $user->last_name;
+    }
+
+    return $contactParameters;
+  }
+
 }
diff --git a/civicrm/CRM/Utils/Token.php b/civicrm/CRM/Utils/Token.php
index ac9ae2de9d167ed4cd417f512288f2160c607232..f29df131b7342ee86d26a91e539b233360cc3270 100644
--- a/civicrm/CRM/Utils/Token.php
+++ b/civicrm/CRM/Utils/Token.php
@@ -683,6 +683,7 @@ class CRM_Utils_Token {
     $html = FALSE,
     $escapeSmarty = FALSE
   ) {
+    CRM_Core_Error::deprecatedFunctionWarning('token processor');
     if (!$categories) {
       $categories = self::getTokenCategories();
     }
@@ -722,6 +723,7 @@ class CRM_Utils_Token {
    * @deprecated
    */
   public static function parseThroughSmarty($tokenHtml, $entity, $entityType = 'contact') {
+    CRM_Core_Error::deprecatedFunctionWarning('no replacement');
     if (defined('CIVICRM_MAIL_SMARTY') && CIVICRM_MAIL_SMARTY) {
       $smarty = CRM_Core_Smarty::singleton();
       // also add the tokens to the template
@@ -1197,6 +1199,7 @@ class CRM_Utils_Token {
    * @deprecated
    */
   public static function getMembershipTokenDetails($membershipIDs) {
+    CRM_Core_Error::deprecatedFunctionWarning('token processor');
     $memberships = civicrm_api3('membership', 'get', [
       'options' => ['limit' => 0],
       'membership_id' => ['IN' => (array) $membershipIDs],
@@ -1389,6 +1392,7 @@ class CRM_Utils_Token {
    *   string with replacements made
    */
   public static function replaceEntityTokens($entity, $entityArray, $str, $knownTokens = [], $escapeSmarty = FALSE) {
+    CRM_Core_Error::deprecatedFunctionWarning('token processor');
     if (!$knownTokens || empty($knownTokens[$entity])) {
       return $str;
     }
@@ -1720,12 +1724,15 @@ class CRM_Utils_Token {
   }
 
   /**
+   * @deprecated
+   *
    * @param $value
    * @param $token
    *
    * @return bool|int|mixed|string|null
    */
   protected static function convertPseudoConstantsUsingMetadata($value, $token) {
+    CRM_Core_Error::deprecatedFunctionWarning('token processor');
     // Convert pseudoconstants using metadata
     if ($value && is_numeric($value)) {
       $allFields = CRM_Contact_BAO_Contact::exportableFields('All');
diff --git a/civicrm/Civi/ActionSchedule/Mapping.php b/civicrm/Civi/ActionSchedule/Mapping.php
index 6032dcf103185238e58e6dffdf2fe63f745679ad..2c092ddcea2e1f22619be7db95f8e6af456920f5 100644
--- a/civicrm/Civi/ActionSchedule/Mapping.php
+++ b/civicrm/Civi/ActionSchedule/Mapping.php
@@ -275,17 +275,15 @@ abstract class Mapping implements MappingInterface {
       asort($valueLabelMap['activity_type']);
 
       $valueLabelMap['activity_status'] = \CRM_Core_PseudoConstant::activityStatus();
-      if (\CRM_Core_Component::isEnabled('CiviEvent')) {
-        $valueLabelMap['event_type'] = \CRM_Event_PseudoConstant::eventType();
-        $valueLabelMap['civicrm_event'] = \CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )");
-        $valueLabelMap['civicrm_participant_status_type'] = \CRM_Event_PseudoConstant::participantStatus(NULL, NULL, 'label');
-        $valueLabelMap['event_template'] = \Civi\Api4\Event::get(FALSE)
-          ->addWhere('is_template', '=', TRUE)
-          ->addWhere('is_active', '=', TRUE)
-          ->execute()
-          ->indexBy('id')
-          ->column('template_title');
-      }
+      $valueLabelMap['event_type'] = \CRM_Event_PseudoConstant::eventType();
+      $valueLabelMap['civicrm_event'] = \CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )");
+      $valueLabelMap['civicrm_participant_status_type'] = \CRM_Event_PseudoConstant::participantStatus(NULL, NULL, 'label');
+      $valueLabelMap['event_template'] = \Civi\Api4\Event::get(FALSE)
+        ->addWhere('is_template', '=', TRUE)
+        ->addWhere('is_active', '=', TRUE)
+        ->execute()
+        ->indexBy('id')
+        ->column('template_title');
       $valueLabelMap['auto_renew_options'] = \CRM_Core_OptionGroup::values('auto_renew_options');
       $valueLabelMap['contact_date_reminder_options'] = \CRM_Core_OptionGroup::values('contact_date_reminder_options');
       $valueLabelMap['civicrm_membership_type'] = \CRM_Member_PseudoConstant::membershipType();
diff --git a/civicrm/Civi/Angular/Coder.php b/civicrm/Civi/Angular/Coder.php
index a1aa0802387edd981dff30f830cc50a4bf9d0339..9799e9659454b546bca44a075db1b409658195cb 100644
--- a/civicrm/Civi/Angular/Coder.php
+++ b/civicrm/Civi/Angular/Coder.php
@@ -61,8 +61,16 @@ class Coder {
     return $html;
   }
 
+  /**
+   * Angular is not as strict about special characters inside html attributes as the xhtml spec.
+   *
+   * This unescapes everything that angular expects to be unescaped.
+   *
+   * @param $matches
+   * @return string
+   */
   protected function cleanupAttribute($matches) {
-    list ($full, $attr, $lquote, $value, $rquote) = $matches;
+    [$full, $attr, $lquote, $value, $rquote] = $matches;
 
     switch ($attr) {
       case 'href':
@@ -72,7 +80,9 @@ class Coder {
         break;
 
       default:
-        $value = html_entity_decode($value);
+        $value = html_entity_decode($value, ENT_NOQUOTES);
+        $oppositeQuote = $lquote === '"' ? "'" : '"';
+        $value = str_replace(htmlspecialchars($oppositeQuote, ENT_QUOTES), $oppositeQuote, $value);
         break;
     }
 
diff --git a/civicrm/Civi/Api4/Action/Contact/GetDuplicates.php b/civicrm/Civi/Api4/Action/Contact/GetDuplicates.php
index f39298c0034fb888dc4e63093390c75a19095ea5..9829af3b03b4d522724a3526364f8681a6252007 100644
--- a/civicrm/Civi/Api4/Action/Contact/GetDuplicates.php
+++ b/civicrm/Civi/Api4/Action/Contact/GetDuplicates.php
@@ -144,6 +144,7 @@ class GetDuplicates extends \Civi\Api4\Generic\DAOCreateAction {
     $ignore = ['id', 'contact_id', 'is_primary', 'on_hold', 'location_type_id', 'phone_type_id'];
     foreach (['Contact', 'Email', 'Phone', 'Address', 'IM'] as $entity) {
       $entityFields = (array) civicrm_api4($entity, 'getFields', [
+        'checkPermissions' => FALSE,
         'action' => 'create',
         'loadOptions' => $action->getLoadOptions(),
         'where' => [['name', 'NOT IN', $ignore], ['type', 'IN', ['Field', 'Custom']]],
diff --git a/civicrm/Civi/Api4/DedupeException.php b/civicrm/Civi/Api4/DedupeException.php
index 989506cd80b49cc5ace3796d577ddd5e666e1133..8adbeab4acb97fe093d297513e2aa43f389015e0 100644
--- a/civicrm/Civi/Api4/DedupeException.php
+++ b/civicrm/Civi/Api4/DedupeException.php
@@ -15,7 +15,7 @@ namespace Civi\Api4;
  *
  * This api exposes CiviCRM (dedupe) exceptions.
  *
- * @searchable none
+ * @searchable secondary
  * @see https://docs.civicrm.org/user/en/latest/organising-your-data/contacts/
  * @since 5.39
  * @package Civi\Api4
diff --git a/civicrm/Civi/Api4/Generic/AbstractAction.php b/civicrm/Civi/Api4/Generic/AbstractAction.php
index b20c7d9fcbcc3234d571acead59951f414f55717..9d7f3d77ad9c245c264271314c4916604910694d 100644
--- a/civicrm/Civi/Api4/Generic/AbstractAction.php
+++ b/civicrm/Civi/Api4/Generic/AbstractAction.php
@@ -227,7 +227,7 @@ abstract class AbstractAction implements \ArrayAccess {
           return $this->$param;
 
         case 'set':
-          $this->$param = $arguments[0];
+          $this->$param = ReflectionUtils::castTypeSoftly($arguments[0], $this->getParamInfo()[$param] ?? []);
           return $this;
       }
     }
diff --git a/civicrm/Civi/Api4/Generic/AbstractSaveAction.php b/civicrm/Civi/Api4/Generic/AbstractSaveAction.php
index 82fac7e023159dbed16736fda0290be1f2f46da5..e6e6959ff488415fb32b59dd0bba823254b3c51e 100644
--- a/civicrm/Civi/Api4/Generic/AbstractSaveAction.php
+++ b/civicrm/Civi/Api4/Generic/AbstractSaveAction.php
@@ -128,7 +128,7 @@ abstract class AbstractSaveAction extends AbstractAction {
     if (empty($record[$primaryKey]) && !empty($this->match)) {
       $where = [];
       foreach ($record as $key => $val) {
-        if (isset($val) && in_array($key, $this->match, TRUE)) {
+        if (in_array($key, $this->match, TRUE)) {
           if ($val === '' || is_null($val)) {
             // If we want to match empty string we have to match on NULL/''
             $where[] = [$key, 'IS EMPTY'];
diff --git a/civicrm/Civi/Api4/Service/Schema/SchemaMapBuilder.php b/civicrm/Civi/Api4/Service/Schema/SchemaMapBuilder.php
index 7401539fe25e34a4e37aa593b5b67033feecbb05..16de8c026de86814b629baa3cfe9c61b2b188465 100644
--- a/civicrm/Civi/Api4/Service/Schema/SchemaMapBuilder.php
+++ b/civicrm/Civi/Api4/Service/Schema/SchemaMapBuilder.php
@@ -105,9 +105,15 @@ class SchemaMapBuilder extends AutoService {
     if (!$customInfo) {
       return;
     }
+    $select = ['f.name', 'f.data_type', 'f.label', 'f.column_name', 'f.option_group_id', 'f.serialize', 'f.fk_entity'];
+    // Prevent errors during upgrade by only selecting fields supported by the current version
+    $supportedFields = \CRM_Utils_Array::prefixKeys(\CRM_Core_BAO_CustomField::getSupportedFields(), 'f.');
+    $select = array_intersect($select, array_keys($supportedFields));
+    // Also select fields from the custom_group table (these fields are so old we don't have to worry about upgrade issues)
+    $select = array_merge(['g.name as custom_group_name', 'g.table_name', 'g.is_multiple'], $select);
     $fieldData = \CRM_Utils_SQL_Select::from('civicrm_custom_field f')
       ->join('custom_group', 'INNER JOIN civicrm_custom_group g ON g.id = f.custom_group_id')
-      ->select(['g.name as custom_group_name', 'g.table_name', 'g.is_multiple', 'f.name', 'f.data_type', 'label', 'column_name', 'option_group_id', 'serialize'])
+      ->select($select)
       ->where('g.extends IN (@entity)', ['@entity' => $customInfo['extends']])
       ->where('g.is_active')
       ->where('f.is_active')
@@ -136,6 +142,12 @@ class SchemaMapBuilder extends AutoService {
         $customTable->addTableLink('entity_id', $joinable);
       }
 
+      if ($fieldData->data_type === 'EntityReference' && isset($fieldData->fk_entity)) {
+        $targetTable = AllCoreTables::getTableForEntityName($fieldData->fk_entity);
+        $joinable = new Joinable($targetTable, 'id', $fieldData->name);
+        $customTable->addTableLink($fieldData->column_name, $joinable);
+      }
+
       if ($fieldData->data_type === 'ContactReference') {
         $joinable = new Joinable('civicrm_contact', 'id', $fieldData->name);
         if ($fieldData->serialize) {
diff --git a/civicrm/Civi/Api4/Service/Spec/CustomFieldSpec.php b/civicrm/Civi/Api4/Service/Spec/CustomFieldSpec.php
index 1d5b8fe5fb53c226ceb63a8f8f18fc353a9ec710..803036a3369302424366a96edb7a2f9cf8b2fbc5 100644
--- a/civicrm/Civi/Api4/Service/Spec/CustomFieldSpec.php
+++ b/civicrm/Civi/Api4/Service/Spec/CustomFieldSpec.php
@@ -38,6 +38,10 @@ class CustomFieldSpec extends FieldSpec {
         $dataType = 'Integer';
         break;
 
+      case 'EntityReference':
+        $dataType = 'Integer';
+        break;
+
       case 'File':
       case 'StateProvince':
       case 'Country':
diff --git a/civicrm/Civi/Api4/Service/Spec/Provider/IsCurrentFieldSpecProvider.php b/civicrm/Civi/Api4/Service/Spec/Provider/IsCurrentFieldSpecProvider.php
index c031c6c2f91c18e2b5042a91adf4fa05a5d84f5e..29ce637ad8f33c638f17abd2cdc7f7b16fda3790 100644
--- a/civicrm/Civi/Api4/Service/Spec/Provider/IsCurrentFieldSpecProvider.php
+++ b/civicrm/Civi/Api4/Service/Spec/Provider/IsCurrentFieldSpecProvider.php
@@ -31,7 +31,7 @@ class IsCurrentFieldSpecProvider extends \Civi\Core\Service\AutoService implemen
   /**
    * @param \Civi\Api4\Service\Spec\RequestSpec $spec
    */
-  public function modifySpec(RequestSpec $spec) {
+  public function modifySpec(RequestSpec $spec): void {
     $field = new FieldSpec('is_current', $spec->getEntity(), 'Boolean');
     $field->setLabel(ts('Is Current'))
       ->setTitle(ts('Current'))
@@ -39,27 +39,42 @@ class IsCurrentFieldSpecProvider extends \Civi\Core\Service\AutoService implemen
       ->setColumnName('is_current')
       ->setDescription(ts('Is active with a non-past end-date'))
       ->setType('Extra')
-      ->setSqlRenderer([__CLASS__, 'renderIsCurrentSql']);
+      ->setSqlRenderer([__CLASS__, $this->getRenderer($field->getEntity())]);
     $spec->addFieldSpec($field);
   }
 
+  /**
+   * Get the function to render the sql.
+   *
+   * @param string $entity
+   *
+   * @return string
+   */
+  private function getRenderer(string $entity): string {
+    if (in_array($entity, ['UserJob', 'SavedSearch'])) {
+      return 'renderNonExpiredSql';
+    }
+    return 'renderIsCurrentSql';
+  }
+
   /**
    * @param string $entity
    * @param string $action
    *
    * @return bool
    */
-  public function applies($entity, $action) {
+  public function applies($entity, $action): bool {
     if ($action !== 'get') {
       return FALSE;
     }
     // TODO: If we wanted this to not be a hard-coded list, we could always return TRUE here
     // and then in the `modifySpec` function check for the 3 fields `is_active`, `start_date`, and `end_date`
-    return in_array($entity, ['Relationship', 'RelationshipCache', 'Event', 'Campaign'], TRUE);
+    return in_array($entity, ['Relationship', 'RelationshipCache', 'Event', 'Campaign', 'SavedSearch', 'UserJob'], TRUE);
   }
 
   /**
    * @param array $field
+   *
    * return string
    */
   public static function renderIsCurrentSql(array $field): string {
@@ -71,4 +86,17 @@ class IsCurrentFieldSpecProvider extends \Civi\Core\Service\AutoService implemen
     return "IF($isActive = 1 AND ($startDate <= '$todayStart' OR $startDate IS NULL) AND ($endDate >= '$todayEnd' OR $endDate IS NULL), '1', '0')";
   }
 
+  /**
+   * Render the sql clause to filter on expires date.
+   *
+   * @param array $field
+   *
+   * return string
+   */
+  public static function renderNonExpiredSql(array $field): string {
+    $endDate = substr_replace($field['sql_name'], 'expires_date', -11, -1);
+    $todayEnd = date('Ymd');
+    return "IF($endDate >= '$todayEnd' OR $endDate IS NULL, 1, 0)";
+  }
+
 }
diff --git a/civicrm/Civi/Api4/Service/Spec/RequestSpec.php b/civicrm/Civi/Api4/Service/Spec/RequestSpec.php
index ac711c6aa8baa13575110ab544e66fec4c51170a..64c259b3cf680732d013c2ec779a735824a71912 100644
--- a/civicrm/Civi/Api4/Service/Spec/RequestSpec.php
+++ b/civicrm/Civi/Api4/Service/Spec/RequestSpec.php
@@ -53,7 +53,7 @@ class RequestSpec implements \Iterator {
    * @param string $action
    * @param array $values
    */
-  public function __construct($entity, $action, $values = []) {
+  public function __construct(string $entity, string $action, array $values = []) {
     $this->entity = $entity;
     $this->action = $action;
     $this->entityTableName = CoreUtil::getTableName($entity);
diff --git a/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php b/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php
index cfdfec55868778a55bc2781bd4243ea8afda75c6..4f32c107729901cc28eb8bb6207a51ea8f2d4191 100644
--- a/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php
+++ b/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php
@@ -37,6 +37,9 @@ class SpecFormatter {
         $field->setType('Field');
         $field->setTableName($data['custom_group_id.table_name']);
       }
+      if ($dataTypeName === 'EntityReference') {
+        $field->setFkEntity($data['fk_entity']);
+      }
       $field->setColumnName($data['column_name']);
       $field->setNullable(empty($data['is_required']));
       $field->setCustomFieldId($data['id'] ?? NULL);
@@ -45,7 +48,7 @@ class SpecFormatter {
       $field->setLabel($data['custom_group_id.title'] . ': ' . $data['label']);
       $field->setHelpPre($data['help_pre'] ?? NULL);
       $field->setHelpPost($data['help_post'] ?? NULL);
-      if (self::customFieldHasOptions($data)) {
+      if (\CRM_Core_BAO_CustomField::hasOptions($data)) {
         $field->setOptionsCallback([__CLASS__, 'getOptions']);
         $suffixes = ['label'];
         if (!empty($data['option_group_id'])) {
@@ -100,28 +103,6 @@ class SpecFormatter {
     return $field;
   }
 
-  /**
-   * Does this custom field have options
-   *
-   * @param array $field
-   * @return bool
-   */
-  private static function customFieldHasOptions($field) {
-    // This will include boolean fields with Yes/No options.
-    if (in_array($field['html_type'], ['Radio', 'CheckBox'])) {
-      return TRUE;
-    }
-    // Do this before the "Select" string search because date fields have a "Select Date" html_type
-    // and contactRef fields have an "Autocomplete-Select" html_type - contacts are an FK not an option list.
-    if (in_array($field['data_type'], ['ContactReference', 'Date'])) {
-      return FALSE;
-    }
-    if (strpos($field['html_type'], 'Select') !== FALSE) {
-      return TRUE;
-    }
-    return !empty($field['option_group_id']);
-  }
-
   /**
    * Get the data type from an array. Defaults to 'data_type' with fallback to
    * mapping for the integer value 'type'
@@ -293,7 +274,7 @@ class SpecFormatter {
       'Link' => 'Url',
     ];
     $inputType = $map[$inputType] ?? $inputType;
-    if ($dataTypeName === 'ContactReference') {
+    if ($dataTypeName === 'ContactReference' || $dataTypeName === 'EntityReference') {
       $inputType = 'EntityRef';
     }
     if (in_array($inputType, ['Select', 'EntityRef'], TRUE) && !empty($data['serialize'])) {
diff --git a/civicrm/Civi/Api4/Service/Spec/SpecGatherer.php b/civicrm/Civi/Api4/Service/Spec/SpecGatherer.php
index 849f90b75857a66dc3a2fc394f3aaed9722d08ae..9433a1b11ede1dff9bee20b26a4be96cccffa122 100644
--- a/civicrm/Civi/Api4/Service/Spec/SpecGatherer.php
+++ b/civicrm/Civi/Api4/Service/Spec/SpecGatherer.php
@@ -32,7 +32,6 @@ class SpecGatherer extends AutoService {
   /**
    * Returns a RequestSpec with all the fields available. Uses spec providers
    * to add or modify field specifications.
-   * @see \Civi\Api4\Service\Spec\Provider\CustomFieldCreationSpecProvider
    *
    * @param string $entity
    * @param string $action
@@ -40,8 +39,11 @@ class SpecGatherer extends AutoService {
    * @param array $values
    *
    * @return \Civi\Api4\Service\Spec\RequestSpec
+   * @throws \CRM_Core_Exception
+   * @see \Civi\Api4\Service\Spec\Provider\CustomFieldCreationSpecProvider
+   *
    */
-  public function getSpec($entity, $action, $includeCustom, $values = []) {
+  public function getSpec(string $entity, string $action, bool $includeCustom, array $values = []): RequestSpec {
     $specification = new RequestSpec($entity, $action, $values);
 
     // Real entities
diff --git a/civicrm/Civi/Api4/Utils/CoreUtil.php b/civicrm/Civi/Api4/Utils/CoreUtil.php
index 630c9e7137dddf91bcba6721f9a8e62e3c15a3a1..a50543de89fd88b1b0723ef5a49aaa325675b74d 100644
--- a/civicrm/Civi/Api4/Utils/CoreUtil.php
+++ b/civicrm/Civi/Api4/Utils/CoreUtil.php
@@ -74,7 +74,7 @@ class CoreUtil {
    *
    * @return string
    */
-  public static function getTableName($entityName) {
+  public static function getTableName(string $entityName) {
     return self::getInfoItem($entityName, 'table_name');
   }
 
diff --git a/civicrm/Civi/Api4/Utils/ReflectionUtils.php b/civicrm/Civi/Api4/Utils/ReflectionUtils.php
index 29e5c4d6c6c4fbe8f303969dcc7b416adc8f7907..409c87db04d55bcb3af96ed4ae9732aebbfa9b50 100644
--- a/civicrm/Civi/Api4/Utils/ReflectionUtils.php
+++ b/civicrm/Civi/Api4/Utils/ReflectionUtils.php
@@ -216,4 +216,56 @@ class ReflectionUtils {
     }
   }
 
+  /**
+   * Cast the $value to the preferred $type (if we're fairly confident).
+   *
+   * This is like PHP's `settype()` but totally not. It only casts in narrow circumstances.
+   * This reflects an opinion that some castings are better than others.
+   *
+   * These will be converted:
+   *
+   *    cast('123', 'int') => 123
+   *    cast('123.4', 'float') => 123.4
+   *    cast('0', 'bool') => FALSE
+   *    cast(1, 'bool') => TRUE
+   *
+   * However, a string like 'hello' will never cast to bool, int, or float -- because that's
+   * a senseless request. We'll leave that to someone else to figure.
+   *
+   * @param mixed $value
+   * @param array $paramInfo
+   * @return mixed
+   *   If the $value is agreeable to casting according to a type-rule from $paramInfo, then
+   *   we return the converted value. Otherwise, return the original value.
+   */
+  public static function castTypeSoftly($value, array $paramInfo) {
+    if (count($paramInfo['type'] ?? []) !== 1) {
+      // I don't know when or why fields can have multiple types. We're just gone leave-be.
+      return $value;
+    }
+
+    switch ($paramInfo['type'][0]) {
+      case 'bool':
+        if (in_array($value, [0, 1, '0', '1'], TRUE)) {
+          return (bool) $value;
+        }
+        break;
+
+      case 'int':
+        if (is_numeric($value)) {
+          return (int) $value;
+        }
+        break;
+
+      case 'float':
+        if (is_numeric($value)) {
+          return (float) $value;
+        }
+        break;
+
+    }
+
+    return $value;
+  }
+
 }
diff --git a/civicrm/Civi/Schema/Traits/DataTypeSpecTrait.php b/civicrm/Civi/Schema/Traits/DataTypeSpecTrait.php
index 0afbef7e269515e23f942a82ae7825dcea21c402..06b38dd82b216e09fc036f599b20b932e71e235b 100644
--- a/civicrm/Civi/Schema/Traits/DataTypeSpecTrait.php
+++ b/civicrm/Civi/Schema/Traits/DataTypeSpecTrait.php
@@ -71,7 +71,7 @@ trait DataTypeSpecTrait {
     }
 
     if (!in_array($dataType, $this->getValidDataTypes())) {
-      throw new \CRM_Core_Exception(sprintf('Invalid data type "%s', $dataType));
+      throw new \CRM_Core_Exception(sprintf('Invalid data type "%s"', $dataType));
     }
 
     $this->dataType = $dataType;
diff --git a/civicrm/Civi/Test/Api3TestTrait.php b/civicrm/Civi/Test/Api3TestTrait.php
index 22e3058cfd4522bb997051d88184de8a9345cd24..49d81e97b43560024fcca2cecd9b55a64c3f7dd0 100644
--- a/civicrm/Civi/Test/Api3TestTrait.php
+++ b/civicrm/Civi/Test/Api3TestTrait.php
@@ -74,16 +74,20 @@ trait Api3TestTrait {
    *   Api result.
    * @param string $prefix
    *   Extra test to add to message.
-   * @param null $expectedError
+   * @param string|null $expectedError
    */
-  public function assertAPIFailure($apiResult, $prefix = '', $expectedError = NULL) {
+  public function assertAPIFailure(array $apiResult, string $prefix = '', ?string $expectedError = NULL): void {
     if (!empty($prefix)) {
       $prefix .= ': ';
     }
     if ($expectedError && !empty($apiResult['is_error'])) {
       $this->assertStringContainsString($expectedError, $apiResult['error_message'], 'api error message not as expected' . $prefix);
     }
-    $this->assertEquals(1, $apiResult['is_error'], "api call should have failed but it succeeded " . $prefix . (print_r($apiResult, TRUE)));
+    if (!$apiResult['is_error']) {
+      // This section only called when it is going to fail - that means we don't have to parse the print_r in the message
+      // if it is not going to be used anyway. It's really helpful for debugging when needed, but potentially expensive otherwise.
+      $this->fail('api call should have failed but it succeeded ' . $prefix . (print_r($apiResult, TRUE)));
+    }
     $this->assertNotEmpty($apiResult['error_message']);
   }
 
diff --git a/civicrm/Civi/Token/TokenException.php b/civicrm/Civi/Token/TokenException.php
index 106c76212898db5dd45c0fd51ddd3c0bc6355745..24ea25ce3362bd8e9d0160f9267fe8551d5a2334 100644
--- a/civicrm/Civi/Token/TokenException.php
+++ b/civicrm/Civi/Token/TokenException.php
@@ -1,6 +1,11 @@
 <?php
 namespace Civi\Token;
 
+/**
+ * @deprecated
+ *
+ * Unused - non-compliant namespace -see https://github.com/civicrm/civicrm-core/pull/25634
+ */
 class TokenException extends \CRM_Core_Exception {
 
 }
diff --git a/civicrm/ang/api4Explorer/Explorer.js b/civicrm/ang/api4Explorer/Explorer.js
index 6fb14a55c6aa2ab9c1cceda715c14f76a44ae883..ee6f2a21cd2a7f007266156dd655741cff22876d 100644
--- a/civicrm/ang/api4Explorer/Explorer.js
+++ b/civicrm/ang/api4Explorer/Explorer.js
@@ -798,6 +798,9 @@
                     code.short += ' +l ' + (params.limit || '0') + (params.offset ? ('@' + params.offset) : '');
                   }
                   break;
+                case (typeof param === 'boolean'):
+                  code.short += ' ' + key + '=' + (param ? 1 : 0);
+                  break;
                 default:
                   code.short += ' ' + key + '=' + (typeof param === 'string' ? cliFormat(param) : cliFormat(JSON.stringify(param)));
               }
diff --git a/civicrm/api/v3/Contribution.php b/civicrm/api/v3/Contribution.php
index 9c00fe34c878d1cc152f0e254360124364cb64df..61483cb9c4ba230183dadf7718a33e0ad417bd80 100644
--- a/civicrm/api/v3/Contribution.php
+++ b/civicrm/api/v3/Contribution.php
@@ -674,60 +674,6 @@ function civicrm_api3_contribution_repeattransaction($params) {
     $params['is_post_payment_create'] ?? NULL);
 }
 
-/**
- * Calls IPN complete transaction for completing or repeating a transaction.
- *
- * The IPN function is overloaded with two purposes - this is simply a wrapper for that
- * when separating them in the api layer.
- *
- * @deprecated
- *
- * @param array $params
- * @param CRM_Contribute_BAO_Contribution $contribution
- * @param array $input
- *
- * @param array $ids
- *
- * @return mixed
- * @throws \CRM_Core_Exception
- */
-function _ipn_process_transaction($params, $contribution, $input, $ids) {
-  CRM_Core_Error::deprecatedFunctionWarning('API3 contribution.completetransaction or contribution.repeattransaction');
-  $objects = $contribution->_relatedObjects;
-  $objects['contribution'] = &$contribution;
-  $input['component'] = $contribution->_component;
-  $input['is_test'] = $contribution->is_test;
-  $input['amount'] = empty($input['total_amount']) ? $contribution->total_amount : $input['total_amount'];
-
-  if (isset($params['is_email_receipt'])) {
-    $input['is_email_receipt'] = $params['is_email_receipt'];
-  }
-  if (!empty($params['trxn_date'])) {
-    $input['trxn_date'] = $params['trxn_date'];
-  }
-  if (!empty($params['receive_date'])) {
-    $input['receive_date'] = $params['receive_date'];
-  }
-  if (empty($contribution->contribution_page_id)) {
-    static $domainFromName;
-    static $domainFromEmail;
-    if (empty($domainFromEmail) && (empty($params['receipt_from_name']) || empty($params['receipt_from_email']))) {
-      [$domainFromName, $domainFromEmail] = CRM_Core_BAO_Domain::getNameAndEmail(TRUE);
-    }
-    $input['receipt_from_name'] = CRM_Utils_Array::value('receipt_from_name', $params, $domainFromName);
-    $input['receipt_from_email'] = CRM_Utils_Array::value('receipt_from_email', $params, $domainFromEmail);
-  }
-  $input['card_type_id'] = $params['card_type_id'] ?? NULL;
-  $input['pan_truncation'] = $params['pan_truncation'] ?? NULL;
-  if (!empty($params['payment_instrument_id'])) {
-    $input['payment_instrument_id'] = $params['payment_instrument_id'];
-  }
-  return CRM_Contribute_BAO_Contribution::completeOrder($input,
-    !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL,
-   $objects['contribution']->id ?? NULL,
-    $params['is_post_payment_create'] ?? NULL);
-}
-
 /**
  * Provide function metadata.
  *
diff --git a/civicrm/api/v3/Event.php b/civicrm/api/v3/Event.php
index bfedf30a9cc4e184ee9a75cf4ccb9db3dc24ac72..750f7c4625db4d26836a4c0ba47999a47ca4949b 100644
--- a/civicrm/api/v3/Event.php
+++ b/civicrm/api/v3/Event.php
@@ -165,9 +165,12 @@ function _civicrm_api3_event_get_legacy_support_42(&$event, $event_id) {
  * @param array $params
  *
  * @return array
+ * @throws \CRM_Core_Exception
+ * @noinspection PhpUnused
  */
-function civicrm_api3_event_delete($params) {
-  return CRM_Event_BAO_Event::del($params['id']) ? civicrm_api3_create_success() : civicrm_api3_create_error(ts('Error while deleting event'));
+function civicrm_api3_event_delete(array $params): array {
+  CRM_Event_BAO_Event::deleteRecord($params);
+  return civicrm_api3_create_success();
 }
 
 /**
diff --git a/civicrm/api/v3/Generic/Getlist.php b/civicrm/api/v3/Generic/Getlist.php
index ec0fb38877dc5966fba320e64a9413c11bac974b..53aa4526e2c1aa2b476dacb39aca080d2f268c6f 100644
--- a/civicrm/api/v3/Generic/Getlist.php
+++ b/civicrm/api/v3/Generic/Getlist.php
@@ -41,7 +41,7 @@ function civicrm_api3_generic_getList($apiRequest) {
 
   $searchResult = _civicrm_api3_generic_getlist_get_result($request, $entity, $meta, $apiRequest);
   $foundIDCount = 0;
-  if ($forceIdSearch && !empty($result['values'])) {
+  if ($forceIdSearch && !empty($result['values']) && isset($idRequest['id'])) {
     $contactSearchID = $idRequest['id'];
     $foundIDCount = 1;
     // Merge id fetch into search result.
@@ -205,7 +205,7 @@ function _civicrm_api3_generic_getList_defaults(string $entity, array &$request,
       // Adding one extra result allows us to see if there are any more
       'limit' => $resultsPerPage + 1,
       // Because sql is zero-based
-      'offset' => ($request['page_num'] - 1) * $resultsPerPage,
+      'offset' => ($request['page_num'] > 1) ? (($request['page_num'] - 1) * $resultsPerPage) : 0,
     ];
   }
 }
diff --git a/civicrm/api/v3/MembershipStatus.php b/civicrm/api/v3/MembershipStatus.php
index c4f6fa5f2ed641a5b115f250876961c7d8ff2eed..036f2afbb20093060948deb5153bbc6bef0ecc1c 100644
--- a/civicrm/api/v3/MembershipStatus.php
+++ b/civicrm/api/v3/MembershipStatus.php
@@ -105,16 +105,13 @@ function civicrm_api3_membership_status_update($params) {
  * This API is used for deleting a membership status
  *
  * @param array $params
+ *
  * @return array
  * @throws CRM_Core_Exception
- * @throws CRM_Core_Exception
+ * @noinspection PhpUnused
  */
-function civicrm_api3_membership_status_delete($params) {
-
-  $memberStatusDelete = CRM_Member_BAO_MembershipStatus::del($params['id'], TRUE);
-  if ($memberStatusDelete) {
-    throw new CRM_Core_Exception($memberStatusDelete['error_message']);
-  }
+function civicrm_api3_membership_status_delete(array $params): array {
+  CRM_Member_BAO_MembershipStatus::deleteRecord($params);
   return civicrm_api3_create_success();
 }
 
diff --git a/civicrm/api/v3/OptionValue.php b/civicrm/api/v3/OptionValue.php
index 34a2b00507fb6e45f9c2feea6def655acb9e9ae3..3c09f146248db39264c4c7db426ba2a34a5fb8ae 100644
--- a/civicrm/api/v3/OptionValue.php
+++ b/civicrm/api/v3/OptionValue.php
@@ -91,7 +91,7 @@ function _civicrm_api3_option_value_create_spec(&$params) {
 function civicrm_api3_option_value_delete($params) {
   // We will get the option group id before deleting so we can flush pseudoconstants.
   $optionGroupID = civicrm_api('option_value', 'getvalue', ['version' => 3, 'id' => $params['id'], 'return' => 'option_group_id']);
-  $result = CRM_Core_BAO_OptionValue::del($params['id']);
+  $result = CRM_Core_BAO_OptionValue::deleteRecord($params);
   if ($result) {
     civicrm_api('option_value', 'getfields', ['version' => 3, 'cache_clear' => 1, 'option_group_id' => $optionGroupID]);
     return civicrm_api3_create_success();
diff --git a/civicrm/api/v3/Profile.php b/civicrm/api/v3/Profile.php
index 78306eb6ecfac3d965aad660dcdfe364d7863880..4c4f78355ebd21838b28befa09c397b29fb4ff67 100644
--- a/civicrm/api/v3/Profile.php
+++ b/civicrm/api/v3/Profile.php
@@ -629,8 +629,19 @@ function _civicrm_api3_order_by_weight($a, $b) {
  */
 function _civicrm_api3_map_profile_fields_to_entity(&$field) {
   $entity = $field['field_type'];
-  $contactTypes = civicrm_api3('contact', 'getoptions', ['field' => 'contact_type']);
-  if (in_array($entity, $contactTypes['values'])) {
+  // let's get the contact types and subtypes so that we can change the entity
+  // of such fields to 'contact'
+  $result = civicrm_api3('ContactType', 'get', [
+    'return' => ["name"],
+    'options' => ['limit' => 0],
+  ]);
+
+  $contactTypes = [];
+  foreach ($result['values'] as $type) {
+    $contactTypes[$type['id']] = $type['name'];
+  }
+
+  if (in_array($entity, $contactTypes)) {
     $entity = 'contact';
   }
   $entity = _civicrm_api_get_entity_name_from_camel($entity);
diff --git a/civicrm/api/v3/utils.php b/civicrm/api/v3/utils.php
index 2a03f8e4a7cb203133f8b033ad8244796ffa16eb..11507b7c0aa2ccbecca8cc4c8fdfdb050e8742dc 100644
--- a/civicrm/api/v3/utils.php
+++ b/civicrm/api/v3/utils.php
@@ -457,25 +457,6 @@ function _civicrm_api3_store_values(array $fields, array $params, &$values): boo
   return $valueFound;
 }
 
-/**
- * Returns field names of the given entity fields.
- *
- * @deprecated
- * @param array $fields
- *   Fields array to retrieve the field names for.
- * @return array
- */
-function _civicrm_api3_field_names($fields) {
-  CRM_Core_Error::deprecatedFunctionWarning('array_column');
-  $result = [];
-  foreach ($fields as $key => $value) {
-    if (!empty($value['name'])) {
-      $result[] = $value['name'];
-    }
-  }
-  return $result;
-}
-
 /**
  * Get function for query object api.
  *
diff --git a/civicrm/civicrm-version.php b/civicrm/civicrm-version.php
index 4b9c6850effd8df192312ba4d33c4768cf879d4e..efb04c7cf3d31cbc190c63118d200cb11620b815 100644
--- a/civicrm/civicrm-version.php
+++ b/civicrm/civicrm-version.php
@@ -1,7 +1,7 @@
 <?php
 /** @deprecated */
 function civicrmVersion( ) {
-  return array( 'version'  => '5.59.4',
+  return array( 'version'  => '5.60.0',
                 'cms'      => 'Wordpress',
                 'revision' => '' );
 }
diff --git a/civicrm/composer.json b/civicrm/composer.json
index c227b14e878b8dbba0967b5f0fbebcaa8a7458b3..e09f2332f52ad74a900378c051061c7b95c31778 100644
--- a/civicrm/composer.json
+++ b/civicrm/composer.json
@@ -52,20 +52,19 @@
   "require": {
     "php": "~7.3 || ~8",
     "composer-runtime-api": "~2.0",
-    "cache/integration-tests": "~0.17.0",
     "dompdf/dompdf" : "~2.0.2",
     "firebase/php-jwt": ">=3 <6",
     "rubobaquero/phpquery": "^0.9.15",
-    "symfony/config": "~4.4",
+    "symfony/config": "~4.4 || ~6.0",
     "symfony/polyfill-iconv": "~1.0",
-    "symfony/dependency-injection": "~4.4",
-    "symfony/event-dispatcher": "~4.4",
-    "symfony/filesystem": "~4.4",
-    "symfony/process": "~4.4",
-    "symfony/var-dumper": "~3.0 || ~4.4 || ~5.1",
-    "symfony/service-contracts": "~2.2",
+    "symfony/dependency-injection": "~4.4 || ~6.0",
+    "symfony/event-dispatcher": "~4.4 || ~6.0",
+    "symfony/filesystem": "~4.4 || ~6.0",
+    "symfony/process": "~4.4 || ~6.0",
+    "symfony/var-dumper": "~3.0 || ~4.4 || ~5.1 || ~6.0",
+    "symfony/service-contracts": "~2.2 || ~3.1",
     "psr/log": "~1.0 || ~2.0 || ~3.0",
-    "symfony/finder": "~4.4",
+    "symfony/finder": "~4.4 || ~6.0",
     "tecnickcom/tcpdf" : "6.4.*",
     "totten/ca-config": "~22.11",
     "zetacomponents/base": "1.9.*",
@@ -104,7 +103,7 @@
     "symfony/polyfill-php81": "^1.0",
     "symfony/polyfill-php82": "^1.0",
     "html2text/html2text": "^4.3.1",
-    "psr/container": "~1.0",
+    "psr/container": "~1.0 || ~2.0",
     "ext-fileinfo": "*"
   },
   "scripts": {
diff --git a/civicrm/composer.lock b/civicrm/composer.lock
index a233b0ac696fee24dafe9ed7b748430001afe991..898bd6d4c66fa940f9c670d185edfa28bae6191e 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": "7a2de17bbccf6b8df7e6fdb1f6d2480e",
+    "content-hash": "18f17bf90f8002419597da6e8c7db3b1",
     "packages": [
         {
             "name": "adrienrn/php-mimetyper",
@@ -167,131 +167,6 @@
             ],
             "time": "2021-10-10T11:59:43+00:00"
         },
-        {
-            "name": "cache/integration-tests",
-            "version": "0.17.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/php-cache/integration-tests.git",
-                "reference": "eda2e6b8bc5abcd623c8047e2345cda38dd6479e"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/php-cache/integration-tests/zipball/eda2e6b8bc5abcd623c8047e2345cda38dd6479e",
-                "reference": "eda2e6b8bc5abcd623c8047e2345cda38dd6479e",
-                "shasum": ""
-            },
-            "require": {
-                "cache/tag-interop": "^1.0",
-                "php": ">=5.5.9",
-                "psr/cache": "~1.0"
-            },
-            "conflict": {
-                "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
-            },
-            "require-dev": {
-                "cache/cache": "^1.0",
-                "illuminate/cache": "^5.4|^5.5|^5.6",
-                "mockery/mockery": "^1.0",
-                "symfony/cache": "^3.4.31|^4.3.4|^5.0",
-                "symfony/phpunit-bridge": "^5.1",
-                "tedivm/stash": "^0.14"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "Cache\\IntegrationTests\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Aaron Scherer",
-                    "email": "aequasi@gmail.com",
-                    "homepage": "https://github.com/aequasi"
-                },
-                {
-                    "name": "Tobias Nyholm",
-                    "email": "tobias.nyholm@gmail.com",
-                    "homepage": "https://github.com/nyholm"
-                }
-            ],
-            "description": "Integration tests for PSR-6 and PSR-16 cache implementations",
-            "homepage": "https://github.com/php-cache/integration-tests",
-            "keywords": [
-                "cache",
-                "psr16",
-                "psr6",
-                "test"
-            ],
-            "support": {
-                "issues": "https://github.com/php-cache/integration-tests/issues",
-                "source": "https://github.com/php-cache/integration-tests/tree/0.17.0"
-            },
-            "time": "2020-11-03T12:52:23+00:00"
-        },
-        {
-            "name": "cache/tag-interop",
-            "version": "1.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/php-cache/tag-interop.git",
-                "reference": "c7496dd81530f538af27b4f2713cde97bc292832"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/php-cache/tag-interop/zipball/c7496dd81530f538af27b4f2713cde97bc292832",
-                "reference": "c7496dd81530f538af27b4f2713cde97bc292832",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.5 || ^7.0",
-                "psr/cache": "^1.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.0-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Cache\\TagInterop\\": ""
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Tobias Nyholm",
-                    "email": "tobias.nyholm@gmail.com",
-                    "homepage": "https://github.com/Nyholm"
-                },
-                {
-                    "name": "Nicolas Grekas",
-                    "email": "p@tchwork.com",
-                    "homepage": "https://github.com/nicolas-grekas"
-                }
-            ],
-            "description": "Framework interoperable interfaces for tags",
-            "homepage": "http://www.php-cache.com/en/latest/",
-            "keywords": [
-                "cache",
-                "psr",
-                "psr6",
-                "tag"
-            ],
-            "support": {
-                "issues": "https://github.com/php-cache/tag-interop/issues",
-                "source": "https://github.com/php-cache/tag-interop/tree/master"
-            },
-            "time": "2017-03-13T09:14:27+00:00"
-        },
         {
             "name": "civicrm/civicrm-cxn-rpc",
             "version": "v0.20.12.01",
@@ -1524,9 +1399,13 @@
             ],
             "support": {
                 "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
-                "source": "https://github.com/maennchen/ZipStream-PHP/tree/master"
+                "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.1.0"
             },
             "funding": [
+                {
+                    "url": "https://github.com/maennchen",
+                    "type": "github"
+                },
                 {
                     "url": "https://opencollective.com/zipstream",
                     "type": "open_collective"
@@ -3008,55 +2887,6 @@
             ],
             "time": "2021-04-06T13:56:45+00:00"
         },
-        {
-            "name": "psr/cache",
-            "version": "1.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/php-fig/cache.git",
-                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
-                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.3.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Psr\\Cache\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
-                }
-            ],
-            "description": "Common interface for caching libraries",
-            "keywords": [
-                "cache",
-                "psr",
-                "psr-6"
-            ],
-            "support": {
-                "source": "https://github.com/php-fig/cache/tree/master"
-            },
-            "time": "2016-08-06T20:24:11+00:00"
-        },
         {
             "name": "psr/container",
             "version": "1.0.0",
diff --git a/civicrm/css/civicrm.css b/civicrm/css/civicrm.css
index e958d97ba0ac8616c9787909440c99f31d79cae0..fa52f86e9c6712c68725e98c4ad210f0047f49df 100644
--- a/civicrm/css/civicrm.css
+++ b/civicrm/css/civicrm.css
@@ -200,6 +200,7 @@ input.crm-form-checkbox + label {
   width: 15em;
 }
 .crm-container .huge,
+input.crm-form-autocomplete,
 input.crm-form-entityref {
   width: 25em;
 }
diff --git a/civicrm/ext/afform/admin/CRM/AfformAdmin/Upgrader.php b/civicrm/ext/afform/admin/CRM/AfformAdmin/Upgrader.php
index 5a9f7ce4633bb03cb7fb1f9452e76542d19536e3..4a0e63fa355ebb29d13fee1e13101b11503768c1 100644
--- a/civicrm/ext/afform/admin/CRM/AfformAdmin/Upgrader.php
+++ b/civicrm/ext/afform/admin/CRM/AfformAdmin/Upgrader.php
@@ -4,7 +4,7 @@ use CRM_AfformAdmin_ExtensionUtil as E;
 /**
  * Collection of upgrade steps.
  */
-class CRM_AfformAdmin_Upgrader extends CRM_AfformAdmin_Upgrader_Base {
+class CRM_AfformAdmin_Upgrader extends CRM_Extension_Upgrader_Base {
 
   /**
    * Obsolete upgrade step, no longer does anything
diff --git a/civicrm/ext/afform/admin/CRM/AfformAdmin/Upgrader/Base.php b/civicrm/ext/afform/admin/CRM/AfformAdmin/Upgrader/Base.php
deleted file mode 100644
index 0e582fabb2bf325ca1319c9f3a0c0604b596a0a8..0000000000000000000000000000000000000000
--- a/civicrm/ext/afform/admin/CRM/AfformAdmin/Upgrader/Base.php
+++ /dev/null
@@ -1,397 +0,0 @@
-<?php
-
-// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
-use CRM_AfformAdmin_ExtensionUtil as E;
-
-/**
- * Base class which provides helpers to execute upgrade logic
- */
-class CRM_AfformAdmin_Upgrader_Base {
-
-  /**
-   * @var CRM_AfformAdmin_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 revisionNumber[]
-   *   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_AfformAdmin_Upgrader(
-        'org.civicrm.afform_admin',
-        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.
-   *
-   * ```
-   * CRM_AfformAdmin_Upgrader_Base::_queueAdapter($ctx, 'methodName', 'arg1', 'arg2');
-   * ```
-   */
-  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_AfformAdmin_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.
-   *
-   * @param CRM_Queue_Queue $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/afform/admin/afform_admin.civix.php b/civicrm/ext/afform/admin/afform_admin.civix.php
index 82e009d93c321469959665cc85ff457734d18d77..52866d86b88fd72c52835f168b50a48f2cec069f 100644
--- a/civicrm/ext/afform/admin/afform_admin.civix.php
+++ b/civicrm/ext/afform/admin/afform_admin.civix.php
@@ -84,27 +84,17 @@ use CRM_AfformAdmin_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _afform_admin_civix_civicrm_config(&$config = NULL) {
+function _afform_admin_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
   }
   $configured = TRUE;
 
-  $template = CRM_Core_Smarty::singleton();
-
   $extRoot = __DIR__ . 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);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -114,35 +104,7 @@ function _afform_admin_civix_civicrm_config(&$config = NULL) {
  */
 function _afform_admin_civix_civicrm_install() {
   _afform_admin_civix_civicrm_config();
-  if ($upgrader = _afform_admin_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _afform_admin_civix_civicrm_postInstall() {
-  _afform_admin_civix_civicrm_config();
-  if ($upgrader = _afform_admin_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 _afform_admin_civix_civicrm_uninstall(): void {
-  _afform_admin_civix_civicrm_config();
-  if ($upgrader = _afform_admin_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -152,56 +114,7 @@ function _afform_admin_civix_civicrm_uninstall(): void {
  */
 function _afform_admin_civix_civicrm_enable(): void {
   _afform_admin_civix_civicrm_config();
-  if ($upgrader = _afform_admin_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 _afform_admin_civix_civicrm_disable(): void {
-  _afform_admin_civix_civicrm_config();
-  if ($upgrader = _afform_admin_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 _afform_admin_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _afform_admin_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_AfformAdmin_Upgrader
- */
-function _afform_admin_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/AfformAdmin/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_AfformAdmin_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -285,14 +198,3 @@ function _afform_admin_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parent
     }
   }
 }
-
-/**
- * (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 _afform_admin_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/afform/admin/afform_admin.php b/civicrm/ext/afform/admin/afform_admin.php
index 61b588aa9733baafe21838f6891cea6b43509171..d4436ac868de6a542257fc6b790c429a36b8f3e9 100644
--- a/civicrm/ext/afform/admin/afform_admin.php
+++ b/civicrm/ext/afform/admin/afform_admin.php
@@ -21,24 +21,6 @@ function afform_admin_civicrm_install() {
   _afform_admin_civix_civicrm_install();
 }
 
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall
- */
-function afform_admin_civicrm_postInstall() {
-  _afform_admin_civix_civicrm_postInstall();
-}
-
-/**
- * Implements hook_civicrm_uninstall().
- *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
- */
-function afform_admin_civicrm_uninstall() {
-  _afform_admin_civix_civicrm_uninstall();
-}
-
 /**
  * Implements hook_civicrm_enable().
  *
@@ -47,32 +29,3 @@ function afform_admin_civicrm_uninstall() {
 function afform_admin_civicrm_enable() {
   _afform_admin_civix_civicrm_enable();
 }
-
-/**
- * Implements hook_civicrm_disable().
- *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
- */
-function afform_admin_civicrm_disable() {
-  _afform_admin_civix_civicrm_disable();
-}
-
-/**
- * Implements hook_civicrm_upgrade().
- *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade
- */
-function afform_admin_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  return _afform_admin_civix_civicrm_upgrade($op, $queue);
-}
-
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_entityTypes
- */
-function afform_admin_civicrm_entityTypes(&$entityTypes) {
-  _afform_admin_civix_civicrm_entityTypes($entityTypes);
-}
diff --git a/civicrm/ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.component.js b/civicrm/ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.component.js
index 06c8648d1a13a2a233c11989d1f04ad5295a8a28..79382e2ba4d71c8207943beae737f1e53c399849 100644
--- a/civicrm/ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.component.js
+++ b/civicrm/ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.component.js
@@ -78,9 +78,10 @@
         }
       };
 
-      this.getSearchDisplay = function(node) {
-        var searchKey = $scope.getSearchKey(node);
-        if (searchKey) {
+      // Finds a SearchDisplay within this container or within the fieldset containing this container
+      this.getSearchDisplay = function() {
+        var searchKey = ctrl.getDataEntity();
+        if (searchKey && !ctrl.entityName) {
           return afGui.getSearchDisplay.apply(null, searchKey.split('.'));
         }
       };
@@ -416,8 +417,7 @@
           var joinType = ctrl.entityName.split('-join-');
           entityType = joinType[1] || (ctrl.editor && ctrl.editor.getEntity(joinType[0]).type);
         } else {
-          var searchKey = ctrl.getDataEntity(),
-            searchDisplay = afGui.getSearchDisplay.apply(null, searchKey.split('.')),
+          var searchDisplay = ctrl.getSearchDisplay(),
             fieldName = fieldKey.substr(fieldKey.indexOf('.') + 1),
             prefix = _.includes(fieldKey, '.') ? fieldKey.split('.')[0] : null;
           if (prefix) {
diff --git a/civicrm/ext/afform/admin/ang/afGuiEditor/elements/afGuiField.component.js b/civicrm/ext/afform/admin/ang/afGuiEditor/elements/afGuiField.component.js
index 1550f94faaf0aa12deb8ba0128d58d18bdc5e031..66136f86f6574d446b0bff755fe57bea057f9db7 100644
--- a/civicrm/ext/afform/admin/ang/afGuiEditor/elements/afGuiField.component.js
+++ b/civicrm/ext/afform/admin/ang/afGuiEditor/elements/afGuiField.component.js
@@ -89,8 +89,8 @@
       this.getDefn = function() {
         var defn = afGui.getField(ctrl.container.getFieldEntityType(ctrl.node.name), ctrl.node.name);
         // Calc fields are specific to a search display, not part of the schema
-        if (!defn && ctrl.container.getSearchDisplay(ctrl.container.node)) {
-          var searchDisplay = ctrl.container.getSearchDisplay(ctrl.container.node);
+        if (!defn && ctrl.container.getSearchDisplay()) {
+          var searchDisplay = ctrl.container.getSearchDisplay();
           defn = _.findWhere(searchDisplay.calc_fields, {name: ctrl.node.name});
         }
         defn = defn || {
diff --git a/civicrm/ext/afform/admin/info.xml b/civicrm/ext/afform/admin/info.xml
index 72a3c53f90abe3559081643a9ac979a8a27b033c..857440b23c4a2424635de3c96d29b054cf067e39 100644
--- a/civicrm/ext/afform/admin/info.xml
+++ b/civicrm/ext/afform/admin/info.xml
@@ -13,10 +13,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-01-09</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>beta</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>FormBuilder provides a UI to administer and edit forms. It is an optional admin tool and not required for the forms to function.</comments>
   <requires>
@@ -25,7 +25,7 @@
   </requires>
   <civix>
     <namespace>CRM/AfformAdmin</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
   <classloader>
     <psr4 prefix="Civi\" path="Civi"/>
@@ -36,5 +36,7 @@
     <mixin>menu-xml@1.0.0</mixin>
     <mixin>mgd-php@1.0.0</mixin>
     <mixin>afform-entity-php@1.0.0</mixin>
+    <mixin>smarty-v2@1.0.0</mixin>
   </mixins>
+  <upgrader>CRM_AfformAdmin_Upgrader</upgrader>
 </extension>
diff --git a/civicrm/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php b/civicrm/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php
index ff7105d45f74a21d766edfaef61ec80e94f8c4d5..707c160afe0b5373a5604fad8bd2bf5de3c2c48a 100644
--- a/civicrm/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php
+++ b/civicrm/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php
@@ -112,7 +112,7 @@ abstract class AbstractProcessor extends \Civi\Api4\Generic\AbstractAction {
 
     $api4 = $this->_formDataModel->getSecureApi4($entity['name']);
     $idField = CoreUtil::getIdFieldName($entity['type']);
-    if ($ids && !empty($entity['fields'][$idField]['saved_search'])) {
+    if ($ids && !empty($entity['fields'][$idField]['defn']['saved_search'])) {
       $ids = $this->validateBySavedSearch($entity, $ids);
     }
     if (!$ids) {
@@ -144,6 +144,14 @@ abstract class AbstractProcessor extends \Civi\Api4\Generic\AbstractAction {
     }
   }
 
+  /**
+   * Validate that given id(s) are actually returned by the Autocomplete API
+   *
+   * @param $entity
+   * @param array $ids
+   * @return array
+   * @throws \CRM_Core_Exception
+   */
   private function validateBySavedSearch($entity, array $ids) {
     $idField = CoreUtil::getIdFieldName($entity['type']);
     $fetched = civicrm_api4($entity['type'], 'autocomplete', [
diff --git a/civicrm/ext/afform/core/Civi/Api4/Action/Afform/Submit.php b/civicrm/ext/afform/core/Civi/Api4/Action/Afform/Submit.php
index c502c510264bcaafb706fca5457907c66ed40b3d..83bdf2d759cf35d24b4999721e869ce810316de7 100644
--- a/civicrm/ext/afform/core/Civi/Api4/Action/Afform/Submit.php
+++ b/civicrm/ext/afform/core/Civi/Api4/Action/Afform/Submit.php
@@ -361,9 +361,11 @@ class Submit extends AbstractProcessor {
           ];
           // Reciprocal relationship types need an extra check
           if ($isReciprocal) {
-            $where[] = ['OR',
-              ['AND', ['contact_id_a', '=', $contact_id_a], ['contact_id_b', '=', $contact_id_b]],
-              ['AND', ['contact_id_a', '=', $contact_id_b], ['contact_id_b', '=', $contact_id_a]],
+            $where[] = [
+              'OR', [
+                ['AND', [['contact_id_a', '=', $contact_id_a], ['contact_id_b', '=', $contact_id_b]]],
+                ['AND', [['contact_id_a', '=', $contact_id_b], ['contact_id_b', '=', $contact_id_a]]],
+              ],
             ];
           }
           else {
diff --git a/civicrm/ext/afform/core/Civi/Api4/Subscriber/AfformAutocompleteSubscriber.php b/civicrm/ext/afform/core/Civi/Api4/Subscriber/AfformAutocompleteSubscriber.php
index c94d392e50190feac60e35ef5124cced717be5a2..70a1bc05ef02d1f8d3da3dde8b98c1b771a44a68 100644
--- a/civicrm/ext/afform/core/Civi/Api4/Subscriber/AfformAutocompleteSubscriber.php
+++ b/civicrm/ext/afform/core/Civi/Api4/Subscriber/AfformAutocompleteSubscriber.php
@@ -78,8 +78,15 @@ class AfformAutocompleteSubscriber extends AutoService implements EventSubscribe
     [$entityName, $joinEntity] = array_pad(explode('+', $entityName), 2, NULL);
     $entity = $formDataModel->getEntity($entityName);
 
+    // If no model entity, it's a search display
+    if (!$entity) {
+      $searchDisplay = $formDataModel->getSearchDisplay($entityName);
+      $apiEntity = civicrm_api4('SavedSearch', 'get', ['where' => [['name', '=', $searchDisplay['searchName']]]])
+        ->first()['api_entity'] ?? NULL;
+      $formField = $searchDisplay['fields'][$fieldName]['defn'] ?? [];
+    }
     // If using a join (e.g. Contact -> Email)
-    if ($joinEntity) {
+    elseif ($joinEntity) {
       $apiEntity = $joinEntity;
       $isId = FALSE;
       $formField = $entity['joins'][$joinEntity]['fields'][$fieldName]['defn'] ?? [];
@@ -97,7 +104,7 @@ class AfformAutocompleteSubscriber extends AutoService implements EventSubscribe
     // For the "Existing Entity" selector,
     // Look up the "type" fields (e.g. contact_type, activity_type_id, case_type_id, etc)
     // And apply it as a filter if specified on the form.
-    if ($isId) {
+    if ($isId && $entity) {
       if ($entity['type'] === 'Contact') {
         $typeFields = ['contact_type', 'contact_sub_type'];
       }
diff --git a/civicrm/ext/afform/core/afform.civix.php b/civicrm/ext/afform/core/afform.civix.php
index 85f9a141821cac15bda44337d7d927c64e6c709f..9519d9269442933e74d25b6947ca7e19df6d36d6 100644
--- a/civicrm/ext/afform/core/afform.civix.php
+++ b/civicrm/ext/afform/core/afform.civix.php
@@ -92,9 +92,6 @@ function _afform_civix_civicrm_config($config = NULL) {
   $configured = TRUE;
 
   $extRoot = __DIR__ . DIRECTORY_SEPARATOR;
-  $extDir = $extRoot . 'templates';
-  CRM_Core_Smarty::singleton()->addTemplateDir($extDir);
-
   $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
   set_include_path($include_path);
   // Based on <compatibility>, this does not currently require mixin/polyfill.php.
@@ -201,20 +198,3 @@ function _afform_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID) {
     }
   }
 }
-
-/**
- * (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 _afform_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, [
-    'CRM_Afform_DAO_AfformSubmission' => [
-      'name' => 'AfformSubmission',
-      'class' => 'CRM_Afform_DAO_AfformSubmission',
-      'table' => 'civicrm_afform_submission',
-    ],
-  ]);
-}
diff --git a/civicrm/ext/afform/core/afform.php b/civicrm/ext/afform/core/afform.php
index b381f64801a97df4029d42a297debe18d15628a1..5cb43af6f81f81a63be6b381b3d47e6a0c75c0a5 100644
--- a/civicrm/ext/afform/core/afform.php
+++ b/civicrm/ext/afform/core/afform.php
@@ -360,17 +360,6 @@ function _afform_get_partials($moduleName, $module) {
   ];
 }
 
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_entityTypes
- */
-function afform_civicrm_entityTypes(&$entityTypes) {
-  _afform_civix_civicrm_entityTypes($entityTypes);
-}
-
 /**
  * Implements hook_civicrm_buildAsset().
  */
diff --git a/civicrm/ext/afform/core/ang/af/fields/EntityRef.html b/civicrm/ext/afform/core/ang/af/fields/EntityRef.html
index 1b783ff44c3470df2d7fdf1427d466590c6da363..38447c8eaf555ec71fb1317702951b2408ce6085 100644
--- a/civicrm/ext/afform/core/ang/af/fields/EntityRef.html
+++ b/civicrm/ext/afform/core/ang/af/fields/EntityRef.html
@@ -5,7 +5,7 @@
        ng-model="getSetSelect"
        ng-model-options="{getterSetter: true}"
        crm-autocomplete="$ctrl.defn.fk_entity"
-       crm-autocomplete-params="{formName: 'afform:' + $ctrl.afFieldset.getFormName(), fieldName: $ctrl.afFieldset.modelName + ':' + $ctrl.fieldName}"
+       crm-autocomplete-params="{formName: 'afform:' + $ctrl.afFieldset.getFormName(), fieldName: $ctrl.afFieldset.getName() + ':' + $ctrl.fieldName}"
        multi="$ctrl.defn.input_attrs.multiple"
        auto-open="$ctrl.defn.input_attrs.autoOpen"
        placeholder="{{:: $ctrl.defn.input_attrs.placeholder }}"
diff --git a/civicrm/ext/afform/core/info.xml b/civicrm/ext/afform/core/info.xml
index dd0a196c11997800d1cf9616196bc92078edb357..38324b441202c12b782fbf75ed1d23ea747324c2 100644
--- a/civicrm/ext/afform/core/info.xml
+++ b/civicrm/ext/afform/core/info.xml
@@ -13,15 +13,15 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-01-09</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>beta</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>The Form Core extension is required to use any dynamic form. To administer and edit forms, also install the FormBuilder extension.</comments>
   <civix>
     <namespace>CRM/Afform</namespace>
-    <format>22.12.1</format>
+    <format>23.02.0</format>
   </civix>
   <classloader>
     <psr4 prefix="Civi\" path="Civi"/>
@@ -34,6 +34,8 @@
     <mixin>ang-php@1.0.0</mixin>
     <mixin>mgd-php@1.0.0</mixin>
     <mixin>scan-classes@1.0.0</mixin>
+    <mixin>smarty-v2@1.0.0</mixin>
+    <mixin>entity-types-php@1.0.0</mixin>
   </mixins>
   <upgrader>CRM_Afform_Upgrader</upgrader>
 </extension>
diff --git a/civicrm/ext/afform/html/info.xml b/civicrm/ext/afform/html/info.xml
index c360e03868c26b71e86197f17b54457c4f7197d7..a5e601ca1689f42fe6470dbe0bf7b6ce9a263a16 100644
--- a/civicrm/ext/afform/html/info.xml
+++ b/civicrm/ext/afform/html/info.xml
@@ -13,10 +13,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-01-09</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>alpha</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <requires>
     <ext>org.civicrm.afform</ext>
diff --git a/civicrm/ext/afform/mock/info.xml b/civicrm/ext/afform/mock/info.xml
index 4337a37ed2ddf9ac7719fbd9816cebd9c9519bcd..c359d8004c1c6251d26bbd165f2df2c1131eccf7 100644
--- a/civicrm/ext/afform/mock/info.xml
+++ b/civicrm/ext/afform/mock/info.xml
@@ -12,13 +12,13 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-01-09</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
   <develStage>alpha</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <requires>
     <ext>org.civicrm.afform</ext>
diff --git a/civicrm/ext/authx/authx.civix.php b/civicrm/ext/authx/authx.civix.php
index 98e7ecc984ba4d66079f913e8d67eaf8c5ba08aa..4e1b32a58e1b1af588f3ef460724b1d49ced1d93 100644
--- a/civicrm/ext/authx/authx.civix.php
+++ b/civicrm/ext/authx/authx.civix.php
@@ -84,27 +84,17 @@ use CRM_Authx_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _authx_civix_civicrm_config(&$config = NULL) {
+function _authx_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
   }
   $configured = TRUE;
 
-  $template = CRM_Core_Smarty::singleton();
-
   $extRoot = __DIR__ . 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);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -114,35 +104,7 @@ function _authx_civix_civicrm_config(&$config = NULL) {
  */
 function _authx_civix_civicrm_install() {
   _authx_civix_civicrm_config();
-  if ($upgrader = _authx_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _authx_civix_civicrm_postInstall() {
-  _authx_civix_civicrm_config();
-  if ($upgrader = _authx_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 _authx_civix_civicrm_uninstall(): void {
-  _authx_civix_civicrm_config();
-  if ($upgrader = _authx_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -152,56 +114,7 @@ function _authx_civix_civicrm_uninstall(): void {
  */
 function _authx_civix_civicrm_enable(): void {
   _authx_civix_civicrm_config();
-  if ($upgrader = _authx_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 _authx_civix_civicrm_disable(): void {
-  _authx_civix_civicrm_config();
-  if ($upgrader = _authx_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 _authx_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _authx_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_Authx_Upgrader
- */
-function _authx_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/Authx/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_Authx_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -285,14 +198,3 @@ function _authx_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID) {
     }
   }
 }
-
-/**
- * (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 _authx_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/authx/authx.php b/civicrm/ext/authx/authx.php
index f6c50359a92e319724ee11e9de2631f97eab88d0..432afe1d893e25f051027f91557db48bb4bb01d2 100644
--- a/civicrm/ext/authx/authx.php
+++ b/civicrm/ext/authx/authx.php
@@ -100,24 +100,6 @@ function authx_civicrm_install() {
 
 }
 
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function authx_civicrm_postInstall() {
-  _authx_civix_civicrm_postInstall();
-}
-
-/**
- * Implements hook_civicrm_uninstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
- */
-function authx_civicrm_uninstall() {
-  _authx_civix_civicrm_uninstall();
-}
-
 /**
  * Implements hook_civicrm_enable().
  *
@@ -134,35 +116,6 @@ function authx_civicrm_enable() {
   }
 }
 
-/**
- * Implements hook_civicrm_disable().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
- */
-function authx_civicrm_disable() {
-  _authx_civix_civicrm_disable();
-}
-
-/**
- * Implements hook_civicrm_upgrade().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
- */
-function authx_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  return _authx_civix_civicrm_upgrade($op, $queue);
-}
-
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
- */
-function authx_civicrm_entityTypes(&$entityTypes) {
-  _authx_civix_civicrm_entityTypes($entityTypes);
-}
-
 /**
  * Implements hook_civicrm_permission().
  *
diff --git a/civicrm/ext/authx/info.xml b/civicrm/ext/authx/info.xml
index 358aca262daa56948ea5693f756db9760b3accac..8847d710dea68d30a0482e4d9e866b1bb5536568 100644
--- a/civicrm/ext/authx/info.xml
+++ b/civicrm/ext/authx/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2021-02-11</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>AuthX enables remote applications to connect to CiviCRM. Use it to enable and disable different forms of authentication (such as username-password, API key, and/or JWT).</comments>
   <classloader>
@@ -32,6 +32,6 @@
   </mixins>
   <civix>
     <namespace>CRM/Authx</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/civicrm_admin_ui/civicrm_admin_ui.civix.php b/civicrm/ext/civicrm_admin_ui/civicrm_admin_ui.civix.php
index ef54bbc93e3bdff2b895e032b76b8ec739c34de3..69a952d2b2295480057878ad3947791b95a899f2 100644
--- a/civicrm/ext/civicrm_admin_ui/civicrm_admin_ui.civix.php
+++ b/civicrm/ext/civicrm_admin_ui/civicrm_admin_ui.civix.php
@@ -84,27 +84,17 @@ use CRM_CivicrmAdminUi_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _civicrm_admin_ui_civix_civicrm_config(&$config = NULL) {
+function _civicrm_admin_ui_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
   }
   $configured = TRUE;
 
-  $template = CRM_Core_Smarty::singleton();
-
   $extRoot = __DIR__ . 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);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -114,35 +104,7 @@ function _civicrm_admin_ui_civix_civicrm_config(&$config = NULL) {
  */
 function _civicrm_admin_ui_civix_civicrm_install() {
   _civicrm_admin_ui_civix_civicrm_config();
-  if ($upgrader = _civicrm_admin_ui_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _civicrm_admin_ui_civix_civicrm_postInstall() {
-  _civicrm_admin_ui_civix_civicrm_config();
-  if ($upgrader = _civicrm_admin_ui_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 _civicrm_admin_ui_civix_civicrm_uninstall(): void {
-  _civicrm_admin_ui_civix_civicrm_config();
-  if ($upgrader = _civicrm_admin_ui_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -152,56 +114,7 @@ function _civicrm_admin_ui_civix_civicrm_uninstall(): void {
  */
 function _civicrm_admin_ui_civix_civicrm_enable(): void {
   _civicrm_admin_ui_civix_civicrm_config();
-  if ($upgrader = _civicrm_admin_ui_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 _civicrm_admin_ui_civix_civicrm_disable(): void {
-  _civicrm_admin_ui_civix_civicrm_config();
-  if ($upgrader = _civicrm_admin_ui_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 _civicrm_admin_ui_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _civicrm_admin_ui_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_CivicrmAdminUi_Upgrader
- */
-function _civicrm_admin_ui_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/CivicrmAdminUi/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_CivicrmAdminUi_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -285,14 +198,3 @@ function _civicrm_admin_ui_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $pa
     }
   }
 }
-
-/**
- * (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 _civicrm_admin_ui_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/civicrm_admin_ui/civicrm_admin_ui.php b/civicrm/ext/civicrm_admin_ui/civicrm_admin_ui.php
index 456b78b883959190940682c5a7ec753e00f9fcb1..85db9c327710d8371e61ee7744686d1d89aa2b53 100644
--- a/civicrm/ext/civicrm_admin_ui/civicrm_admin_ui.php
+++ b/civicrm/ext/civicrm_admin_ui/civicrm_admin_ui.php
@@ -13,14 +13,3 @@ use CRM_CivicrmAdminUi_ExtensionUtil as E;
 function civicrm_admin_ui_civicrm_config(&$config) {
   _civicrm_admin_ui_civix_civicrm_config($config);
 }
-
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
- */
-function civicrm_admin_ui_civicrm_entityTypes(&$entityTypes) {
-  _civicrm_admin_ui_civix_civicrm_entityTypes($entityTypes);
-}
diff --git a/civicrm/ext/civicrm_admin_ui/info.xml b/civicrm/ext/civicrm_admin_ui/info.xml
index 1cf065a4c31207cf8e3b2183a12476f37650dad7..55f7fdc0b80f0ed8029d4d21daef6b95be15816a 100644
--- a/civicrm/ext/civicrm_admin_ui/info.xml
+++ b/civicrm/ext/civicrm_admin_ui/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2022-01-02</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>alpha</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <requires>
     <ext>org.civicrm.search_kit</ext>
@@ -32,7 +32,7 @@
   <civix>
     <namespace>CRM/CivicrmAdminUi</namespace>
     <angularModule>crmCivicrmAdminUi</angularModule>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
   <mixins>
     <mixin>mgd-php@1.0.0</mixin>
diff --git a/civicrm/ext/civigrant/civigrant.civix.php b/civicrm/ext/civigrant/civigrant.civix.php
index 0b066cd2fa0f04c8b377b162cc8bd6c410f2309c..2b49bd6d7a27a118f5a7bdd42e521a2b2640b52e 100644
--- a/civicrm/ext/civigrant/civigrant.civix.php
+++ b/civicrm/ext/civigrant/civigrant.civix.php
@@ -84,7 +84,7 @@ use CRM_Grant_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _civigrant_civix_civicrm_config(&$config = NULL) {
+function _civigrant_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
@@ -94,6 +94,7 @@ function _civigrant_civix_civicrm_config(&$config = NULL) {
   $extRoot = __DIR__ . DIRECTORY_SEPARATOR;
   $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
   set_include_path($include_path);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -103,35 +104,7 @@ function _civigrant_civix_civicrm_config(&$config = NULL) {
  */
 function _civigrant_civix_civicrm_install() {
   _civigrant_civix_civicrm_config();
-  if ($upgrader = _civigrant_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _civigrant_civix_civicrm_postInstall() {
-  _civigrant_civix_civicrm_config();
-  if ($upgrader = _civigrant_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 _civigrant_civix_civicrm_uninstall(): void {
-  _civigrant_civix_civicrm_config();
-  if ($upgrader = _civigrant_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -141,56 +114,7 @@ function _civigrant_civix_civicrm_uninstall(): void {
  */
 function _civigrant_civix_civicrm_enable(): void {
   _civigrant_civix_civicrm_config();
-  if ($upgrader = _civigrant_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 _civigrant_civix_civicrm_disable(): void {
-  _civigrant_civix_civicrm_config();
-  if ($upgrader = _civigrant_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 _civigrant_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _civigrant_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_Grant_Upgrader
- */
-function _civigrant_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/Grant/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_Grant_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -274,20 +198,3 @@ function _civigrant_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID)
     }
   }
 }
-
-/**
- * (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 _civigrant_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, [
-    'CRM_Grant_DAO_Grant' => [
-      'name' => 'Grant',
-      'class' => 'CRM_Grant_DAO_Grant',
-      'table' => 'civicrm_grant',
-    ],
-  ]);
-}
diff --git a/civicrm/ext/civigrant/civigrant.php b/civicrm/ext/civigrant/civigrant.php
index bd14f68621a777c670131bef1b58e6bfed90ee03..9f4ca39e70fbd55c40570c5df9c78d6290837ce0 100644
--- a/civicrm/ext/civigrant/civigrant.php
+++ b/civicrm/ext/civigrant/civigrant.php
@@ -12,17 +12,6 @@ function civigrant_civicrm_config(&$config) {
   _civigrant_civix_civicrm_config($config);
 }
 
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
- */
-function civigrant_civicrm_entityTypes(&$entityTypes) {
-  _civigrant_civix_civicrm_entityTypes($entityTypes);
-}
-
 /**
  * Implements hook_civicrm_links().
  *
diff --git a/civicrm/ext/civigrant/info.xml b/civicrm/ext/civigrant/info.xml
index 17a488eb54b7e96dd2893a3877047a783432b03f..640df3e62ad3f1eea5a2316d4fe305714034b5a0 100644
--- a/civicrm/ext/civigrant/info.xml
+++ b/civicrm/ext/civigrant/info.xml
@@ -13,10 +13,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2021-11-11</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>CiviGrant was originally a core component before migrating to an extension</comments>
   <requires>
@@ -33,9 +33,10 @@
     <mixin>mgd-php@1.0.0</mixin>
     <mixin>afform-entity-php@1.0.0</mixin>
     <mixin>smarty-v2@1.0.0</mixin>
+    <mixin>entity-types-php@1.0.0</mixin>
   </mixins>
   <civix>
     <namespace>CRM/Grant</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/civigrant/templates/CRM/Grant/Form/Task/Delete.tpl b/civicrm/ext/civigrant/templates/CRM/Grant/Form/Task/Delete.tpl
index 85f3168b06616895e3f11805c6d416f0f76523e7..be3e263e63b3167284e3a18513e50e96b2b01c43 100644
--- a/civicrm/ext/civigrant/templates/CRM/Grant/Form/Task/Delete.tpl
+++ b/civicrm/ext/civigrant/templates/CRM/Grant/Form/Task/Delete.tpl
@@ -14,5 +14,5 @@
           {ts}Are you sure you want to delete the selected Grants? This delete operation cannot be undone and will delete all transactions associated with these grants.{/ts}
           <p>{include file="CRM/Grant/Form/Task.tpl"}</p>
   </div>
-  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 </div>
diff --git a/civicrm/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php b/civicrm/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php
index 4ee6bbbe5efebce08da75021de2ae05ac6622f02..0eade33f0db9217dcdcef70e95fd19ae286c0cf2 100644
--- a/civicrm/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php
+++ b/civicrm/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php
@@ -104,7 +104,7 @@ class ImportSubscriber extends AutoService implements EventSubscriberInterface {
   public function onApiAuthorize(AuthorizeEvent $event): void {
     $apiRequest = $event->getApiRequest();
     $entity = $apiRequest['entity'];
-    if (strpos($entity, 'Import_') === 0) {
+    if (strpos($entity, 'Import_') === 0 && !in_array($event->getActionName(), ['getFields', 'getActions', 'checkAccess'], TRUE)) {
       $userJobID = (int) (str_replace('Import_', '', $entity));
       if (!UserJob::get(TRUE)->addWhere('id', '=', $userJobID)->selectRowCount()->execute()->count()) {
         throw new UnauthorizedException('Import access not permitted');
diff --git a/civicrm/ext/civiimport/civiimport.civix.php b/civicrm/ext/civiimport/civiimport.civix.php
index d7f10c08836f60c01357a0795b8dcd2c9a0150f1..c912ca5bcfeebc58cbb6069419192daccde8d1df 100644
--- a/civicrm/ext/civiimport/civiimport.civix.php
+++ b/civicrm/ext/civiimport/civiimport.civix.php
@@ -84,27 +84,17 @@ use CRM_Civiimport_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _civiimport_civix_civicrm_config(&$config = NULL) {
+function _civiimport_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
   }
   $configured = TRUE;
 
-  $template = CRM_Core_Smarty::singleton();
-
   $extRoot = __DIR__ . 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);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -114,35 +104,7 @@ function _civiimport_civix_civicrm_config(&$config = NULL) {
  */
 function _civiimport_civix_civicrm_install() {
   _civiimport_civix_civicrm_config();
-  if ($upgrader = _civiimport_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _civiimport_civix_civicrm_postInstall() {
-  _civiimport_civix_civicrm_config();
-  if ($upgrader = _civiimport_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 _civiimport_civix_civicrm_uninstall(): void {
-  _civiimport_civix_civicrm_config();
-  if ($upgrader = _civiimport_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -152,56 +114,7 @@ function _civiimport_civix_civicrm_uninstall(): void {
  */
 function _civiimport_civix_civicrm_enable(): void {
   _civiimport_civix_civicrm_config();
-  if ($upgrader = _civiimport_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 _civiimport_civix_civicrm_disable(): void {
-  _civiimport_civix_civicrm_config();
-  if ($upgrader = _civiimport_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 _civiimport_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _civiimport_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_Civiimport_Upgrader
- */
-function _civiimport_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/Civiimport/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_Civiimport_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -285,14 +198,3 @@ function _civiimport_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID
     }
   }
 }
-
-/**
- * (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 _civiimport_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/civiimport/civiimport.php b/civicrm/ext/civiimport/civiimport.php
index ded5220c2c45284dbc8e52978524df201bde347f..eef2432d68446bc79642ce4b76967227a0d9d8c4 100644
--- a/civicrm/ext/civiimport/civiimport.php
+++ b/civicrm/ext/civiimport/civiimport.php
@@ -31,24 +31,6 @@ function civiimport_civicrm_install() {
   _civiimport_civix_civicrm_install();
 }
 
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function civiimport_civicrm_postInstall() {
-  _civiimport_civix_civicrm_postInstall();
-}
-
-/**
- * Implements hook_civicrm_uninstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
- */
-function civiimport_civicrm_uninstall() {
-  _civiimport_civix_civicrm_uninstall();
-}
-
 /**
  * Implements hook_civicrm_enable().
  *
@@ -58,24 +40,6 @@ function civiimport_civicrm_enable() {
   _civiimport_civix_civicrm_enable();
 }
 
-/**
- * Implements hook_civicrm_disable().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
- */
-function civiimport_civicrm_disable() {
-  _civiimport_civix_civicrm_disable();
-}
-
-/**
- * Implements hook_civicrm_upgrade().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
- */
-function civiimport_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  return _civiimport_civix_civicrm_upgrade($op, $queue);
-}
-
 /**
  * Implements hook_civicrm_entityTypes().
  *
diff --git a/civicrm/ext/civiimport/info.xml b/civicrm/ext/civiimport/info.xml
index 1097ce32bea768aa1fb5337c42c4e399617309ac..23ad03fd91ddcc124862f01859889d9ad97ba10e 100644
--- a/civicrm/ext/civiimport/info.xml
+++ b/civicrm/ext/civiimport/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2022-08-11</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>alpha</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>Core extension for us to start moving import logic into, has more functionality</comments>
   <requires>
@@ -31,7 +31,7 @@
   </classloader>
   <civix>
     <namespace>CRM/Civiimport</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
     <angularModule>crmCiviimport</angularModule>
   </civix>
   <mixins>
@@ -39,5 +39,6 @@
     <mixin>scan-classes@1.0.0</mixin>
     <mixin>setting-php@1.0.0</mixin>
     <mixin>ang-php@1.0.0</mixin>
+    <mixin>smarty-v2@1.0.0</mixin>
   </mixins>
 </extension>
diff --git a/civicrm/ext/ckeditor4/CRM/Ckeditor4/Upgrader.php b/civicrm/ext/ckeditor4/CRM/Ckeditor4/Upgrader.php
index 76d3f1b7d73214bc0fff9eedd751f376c50cbd36..6455ef964f8546d7770eb1a50051ccbdd1b9b5c5 100644
--- a/civicrm/ext/ckeditor4/CRM/Ckeditor4/Upgrader.php
+++ b/civicrm/ext/ckeditor4/CRM/Ckeditor4/Upgrader.php
@@ -4,7 +4,7 @@ use CRM_Ckeditor4_ExtensionUtil as E;
 /**
  * Collection of upgrade steps.
  */
-class CRM_Ckeditor4_Upgrader extends CRM_Ckeditor4_Upgrader_Base {
+class CRM_Ckeditor4_Upgrader extends CRM_Extension_Upgrader_Base {
 
   /**
    * Install extension.
diff --git a/civicrm/ext/ckeditor4/CRM/Ckeditor4/Upgrader/Base.php b/civicrm/ext/ckeditor4/CRM/Ckeditor4/Upgrader/Base.php
deleted file mode 100644
index f323ad094128c02764d1de4ebd0dd72bac17887d..0000000000000000000000000000000000000000
--- a/civicrm/ext/ckeditor4/CRM/Ckeditor4/Upgrader/Base.php
+++ /dev/null
@@ -1,396 +0,0 @@
-<?php
-
-// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
-use CRM_Ckeditor4_ExtensionUtil as E;
-
-/**
- * Base class which provides helpers to execute upgrade logic
- */
-class CRM_Ckeditor4_Upgrader_Base {
-
-  /**
-   * @var CRM_Ckeditor4_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) {
-      self::$instance = new CRM_Ckeditor4_Upgrader(
-        'ckeditor4',
-        E::path()
-      );
-    }
-    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.
-   *
-   * ```
-   * CRM_Ckeditor4_Upgrader_Base::_queueAdapter($ctx, 'methodName', 'arg1', 'arg2');
-   * ```
-   */
-  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_Ckeditor4_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.
-   *
-   * @param CRM_Queue_Queue $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/ckeditor4/ckeditor4.civix.php b/civicrm/ext/ckeditor4/ckeditor4.civix.php
index e7ce69749e84ae2a0a1568f065eadc0ebba00d97..39aa238b671798244921e5d7e3353e3302c0cd35 100644
--- a/civicrm/ext/ckeditor4/ckeditor4.civix.php
+++ b/civicrm/ext/ckeditor4/ckeditor4.civix.php
@@ -84,27 +84,17 @@ use CRM_Ckeditor4_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _ckeditor4_civix_civicrm_config(&$config = NULL) {
+function _ckeditor4_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
   }
   $configured = TRUE;
 
-  $template = CRM_Core_Smarty::singleton();
-
   $extRoot = __DIR__ . 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);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -114,35 +104,7 @@ function _ckeditor4_civix_civicrm_config(&$config = NULL) {
  */
 function _ckeditor4_civix_civicrm_install() {
   _ckeditor4_civix_civicrm_config();
-  if ($upgrader = _ckeditor4_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _ckeditor4_civix_civicrm_postInstall() {
-  _ckeditor4_civix_civicrm_config();
-  if ($upgrader = _ckeditor4_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 _ckeditor4_civix_civicrm_uninstall(): void {
-  _ckeditor4_civix_civicrm_config();
-  if ($upgrader = _ckeditor4_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -152,56 +114,7 @@ function _ckeditor4_civix_civicrm_uninstall(): void {
  */
 function _ckeditor4_civix_civicrm_enable(): void {
   _ckeditor4_civix_civicrm_config();
-  if ($upgrader = _ckeditor4_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 _ckeditor4_civix_civicrm_disable(): void {
-  _ckeditor4_civix_civicrm_config();
-  if ($upgrader = _ckeditor4_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 _ckeditor4_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _ckeditor4_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_Ckeditor4_Upgrader
- */
-function _ckeditor4_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/Ckeditor4/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_Ckeditor4_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -285,14 +198,3 @@ function _ckeditor4_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID)
     }
   }
 }
-
-/**
- * (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 _ckeditor4_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/ckeditor4/ckeditor4.php b/civicrm/ext/ckeditor4/ckeditor4.php
index cdd366c94d683b76f9f30dbb9ecc32f33b724d32..5bd699dccc2e2a9525a4d4b376d5a8225630c223 100644
--- a/civicrm/ext/ckeditor4/ckeditor4.php
+++ b/civicrm/ext/ckeditor4/ckeditor4.php
@@ -23,24 +23,6 @@ function ckeditor4_civicrm_install() {
   _ckeditor4_civix_civicrm_install();
 }
 
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function ckeditor4_civicrm_postInstall() {
-  _ckeditor4_civix_civicrm_postInstall();
-}
-
-/**
- * Implements hook_civicrm_uninstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
- */
-function ckeditor4_civicrm_uninstall() {
-  _ckeditor4_civix_civicrm_uninstall();
-}
-
 /**
  * Implements hook_civicrm_enable().
  *
@@ -50,35 +32,6 @@ function ckeditor4_civicrm_enable() {
   _ckeditor4_civix_civicrm_enable();
 }
 
-/**
- * Implements hook_civicrm_disable().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
- */
-function ckeditor4_civicrm_disable() {
-  _ckeditor4_civix_civicrm_disable();
-}
-
-/**
- * Implements hook_civicrm_upgrade().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
- */
-function ckeditor4_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  return _ckeditor4_civix_civicrm_upgrade($op, $queue);
-}
-
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
- */
-function ckeditor4_civicrm_entityTypes(&$entityTypes) {
-  _ckeditor4_civix_civicrm_entityTypes($entityTypes);
-}
-
 function ckeditor4_civicrm_buildForm($formName, $form) {
   if ($formName === 'CRM_Admin_Form_Preferences_Display') {
     $form->addElement(
diff --git a/civicrm/ext/ckeditor4/info.xml b/civicrm/ext/ckeditor4/info.xml
index af8a9d240b106b27be82c38b28e06a950647ab8e..2a83898bf14c084fe4b1062da4cd803633d2eeb6 100644
--- a/civicrm/ext/ckeditor4/info.xml
+++ b/civicrm/ext/ckeditor4/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">https://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2021-05-23</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>This is the version of CKEditor that originally shipped with CiviCRM core</comments>
   <classloader>
@@ -27,9 +27,11 @@
   </classloader>
   <mixins>
     <mixin>menu-xml@1.0.0</mixin>
+    <mixin>smarty-v2@1.0.0</mixin>
   </mixins>
   <civix>
     <namespace>CRM/Ckeditor4</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
+  <upgrader>CRM_Ckeditor4_Upgrader</upgrader>
 </extension>
diff --git a/civicrm/ext/contributioncancelactions/info.xml b/civicrm/ext/contributioncancelactions/info.xml
index f40b8c2d328c097b2b507c74dc316fb9fe3d8566..d80fb165593e2a74edd2610abd46b8e28811d4c7 100644
--- a/civicrm/ext/contributioncancelactions/info.xml
+++ b/civicrm/ext/contributioncancelactions/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-10-12</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>This code has been moved from core to a separate extension in 5.32. Note that if you disable it failed or cancelled contributions will not cause related memberships and participant records to be updated</comments>
   <classloader>
diff --git a/civicrm/ext/elavon/info.xml b/civicrm/ext/elavon/info.xml
index cf49eca08d4d6a486fbf62af5281ea285b61c09e..53e5119a3cccf14f6219ce6830d7d07a94d555e5 100644
--- a/civicrm/ext/elavon/info.xml
+++ b/civicrm/ext/elavon/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2022-08-05</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments/>
   <classloader>
diff --git a/civicrm/ext/eventcart/info.xml b/civicrm/ext/eventcart/info.xml
index 11f7c5485053ec907d9cc75d8f6d774e8b3aaf91..aaeb0c4aa60d1b56156511d34728286364a74900 100644
--- a/civicrm/ext/eventcart/info.xml
+++ b/civicrm/ext/eventcart/info.xml
@@ -13,13 +13,13 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-08-03</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <classloader>
     <psr0 prefix="CRM_" path="."/>
diff --git a/civicrm/ext/ewaysingle/info.xml b/civicrm/ext/ewaysingle/info.xml
index 644b6c005df5f4e1ce62c718eaaccece9307f687..42ff6c25e3942031f7a9304ccbda71c5cc62843e 100644
--- a/civicrm/ext/ewaysingle/info.xml
+++ b/civicrm/ext/ewaysingle/info.xml
@@ -15,13 +15,13 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-10-07</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>This is an extension to contain the eWAY Single Currency Payment Processor</comments>
   <classloader>
diff --git a/civicrm/ext/financialacls/financialacls.civix.php b/civicrm/ext/financialacls/financialacls.civix.php
index 62bdbe3f8bce611860746060d9a60619813c1450..d7b20fa37aca433efb35e853942adc19098b395d 100644
--- a/civicrm/ext/financialacls/financialacls.civix.php
+++ b/civicrm/ext/financialacls/financialacls.civix.php
@@ -84,27 +84,17 @@ use CRM_Financialacls_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _financialacls_civix_civicrm_config(&$config = NULL) {
+function _financialacls_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
   }
   $configured = TRUE;
 
-  $template = CRM_Core_Smarty::singleton();
-
   $extRoot = __DIR__ . 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);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -114,35 +104,7 @@ function _financialacls_civix_civicrm_config(&$config = NULL) {
  */
 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(): void {
-  _financialacls_civix_civicrm_config();
-  if ($upgrader = _financialacls_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -152,56 +114,7 @@ function _financialacls_civix_civicrm_uninstall(): void {
  */
 function _financialacls_civix_civicrm_enable(): void {
   _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(): void {
-  _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();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -285,14 +198,3 @@ function _financialacls_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $paren
     }
   }
 }
-
-/**
- * (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
index af3378eb49abe0e34dc01e302f661d4b2b54e5e1..8dea84fe6fe9bbca2a8309a6aeb51f3e2b5c3aa4 100644
--- a/civicrm/ext/financialacls/financialacls.php
+++ b/civicrm/ext/financialacls/financialacls.php
@@ -34,24 +34,6 @@ 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().
  *
@@ -61,35 +43,6 @@ 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_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);
-}
-
 /**
  * Intervene to prevent deletion, where permissions block it.
  *
diff --git a/civicrm/ext/financialacls/info.xml b/civicrm/ext/financialacls/info.xml
index f79347bbbf62e91a45e9483d0b93d7d171e3b307..365ef0dae2e109b67c571517ff6e48b4862f5971 100644
--- a/civicrm/ext/financialacls/info.xml
+++ b/civicrm/ext/financialacls/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-08-27</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <tags>
     <tag>mgmt:hidden</tag>
@@ -33,6 +33,6 @@
   </mixins>
   <civix>
     <namespace>CRM/Financialacls</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/flexmailer/flexmailer.civix.php b/civicrm/ext/flexmailer/flexmailer.civix.php
index 6a215af368203984f1e8e053025aadc56f39e8cb..2905f48988722150c8163185940830550b426b5b 100644
--- a/civicrm/ext/flexmailer/flexmailer.civix.php
+++ b/civicrm/ext/flexmailer/flexmailer.civix.php
@@ -92,9 +92,6 @@ function _flexmailer_civix_civicrm_config($config = NULL) {
   $configured = TRUE;
 
   $extRoot = __DIR__ . DIRECTORY_SEPARATOR;
-  $extDir = $extRoot . 'templates';
-  CRM_Core_Smarty::singleton()->addTemplateDir($extDir);
-
   $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
   set_include_path($include_path);
   // Based on <compatibility>, this does not currently require mixin/polyfill.php.
@@ -201,14 +198,3 @@ function _flexmailer_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID
     }
   }
 }
-
-/**
- * (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 _flexmailer_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/flexmailer/info.xml b/civicrm/ext/flexmailer/info.xml
index 8abc677e8f4f8577936701152eb4697c46999125..4128a21c65d92bfa08f479d0f10f236b743297d8 100644
--- a/civicrm/ext/flexmailer/info.xml
+++ b/civicrm/ext/flexmailer/info.xml
@@ -15,7 +15,7 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-08-05</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <comments>
     FlexMailer is an email delivery engine which replaces the internal guts
@@ -23,7 +23,7 @@
     to provide richer email features.
   </comments>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <tags>
     <tag>mgmt:required</tag>
@@ -37,6 +37,6 @@
   </mixins>
   <civix>
     <namespace>CRM/Flexmailer</namespace>
-    <format>22.12.1</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/flexmailer/src/Listener/BounceTracker.php b/civicrm/ext/flexmailer/src/Listener/BounceTracker.php
index 210fea22dfae19eccd0d38fe1e7067713f83b58f..e3c3dd112517079f8a830a96bb199441fd452256 100644
--- a/civicrm/ext/flexmailer/src/Listener/BounceTracker.php
+++ b/civicrm/ext/flexmailer/src/Listener/BounceTracker.php
@@ -25,6 +25,7 @@ class BounceTracker extends BaseListener {
     }
 
     $mailing = $e->getMailing();
+    $defaultReturnPath = \CRM_Core_BAO_MailSettings::defaultReturnPath();
 
     foreach ($e->getTasks() as $task) {
       /** @var \Civi\FlexMailer\FlexMailerTask $task */
@@ -33,7 +34,7 @@ class BounceTracker extends BaseListener {
         $task->getAddress());
 
       if (!$task->getMailParam('Return-Path')) {
-        $task->setMailParam('Return-Path', $verp['bounce']);
+        $task->setMailParam('Return-Path', $defaultReturnPath ?? $verp['bounce']);
       }
       if (!$task->getMailParam('X-CiviMail-Bounce')) {
         $task->setMailParam('X-CiviMail-Bounce', $verp['bounce']);
diff --git a/civicrm/ext/greenwich/greenwich.civix.php b/civicrm/ext/greenwich/greenwich.civix.php
index 3e018c743bd32fc9ce60d0ed00d1218b51bf8830..2c4dfb13090ea09aafd88e22b502214799f1c686 100644
--- a/civicrm/ext/greenwich/greenwich.civix.php
+++ b/civicrm/ext/greenwich/greenwich.civix.php
@@ -92,9 +92,6 @@ function _greenwich_civix_civicrm_config($config = NULL) {
   $configured = TRUE;
 
   $extRoot = __DIR__ . DIRECTORY_SEPARATOR;
-  $extDir = $extRoot . 'templates';
-  CRM_Core_Smarty::singleton()->addTemplateDir($extDir);
-
   $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
   set_include_path($include_path);
   // Based on <compatibility>, this does not currently require mixin/polyfill.php.
@@ -201,14 +198,3 @@ function _greenwich_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID)
     }
   }
 }
-
-/**
- * (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 _greenwich_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/greenwich/info.xml b/civicrm/ext/greenwich/info.xml
index 4d92444446e676b334c6987b1940f560356ef445..77e8c3a42aec0f14c85c49fd3b0dff4d9906f2ad 100644
--- a/civicrm/ext/greenwich/info.xml
+++ b/civicrm/ext/greenwich/info.xml
@@ -15,13 +15,13 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-07-21</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <classloader>
     <psr0 prefix="CRM_" path="."/>
@@ -29,6 +29,6 @@
   </classloader>
   <civix>
     <namespace>CRM/Greenwich</namespace>
-    <format>22.12.1</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/legacycustomsearches/CRM/Contact/Selector/Custom.php b/civicrm/ext/legacycustomsearches/CRM/Contact/Selector/Custom.php
index 95507319a9692364566376056a6e06c533914a87..7c0a3b50f39c09658701a0e0675a9ada830766ea 100644
--- a/civicrm/ext/legacycustomsearches/CRM/Contact/Selector/Custom.php
+++ b/civicrm/ext/legacycustomsearches/CRM/Contact/Selector/Custom.php
@@ -13,7 +13,6 @@
  *
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
- * $Id: Selector.php 11510 2007-09-18 09:21:34Z lobo $
  */
 
 /**
@@ -28,7 +27,7 @@ class CRM_Contact_Selector_Custom extends CRM_Contact_Selector {
    *
    * @var array
    */
-  public static $_links = NULL;
+  public static $_links;
 
   /**
    * We use desc to remind us what that column is, name is used in the tpl
@@ -360,6 +359,107 @@ class CRM_Contact_Selector_Custom extends CRM_Contact_Selector {
     return $rows;
   }
 
+  /**
+   * @param CRM_Utils_Sort $sort
+   *
+   * @return string
+   * @throws \CRM_Core_Exception
+   */
+  private function buildPrevNextCache($sort): string {
+    $cacheKey = 'civicrm search ' . $this->_key;
+
+    // We should clear the cache in following conditions:
+    // 1. when starting from scratch, i.e new search
+    // 2. if records are sorted
+
+    // get current page requested
+    $pageNum = CRM_Utils_Request::retrieve('crmPID', 'Integer');
+
+    // get the current sort order
+    $currentSortID = CRM_Utils_Request::retrieve('crmSID', 'String');
+
+    $session = CRM_Core_Session::singleton();
+
+    // get previous sort id
+    $previousSortID = $session->get('previousSortID');
+
+    // check for current != previous to ensure cache is not reset if paging is done without changing
+    // sort criteria
+    if (!$pageNum || (!empty($currentSortID) && $currentSortID != $previousSortID)) {
+      Civi::service('prevnext')->deleteItem(NULL, $cacheKey, 'civicrm_contact');
+      // this means it's fresh search, so set pageNum=1
+      if (!$pageNum) {
+        $pageNum = 1;
+      }
+    }
+
+    // set the current sort as previous sort
+    if (!empty($currentSortID)) {
+      $session->set('previousSortID', $currentSortID);
+    }
+
+    $pageSize = CRM_Utils_Request::retrieve('crmRowCount', 'Integer', CRM_Core_DAO::$_nullObject, FALSE, 50);
+    $firstRecord = ($pageNum - 1) * $pageSize;
+
+    //for alphabetic pagination selection save
+    $sortByCharacter = CRM_Utils_Request::retrieve('sortByCharacter', 'String');
+
+    //for text field pagination selection save
+    $countRow = Civi::service('prevnext')->getCount($cacheKey);
+    // $sortByCharacter triggers a refresh in the prevNext cache
+    if ($sortByCharacter && $sortByCharacter !== 'all') {
+      $this->fillPrevNextCache($sort, $cacheKey, 0, max(self::CACHE_SIZE, $pageSize));
+    }
+    elseif (($firstRecord + $pageSize) >= $countRow) {
+      $this->fillPrevNextCache($sort, $cacheKey, $countRow, max(self::CACHE_SIZE, $pageSize) + $firstRecord - $countRow);
+    }
+    return $cacheKey;
+  }
+
+  /**
+   * @param CRM_Utils_Sort $sort
+   * @param string $cacheKey
+   * @param int $start
+   * @param int $end
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function fillPrevNextCache($sort, $cacheKey, $start = 0, $end = self::CACHE_SIZE): void {
+    $sql = $this->_search->contactIDs($start, $end, $sort, TRUE);
+
+    // CRM-9096
+    // due to limitations in our search query writer, the above query does not work
+    // in cases where the query is being sorted on a non-contact table
+    // this results in a fatal error :(
+    // see below for the gross hack of trapping the error and not filling
+    // the prev next cache in this situation
+    // the other alternative of running the FULL query will just be incredibly inefficient
+    // and slow things down way too much on large data sets / complex queries
+
+    $selectSQL = CRM_Core_DAO::composeQuery("SELECT DISTINCT %1, contact_a.id, contact_a.sort_name", [1 => [$cacheKey, 'String']]);
+
+    $sql = str_ireplace(['SELECT contact_a.id as contact_id', 'SELECT contact_a.id as id'], $selectSQL, $sql);
+    $sql = str_ireplace('ORDER BY `contact_id`', 'ORDER BY `id`', $sql, $sql);
+
+    try {
+      Civi::service('prevnext')->fillWithSql($cacheKey, $sql);
+    }
+    catch (\Exception $e) {
+      CRM_Core_Error::deprecatedFunctionWarning('Custom searches should return sql capable of filling the prevnext cache.');
+      // This will always show for CiviRules :-( as a) it orders by 'rule_label'
+      // which is not available in the query & b) it uses contact not contact_a
+      // as an alias.
+      // CRM_Core_Session::setStatus(ts('Query Failed'));
+      return;
+    }
+
+    if (Civi::service('prevnext') instanceof CRM_Core_PrevNextCache_Sql) {
+      // SQL-backed prevnext cache uses an extra record for pruning the cache.
+      // Also ensure that caches stay alive for 2 days as per previous code
+      Civi::cache('prevNextCache')->set($cacheKey, $cacheKey, 60 * 60 * 24 * CRM_Core_PrevNextCache_Sql::cacheDays);
+    }
+  }
+
   /**
    * Given the current formValues, gets the query in local language.
    *
diff --git a/civicrm/ext/legacycustomsearches/info.xml b/civicrm/ext/legacycustomsearches/info.xml
index fd05619ca0b3257a4dc6c00ef426f4c18838374f..ac5c0f1ed80472d8ea342d684c87a9b4ab8a5b7d 100644
--- a/civicrm/ext/legacycustomsearches/info.xml
+++ b/civicrm/ext/legacycustomsearches/info.xml
@@ -15,13 +15,13 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2021-07-25</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>This is hidden on install to give extensions that require it time to add it to their requires and to allow us to get it out of GroupContact load</comments>
   <classloader>
@@ -31,9 +31,10 @@
   <mixins>
     <mixin>menu-xml@1.0.0</mixin>
     <mixin>mgd-php@1.0.0</mixin>
+    <mixin>smarty-v2@1.0.0</mixin>
   </mixins>
   <civix>
     <namespace>CRM/Legacycustomsearches</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/legacycustomsearches/legacycustomsearches.civix.php b/civicrm/ext/legacycustomsearches/legacycustomsearches.civix.php
index b769c8311fc11c51fd7068d1ce0ff50c8090946b..ffcb4eeb94c1325bd0e046750ecb2162bd43025f 100644
--- a/civicrm/ext/legacycustomsearches/legacycustomsearches.civix.php
+++ b/civicrm/ext/legacycustomsearches/legacycustomsearches.civix.php
@@ -84,27 +84,17 @@ use CRM_Legacycustomsearches_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _legacycustomsearches_civix_civicrm_config(&$config = NULL) {
+function _legacycustomsearches_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
   }
   $configured = TRUE;
 
-  $template = CRM_Core_Smarty::singleton();
-
   $extRoot = __DIR__ . 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);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -114,35 +104,7 @@ function _legacycustomsearches_civix_civicrm_config(&$config = NULL) {
  */
 function _legacycustomsearches_civix_civicrm_install() {
   _legacycustomsearches_civix_civicrm_config();
-  if ($upgrader = _legacycustomsearches_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _legacycustomsearches_civix_civicrm_postInstall() {
-  _legacycustomsearches_civix_civicrm_config();
-  if ($upgrader = _legacycustomsearches_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 _legacycustomsearches_civix_civicrm_uninstall(): void {
-  _legacycustomsearches_civix_civicrm_config();
-  if ($upgrader = _legacycustomsearches_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -152,56 +114,7 @@ function _legacycustomsearches_civix_civicrm_uninstall(): void {
  */
 function _legacycustomsearches_civix_civicrm_enable(): void {
   _legacycustomsearches_civix_civicrm_config();
-  if ($upgrader = _legacycustomsearches_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 _legacycustomsearches_civix_civicrm_disable(): void {
-  _legacycustomsearches_civix_civicrm_config();
-  if ($upgrader = _legacycustomsearches_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 _legacycustomsearches_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _legacycustomsearches_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_Legacycustomsearches_Upgrader
- */
-function _legacycustomsearches_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/Legacycustomsearches/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_Legacycustomsearches_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -285,14 +198,3 @@ function _legacycustomsearches_civix_fixNavigationMenuItems(&$nodes, &$maxNavID,
     }
   }
 }
-
-/**
- * (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 _legacycustomsearches_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/legacycustomsearches/legacycustomsearches.php b/civicrm/ext/legacycustomsearches/legacycustomsearches.php
index 92c9aa328adf275f0a9b409b2d7f23a2f12498d5..9d16959ce51fc0926d894f6a085d8a915e71fb3d 100644
--- a/civicrm/ext/legacycustomsearches/legacycustomsearches.php
+++ b/civicrm/ext/legacycustomsearches/legacycustomsearches.php
@@ -23,24 +23,6 @@ function legacycustomsearches_civicrm_install() {
   _legacycustomsearches_civix_civicrm_install();
 }
 
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function legacycustomsearches_civicrm_postInstall() {
-  _legacycustomsearches_civix_civicrm_postInstall();
-}
-
-/**
- * Implements hook_civicrm_uninstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
- */
-function legacycustomsearches_civicrm_uninstall() {
-  _legacycustomsearches_civix_civicrm_uninstall();
-}
-
 /**
  * Implements hook_civicrm_enable().
  *
@@ -49,32 +31,3 @@ function legacycustomsearches_civicrm_uninstall() {
 function legacycustomsearches_civicrm_enable() {
   _legacycustomsearches_civix_civicrm_enable();
 }
-
-/**
- * Implements hook_civicrm_disable().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
- */
-function legacycustomsearches_civicrm_disable() {
-  _legacycustomsearches_civix_civicrm_disable();
-}
-
-/**
- * Implements hook_civicrm_upgrade().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
- */
-function legacycustomsearches_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  return _legacycustomsearches_civix_civicrm_upgrade($op, $queue);
-}
-
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
- */
-function legacycustomsearches_civicrm_entityTypes(&$entityTypes) {
-  _legacycustomsearches_civix_civicrm_entityTypes($entityTypes);
-}
diff --git a/civicrm/ext/legacycustomsearches/templates/CRM/Contact/Form/Search/Custom/MultipleValuesCriteria.tpl b/civicrm/ext/legacycustomsearches/templates/CRM/Contact/Form/Search/Custom/MultipleValuesCriteria.tpl
index 3326b6fbfd2f4fd5325a15138337c008a2dc2476..1b3ab635b7daa5c11c5cef74464a46ea3d4d3e8d 100644
--- a/civicrm/ext/legacycustomsearches/templates/CRM/Contact/Form/Search/Custom/MultipleValuesCriteria.tpl
+++ b/civicrm/ext/legacycustomsearches/templates/CRM/Contact/Form/Search/Custom/MultipleValuesCriteria.tpl
@@ -86,7 +86,7 @@
                 </div>
             </div>
 
-        <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+        <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
     </div><!-- /.crm-accordion-body -->
     </div><!-- /.crm-accordion-wrapper -->
 </div><!-- /.crm-form-block -->
diff --git a/civicrm/ext/message_admin/info.xml b/civicrm/ext/message_admin/info.xml
index 27dd551427715bd944df6b93db4435f35bdc79bb..2807a6109b04f29918b87287ae778a1b08ebe56d 100644
--- a/civicrm/ext/message_admin/info.xml
+++ b/civicrm/ext/message_admin/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2021-06-12</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>alpha</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <requires>
     <ext>org.civicrm.afform</ext>
@@ -31,9 +31,10 @@
   <mixins>
     <mixin>ang-php@1.0.0</mixin>
     <mixin>menu-xml@1.0.0</mixin>
+    <mixin>smarty-v2@1.0.0</mixin>
   </mixins>
   <civix>
     <namespace>CRM/MessageAdmin</namespace>
-    <format>22.12.1</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/message_admin/message_admin.civix.php b/civicrm/ext/message_admin/message_admin.civix.php
index b386a62dc72dc94c064415741459afc7d43bc7fe..78da356905da30a46d2d36d1e5284abd7c0838b1 100644
--- a/civicrm/ext/message_admin/message_admin.civix.php
+++ b/civicrm/ext/message_admin/message_admin.civix.php
@@ -92,9 +92,6 @@ function _message_admin_civix_civicrm_config($config = NULL) {
   $configured = TRUE;
 
   $extRoot = __DIR__ . DIRECTORY_SEPARATOR;
-  $extDir = $extRoot . 'templates';
-  CRM_Core_Smarty::singleton()->addTemplateDir($extDir);
-
   $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
   set_include_path($include_path);
   // Based on <compatibility>, this does not currently require mixin/polyfill.php.
@@ -201,14 +198,3 @@ function _message_admin_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $paren
     }
   }
 }
-
-/**
- * (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 _message_admin_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/message_admin/message_admin.php b/civicrm/ext/message_admin/message_admin.php
index 872e322729f4fbee94b61262979ef47e1e8b5fe0..41fa59a3461112ac342b3d59d81c38aa6e9d5d4e 100644
--- a/civicrm/ext/message_admin/message_admin.php
+++ b/civicrm/ext/message_admin/message_admin.php
@@ -32,17 +32,6 @@ function message_admin_civicrm_enable() {
   _message_admin_civix_civicrm_enable();
 }
 
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
- */
-function message_admin_civicrm_entityTypes(&$entityTypes) {
-  _message_admin_civix_civicrm_entityTypes($entityTypes);
-}
-
 // --- Functions below this ship commented out. Uncomment as required. ---
 
 /**
diff --git a/civicrm/ext/oauth-client/Civi/Api4/Action/OAuthClient/AuthorizationCode.php b/civicrm/ext/oauth-client/Civi/Api4/Action/OAuthClient/AuthorizationCode.php
index f9c896a5b0b3a997a30c219526da3fef7a3d9059..73a01aaf0f4e2c25583175d529e94163e1305cbe 100644
--- a/civicrm/ext/oauth-client/Civi/Api4/Action/OAuthClient/AuthorizationCode.php
+++ b/civicrm/ext/oauth-client/Civi/Api4/Action/OAuthClient/AuthorizationCode.php
@@ -91,20 +91,19 @@ class AuthorizationCode extends AbstractGrantAction {
     parent::validate();
     if ($this->landingUrl) {
       $landingUrlParsed = parse_url($this->landingUrl);
-      $landingUrlIp = gethostbyname($landingUrlParsed['host']);
+      $landingUrlIp = gethostbyname($landingUrlParsed['host'] . '.');
       $allowedBases = [
         \Civi::paths()->getVariable('cms.root', 'url'),
         \Civi::paths()->getVariable('civicrm.root', 'url'),
       ];
-      $ok = max(array_map(function($allowed) use ($landingUrlParsed, $landingUrlIp) {
+      foreach ($allowedBases as $allowed) {
         $allowedParsed = parse_url($allowed);
-        $allowedIp = gethostbyname($allowedParsed['host']);
-        $ok = $landingUrlIp === $allowedIp && $landingUrlParsed['scheme'] == $allowedParsed['scheme'];
-        return (int) $ok;
-      }, $allowedBases));
-      if (!$ok) {
-        throw new OAuthException("Cannot initiate OAuth. Unsupported landing URL.");
+        $allowedIp = gethostbyname($allowedParsed['host'] . '.');
+        if ($landingUrlIp === $allowedIp && $landingUrlParsed['scheme'] == $allowedParsed['scheme']) {
+          return;
+        }
       }
+      throw new OAuthException("Cannot initiate OAuth. Unsupported landing URL.");
     }
   }
 
diff --git a/civicrm/ext/oauth-client/Civi/OAuth/OAuthLeagueFacade.php b/civicrm/ext/oauth-client/Civi/OAuth/OAuthLeagueFacade.php
index 27d0c18fcbe69b642b46a209ce7ca9140b6a140d..be66cfe0c7bff4ed7f106a5a86d73303ed278c94 100644
--- a/civicrm/ext/oauth-client/Civi/OAuth/OAuthLeagueFacade.php
+++ b/civicrm/ext/oauth-client/Civi/OAuth/OAuthLeagueFacade.php
@@ -36,13 +36,12 @@ class OAuthLeagueFacade {
     $localOptions['clientId'] = $clientDef['guid'];
     $localOptions['tenant'] = !empty($clientDef['tenant']) ? $clientDef['tenant'] : '';
     $localOptions['clientSecret'] = $clientDef['secret'];
-    // NOTE: If we ever have frontend users, this may need to change.
-    $localOptions['redirectUri'] = \CRM_OAuth_BAO_OAuthClient::getRedirectUri();
     $options = array_merge(
       $providerDef['options'] ?? [],
       $clientDef['options'] ?? [],
       $localOptions
     );
+    $options['redirectUri'] = $options['redirectUri'] ?? \CRM_OAuth_BAO_OAuthClient::getRedirectUri();
 
     return [$class, $options];
   }
diff --git a/civicrm/ext/oauth-client/info.xml b/civicrm/ext/oauth-client/info.xml
index b5f07c6f097bce8fc81309f33926b73781e6f2d6..5276987323ff87532fee2f266a5a0b328b147f04 100644
--- a/civicrm/ext/oauth-client/info.xml
+++ b/civicrm/ext/oauth-client/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-10-23</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <requires>
     <ext version="~4.5">org.civicrm.afform</ext>
@@ -33,9 +33,11 @@
     <mixin>ang-php@1.0.0</mixin>
     <mixin>menu-xml@1.0.0</mixin>
     <mixin>setting-php@1.0.0</mixin>
+    <mixin>smarty-v2@1.0.0</mixin>
+    <mixin>entity-types-php@1.0.0</mixin>
   </mixins>
   <civix>
     <namespace>CRM/OAuth</namespace>
-    <format>22.12.1</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/oauth-client/oauth_client.civix.php b/civicrm/ext/oauth-client/oauth_client.civix.php
index 91cdf5cfa0fd33d4cdc0d31402d157161e767077..bf8cc5fd0aa5cf758c84ae9c8b586783fb48a279 100644
--- a/civicrm/ext/oauth-client/oauth_client.civix.php
+++ b/civicrm/ext/oauth-client/oauth_client.civix.php
@@ -92,9 +92,6 @@ function _oauth_client_civix_civicrm_config($config = NULL) {
   $configured = TRUE;
 
   $extRoot = __DIR__ . DIRECTORY_SEPARATOR;
-  $extDir = $extRoot . 'templates';
-  CRM_Core_Smarty::singleton()->addTemplateDir($extDir);
-
   $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
   set_include_path($include_path);
   // Based on <compatibility>, this does not currently require mixin/polyfill.php.
@@ -201,30 +198,3 @@ function _oauth_client_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parent
     }
   }
 }
-
-/**
- * (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 _oauth_client_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, [
-    'CRM_OAuth_DAO_OAuthClient' => [
-      'name' => 'OAuthClient',
-      'class' => 'CRM_OAuth_DAO_OAuthClient',
-      'table' => 'civicrm_oauth_client',
-    ],
-    'CRM_OAuth_DAO_OAuthContactToken' => [
-      'name' => 'OAuthContactToken',
-      'class' => 'CRM_OAuth_DAO_OAuthContactToken',
-      'table' => 'civicrm_oauth_contact_token',
-    ],
-    'CRM_OAuth_DAO_OAuthSysToken' => [
-      'name' => 'OAuthSysToken',
-      'class' => 'CRM_OAuth_DAO_OAuthSysToken',
-      'table' => 'civicrm_oauth_systoken',
-    ],
-  ]);
-}
diff --git a/civicrm/ext/oauth-client/oauth_client.php b/civicrm/ext/oauth-client/oauth_client.php
index c1260c0fd6ca4b88a054d2a0cac6ab5508d66ef8..fd23498bf8f2585ba7737e688a575a249e338679 100644
--- a/civicrm/ext/oauth-client/oauth_client.php
+++ b/civicrm/ext/oauth-client/oauth_client.php
@@ -44,17 +44,6 @@ function oauth_client_civicrm_permission(&$permissions) {
   ];
 }
 
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
- */
-function oauth_client_civicrm_entityTypes(&$entityTypes) {
-  _oauth_client_civix_civicrm_entityTypes($entityTypes);
-}
-
 /**
  * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
  */
diff --git a/civicrm/ext/payflowpro/info.xml b/civicrm/ext/payflowpro/info.xml
index 5aa54aaf0a21998f8c50debf2882057f003a4b3a..49a8e72907f623eea88855ea0f3d8f1cf95c06cc 100644
--- a/civicrm/ext/payflowpro/info.xml
+++ b/civicrm/ext/payflowpro/info.xml
@@ -15,10 +15,10 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2021-04-13</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>This extension is extraction of the original Core Payflow Pro Payment Processor</comments>
   <classloader>
diff --git a/civicrm/ext/recaptcha/info.xml b/civicrm/ext/recaptcha/info.xml
index 7eb2eca5e111ffccecc78184e86f7fbbd5b22c60..a110ae64ea599660da6780399b3649495fe6a8d0 100644
--- a/civicrm/ext/recaptcha/info.xml
+++ b/civicrm/ext/recaptcha/info.xml
@@ -13,13 +13,13 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2021-04-03</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <classloader>
     <psr0 prefix="CRM_" path="."/>
@@ -30,9 +30,10 @@
     <mixin>setting-php@1.0.0</mixin>
     <mixin>ang-php@1.0.0</mixin>
     <mixin>scan-classes@1.0.0</mixin>
+    <mixin>smarty-v2@1.0.0</mixin>
   </mixins>
   <civix>
     <namespace>CRM/Recaptcha</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/recaptcha/recaptcha.civix.php b/civicrm/ext/recaptcha/recaptcha.civix.php
index 67c3221da5e7fba9e23ddde4ee48e833c11f1776..13f0af1218f2a0ad575e81029ea01d6149efb5b6 100644
--- a/civicrm/ext/recaptcha/recaptcha.civix.php
+++ b/civicrm/ext/recaptcha/recaptcha.civix.php
@@ -84,27 +84,17 @@ use CRM_Recaptcha_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _recaptcha_civix_civicrm_config(&$config = NULL) {
+function _recaptcha_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
   }
   $configured = TRUE;
 
-  $template = CRM_Core_Smarty::singleton();
-
   $extRoot = __DIR__ . 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);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -114,35 +104,7 @@ function _recaptcha_civix_civicrm_config(&$config = NULL) {
  */
 function _recaptcha_civix_civicrm_install() {
   _recaptcha_civix_civicrm_config();
-  if ($upgrader = _recaptcha_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _recaptcha_civix_civicrm_postInstall() {
-  _recaptcha_civix_civicrm_config();
-  if ($upgrader = _recaptcha_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 _recaptcha_civix_civicrm_uninstall(): void {
-  _recaptcha_civix_civicrm_config();
-  if ($upgrader = _recaptcha_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -152,56 +114,7 @@ function _recaptcha_civix_civicrm_uninstall(): void {
  */
 function _recaptcha_civix_civicrm_enable(): void {
   _recaptcha_civix_civicrm_config();
-  if ($upgrader = _recaptcha_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 _recaptcha_civix_civicrm_disable(): void {
-  _recaptcha_civix_civicrm_config();
-  if ($upgrader = _recaptcha_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 _recaptcha_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _recaptcha_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_Recaptcha_Upgrader
- */
-function _recaptcha_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/Recaptcha/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_Recaptcha_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -285,14 +198,3 @@ function _recaptcha_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID)
     }
   }
 }
-
-/**
- * (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 _recaptcha_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/ext/recaptcha/recaptcha.php b/civicrm/ext/recaptcha/recaptcha.php
index 64c46c522563f934f1837eff2d3244ae19fbf6af..e959cfb329c9323502ba1afd5a8801435c1537fe 100644
--- a/civicrm/ext/recaptcha/recaptcha.php
+++ b/civicrm/ext/recaptcha/recaptcha.php
@@ -23,24 +23,6 @@ function recaptcha_civicrm_install() {
   _recaptcha_civix_civicrm_install();
 }
 
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function recaptcha_civicrm_postInstall() {
-  _recaptcha_civix_civicrm_postInstall();
-}
-
-/**
- * Implements hook_civicrm_uninstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall
- */
-function recaptcha_civicrm_uninstall() {
-  _recaptcha_civix_civicrm_uninstall();
-}
-
 /**
  * Implements hook_civicrm_enable().
  *
@@ -50,35 +32,6 @@ function recaptcha_civicrm_enable() {
   _recaptcha_civix_civicrm_enable();
 }
 
-/**
- * Implements hook_civicrm_disable().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable
- */
-function recaptcha_civicrm_disable() {
-  _recaptcha_civix_civicrm_disable();
-}
-
-/**
- * Implements hook_civicrm_upgrade().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade
- */
-function recaptcha_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  return _recaptcha_civix_civicrm_upgrade($op, $queue);
-}
-
-/**
- * Implements hook_civicrm_entityTypes().
- *
- * Declare entity types provided by this module.
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
- */
-function recaptcha_civicrm_entityTypes(&$entityTypes) {
-  _recaptcha_civix_civicrm_entityTypes($entityTypes);
-}
-
 /**
  * Implements hook_civicrm_navigationMenu().
  */
diff --git a/civicrm/ext/search_kit/CRM/Search/Upgrader.php b/civicrm/ext/search_kit/CRM/Search/Upgrader.php
index 8451a3c49187caa25fb019bf3771fefe3350934c..aa37914a90dce868004463e28e5f8a6cfb232767 100644
--- a/civicrm/ext/search_kit/CRM/Search/Upgrader.php
+++ b/civicrm/ext/search_kit/CRM/Search/Upgrader.php
@@ -4,7 +4,7 @@ use CRM_Search_ExtensionUtil as E;
 /**
  * Collection of upgrade steps.
  */
-class CRM_Search_Upgrader extends CRM_Search_Upgrader_Base {
+class CRM_Search_Upgrader extends CRM_Extension_Upgrader_Base {
 
   /**
    * Upgrade 1000 - install schema
diff --git a/civicrm/ext/search_kit/CRM/Search/Upgrader/Base.php b/civicrm/ext/search_kit/CRM/Search/Upgrader/Base.php
deleted file mode 100644
index 4efc0e415f044b9698728a1f9f811b086d057a37..0000000000000000000000000000000000000000
--- a/civicrm/ext/search_kit/CRM/Search/Upgrader/Base.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-/**
- * Base class which provides helpers to execute upgrade logic
- */
-class CRM_Search_Upgrader_Base extends CRM_Extension_Upgrader_Base {
-}
diff --git a/civicrm/ext/search_kit/Civi/Api4/Action/SearchDisplay/GetSearchTasks.php b/civicrm/ext/search_kit/Civi/Api4/Action/SearchDisplay/GetSearchTasks.php
index b2ca9c97c04f0e6f1a5be3a5046d69daab19286b..f1fb340a889fba9a142308e8104c7bec5c848621 100644
--- a/civicrm/ext/search_kit/Civi/Api4/Action/SearchDisplay/GetSearchTasks.php
+++ b/civicrm/ext/search_kit/Civi/Api4/Action/SearchDisplay/GetSearchTasks.php
@@ -206,13 +206,15 @@ class GetSearchTasks extends \Civi\Api4\Generic\AbstractAction {
       }
     }
 
-    // Call `hook_civicrm_searchKitTasks`.
-    // Note - this hook serves 2 purposes, both to augment this list of tasks AND to
-    // get a full list of Angular modules which provide tasks. That's why this hook needs
-    // the base-level array and not just the array of tasks for `$this->entity`.
-    // Although it may seem wasteful to have extensions add tasks for all possible entities and then
-    // discard most of it (all but the ones relevant to `$this->entity`), it's necessary to do it this way
-    // so that they can be declared as angular dependencies - see search_kit_civicrm_angularModules().
+    // Call `hook_civicrm_searchKitTasks` which serves 3 purposes:
+    // 1. For extensions to augment this list of tasks
+    // 2. To allow tasks to be added/removed per search display
+    //    Note: Use Events::W_LATE to do so after the tasks are filtered per search-display settings.
+    // 3. To get a full list of Angular modules which provide tasks.
+    //    Note: That's why this hook needs the base-level array and not just the array of tasks for `$this->entity`.
+    //    Although it may seem wasteful to have extensions add tasks for all possible entities and then
+    //    discard most of it (all but the ones relevant to `$this->entity`), it's necessary to do it this way
+    //    so that they can be declared as angular dependencies - see search_kit_civicrm_angularModules().
     $null = NULL;
     $checkPermissions = $this->checkPermissions;
     $userId = $this->checkPermissions ? \CRM_Core_Session::getLoggedInContactID() : NULL;
@@ -234,7 +236,7 @@ class GetSearchTasks extends \Civi\Api4\Generic\AbstractAction {
     $result->exchangeArray($tasks[$entity['name']]);
   }
 
-  public static function fields() {
+  public static function fields(): array {
     return [
       [
         'name' => 'name',
diff --git a/civicrm/ext/search_kit/Civi/Api4/Event/Subscriber/SearchDisplayTasksSubscriber.php b/civicrm/ext/search_kit/Civi/Api4/Event/Subscriber/SearchDisplayTasksSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..832ce2808e62c703dc4ed7d5a8df08831ff96db6
--- /dev/null
+++ b/civicrm/ext/search_kit/Civi/Api4/Event/Subscriber/SearchDisplayTasksSubscriber.php
@@ -0,0 +1,48 @@
+<?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       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\Core\Event\GenericHookEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Event subscriber to filter search display tasks
+ * @service
+ * @internal
+ */
+class SearchDisplayTasksSubscriber extends \Civi\Core\Service\AutoService implements EventSubscriberInterface {
+
+  /**
+   * Filter tasks with a priority of -50, which allows W_MIDDLE & W_EARLY to go first, but W_LATE to go after.
+   *
+   * @return array
+   */
+  public static function getSubscribedEvents() {
+    return [
+      'hook_civicrm_searchKitTasks' => [
+        ['filterTasksForDisplay', -50],
+      ],
+    ];
+  }
+
+  /**
+   * @param \Civi\Core\Event\GenericHookEvent $event
+   */
+  public function filterTasksForDisplay(GenericHookEvent $event): void {
+    $enabledActions = $event->display['settings']['actions'] ?? NULL;
+    $entityName = $event->search['api_entity'] ?? NULL;
+    if ($entityName && is_array($enabledActions)) {
+      $event->tasks[$entityName] = array_intersect_key($event->tasks[$entityName], array_flip($enabledActions));
+    }
+  }
+
+}
diff --git a/civicrm/ext/search_kit/Civi/Search/Admin.php b/civicrm/ext/search_kit/Civi/Search/Admin.php
index ee85568afd35e737fb4dda130a19e915dbeabc69..4967484f6f8d0b9861b8c98befe1c0dd3560c3d3 100644
--- a/civicrm/ext/search_kit/Civi/Search/Admin.php
+++ b/civicrm/ext/search_kit/Civi/Search/Admin.php
@@ -191,9 +191,10 @@ class Admin {
         foreach (array_reverse($entity['fields'], TRUE) as $index => $field) {
           if (!empty($field['fk_entity']) && !$field['options'] && !empty($schema[$field['fk_entity']]['label_field'])) {
             $isCustom = strpos($field['name'], '.');
-            // Custom fields: append "Contact ID" to original field label
+            // Custom fields: append "Contact ID" etc. to original field label
             if ($isCustom) {
-              $entity['fields'][$index]['label'] .= ' ' . E::ts('Contact ID');
+              $idField = array_column($schema[$field['fk_entity']]['fields'], NULL, 'name')['id'];
+              $entity['fields'][$index]['label'] .= ' ' . $idField['title'];
             }
             // DAO fields: use title instead of label since it represents the id (title usually ends in ID but label does not)
             else {
diff --git a/civicrm/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.html b/civicrm/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.html
index 51d0d2a47c7a61f14edd6f23218734a90fcc70be..11a1765f098dd66a3b3e84549477e450fa70e61c 100644
--- a/civicrm/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.html
+++ b/civicrm/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.html
@@ -71,7 +71,7 @@
   <tfoot>
     <tr>
       <td colspan="6" class="form-inline">
-        <select class="form-control crm-search-admin-add-link" ng-show="$ctrl.links.length">
+        <select class="form-control crm-search-admin-add-link">
           <option value="">
             + {{:: ts('Add...') }}
           </option>
diff --git a/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/common/searchAdminTasksConfig.component.js b/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/common/searchAdminTasksConfig.component.js
new file mode 100644
index 0000000000000000000000000000000000000000..11887b95709c3ee2f371333a2623f4dc7a3e9181
--- /dev/null
+++ b/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/common/searchAdminTasksConfig.component.js
@@ -0,0 +1,58 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('crmSearchAdmin').component('searchAdminTasksConfig', {
+    bindings: {
+      display: '<',
+      apiEntity: '<',
+      apiParams: '<'
+    },
+    templateUrl: '~/crmSearchAdmin/displays/common/searchAdminTasksConfig.html',
+    controller: function($scope, crmApi4, $timeout) {
+      var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
+        ctrl = this;
+
+      this.$onInit = function() {
+        crmApi4('SearchDisplay', 'getSearchTasks', {
+          entity: ctrl.apiEntity,
+          savedSearch: {api_entity: ctrl.apiEntity, api_params: ctrl.apiParams}
+        }).then(function(tasks) {
+          ctrl.allTasks = tasks;
+        });
+      };
+
+      this.toggleActions = function() {
+        this.display.settings.actions = !this.display.settings.actions;
+        ctrl.menuOpen = false;
+      };
+
+      this.toggleTask = function(name) {
+        // Timeout waits for checkbox state to change, otherwise checkbox 'checked' property gets out-of-sync with angular model
+        $timeout(function() {
+          // Disabling one when all enabled, convert to array
+          if (typeof ctrl.display.settings.actions === 'boolean') {
+            ctrl.display.settings.actions = _.map(ctrl.allTasks, 'name');
+          }
+          // Remove enabled task
+          if (ctrl.display.settings.actions.includes(name)) {
+            _.pull(ctrl.display.settings.actions, name);
+          }
+          // Add disabled task
+          else {
+            ctrl.display.settings.actions.push(name);
+          }
+          // All enabled, convert to boolean
+          if (ctrl.display.settings.actions.length === ctrl.allTasks.length) {
+            ctrl.display.settings.actions = true;
+          }
+        });
+      };
+
+      this.isEnabled = function(name) {
+        return (typeof this.display.settings.actions === 'boolean') || this.display.settings.actions.includes(name);
+      };
+
+    }
+  });
+
+})(angular, CRM.$, CRM._);
diff --git a/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/common/searchAdminTasksConfig.html b/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/common/searchAdminTasksConfig.html
new file mode 100644
index 0000000000000000000000000000000000000000..b6ae4adc2c6694b2e4166867c5573476535fb669
--- /dev/null
+++ b/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/common/searchAdminTasksConfig.html
@@ -0,0 +1,26 @@
+<div class="checkbox-inline form-control">
+  <label>
+    <input type="checkbox" ng-checked="$ctrl.display.settings.actions" ng-click="$ctrl.toggleActions()">
+    <span>{{:: ts('Actions Menu') }}</span>
+  </label>
+</div>
+<div class="input-group" ng-if="$ctrl.display.settings.actions">
+  <div class="input-group-btn">
+    <button type="button" ng-click="$ctrl.menuOpen = true" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+      <span ng-if="$ctrl.display.settings.actions === true">{{:: ts('All Enabled') }}</span>
+      <span ng-if="$ctrl.display.settings.actions !== true">{{ ts('%1 Enabled', {1: $ctrl.display.settings.actions.length}) }}</span>
+      <span class="caret"></span>
+    </button>
+    <ul class="dropdown-menu" ng-if="$ctrl.menuOpen">
+      <li ng-if="!$ctrl.allTasks" class="disabled">
+        <a href><i class="crm-i fa-spinner fa-spin"></i></a>
+      </li>
+      <li ng-repeat="task in $ctrl.allTasks">
+        <a href ng-click="$ctrl.toggleTask(task.name); $event.stopPropagation()">
+          <input type="checkbox" ng-checked="$ctrl.isEnabled(task.name)">
+          {{:: task.title }}
+        </a>
+      </li>
+    </ul>
+  </div>
+</div>
diff --git a/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html b/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html
index 9de37446b50160eec37f14c6e2bf78071a6bf1e3..737dbdd47973e1cfff2dc3e4100a42122b0132fc 100644
--- a/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html
+++ b/civicrm/ext/search_kit/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html
@@ -10,12 +10,9 @@
     </div>
   </div>
   <div class="form-inline">
-    <div class="checkbox-inline form-control">
-      <label>
-        <input type="checkbox" ng-model="$ctrl.display.settings.actions">
-        <span>{{:: ts('Enable Actions') }}</span>
-      </label>
-    </div>
+    <search-admin-tasks-config display="$ctrl.display" api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams"></search-admin-tasks-config>
+  </div>
+  <div class="form-inline">
     <div class="form-group" ng-include="'~/crmSearchAdmin/displays/common/searchButtonConfig.html'"></div>
   </div>
   <div class="form-inline">
diff --git a/civicrm/ext/search_kit/ang/crmSearchAdmin/resultsTable/debug.html b/civicrm/ext/search_kit/ang/crmSearchAdmin/resultsTable/debug.html
index d435faed5d5bcebe1a526f4766450e37417adaf6..f80476cdaf6fa31497fc7356301a4e1cb73367dd 100644
--- a/civicrm/ext/search_kit/ang/crmSearchAdmin/resultsTable/debug.html
+++ b/civicrm/ext/search_kit/ang/crmSearchAdmin/resultsTable/debug.html
@@ -9,9 +9,8 @@
       <strong>API:</strong>
     </div>
     <pre>{{ $ctrl.debug.apiParams }}</pre>
-    <div ng-if="$ctrl.debug.sql">
-      <strong>SQL:</strong>
-    </div>
+    <strong>SQL:</strong>
+    <pre ng-if="!$ctrl.debug.sql">{{:: ts('Run search to view SQL') }}</pre>
     <pre ng-repeat="query in $ctrl.debug.sql">{{ query }}</pre>
   </div>
 </fieldset>
diff --git a/civicrm/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js b/civicrm/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js
index 69d59c8010c45031450f756fb016f64ce0dc4f32..c9b75271f1dc10777b5220eb706a12aca268e635 100644
--- a/civicrm/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js
+++ b/civicrm/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js
@@ -56,7 +56,7 @@
             ['Group AS group', 'LEFT', ['id', '=', 'group.saved_search_id']],
             ['EntityTag AS entity_tag', 'LEFT', ['entity_tag.entity_table', '=', '"civicrm_saved_search"'], ['id', '=', 'entity_tag.entity_id']],
           ],
-          where: [['api_entity', 'IS NOT NULL']],
+          where: [['api_entity', 'IS NOT NULL'], ['is_current', '=', true]],
           groupBy: ['id']
         }
       };
diff --git a/civicrm/ext/search_kit/info.xml b/civicrm/ext/search_kit/info.xml
index ed8ca3afd2f53a9d2694a2651398d6f79fe50264..e4fab06e36d9ee3046960902f3609aa3a5d7034d 100644
--- a/civicrm/ext/search_kit/info.xml
+++ b/civicrm/ext/search_kit/info.xml
@@ -15,13 +15,13 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2021-01-06</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <develStage>stable</develStage>
   <tags>
     <tag>mgmt:required</tag>
   </tags>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <comments>Click on the chat link above to discuss development, report problems or ask questions.</comments>
   <classloader>
@@ -39,6 +39,6 @@
   </mixins>
   <civix>
     <namespace>CRM/Search</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/search_kit/search_kit.civix.php b/civicrm/ext/search_kit/search_kit.civix.php
index b74feb61fc6be7d47ba5853fa19ec5c684074691..a0be0f9032eb7f883e696d715a7589d37ad2453d 100644
--- a/civicrm/ext/search_kit/search_kit.civix.php
+++ b/civicrm/ext/search_kit/search_kit.civix.php
@@ -84,7 +84,7 @@ use CRM_Search_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _search_kit_civix_civicrm_config(&$config = NULL) {
+function _search_kit_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
@@ -92,9 +92,9 @@ function _search_kit_civix_civicrm_config(&$config = NULL) {
   $configured = TRUE;
 
   $extRoot = __DIR__ . DIRECTORY_SEPARATOR;
-
   $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
   set_include_path($include_path);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -104,35 +104,7 @@ function _search_kit_civix_civicrm_config(&$config = NULL) {
  */
 function _search_kit_civix_civicrm_install() {
   _search_kit_civix_civicrm_config();
-  if ($upgrader = _search_kit_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _search_kit_civix_civicrm_postInstall() {
-  _search_kit_civix_civicrm_config();
-  if ($upgrader = _search_kit_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_kit_civix_civicrm_uninstall(): void {
-  _search_kit_civix_civicrm_config();
-  if ($upgrader = _search_kit_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -142,56 +114,7 @@ function _search_kit_civix_civicrm_uninstall(): void {
  */
 function _search_kit_civix_civicrm_enable(): void {
   _search_kit_civix_civicrm_config();
-  if ($upgrader = _search_kit_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_kit_civix_civicrm_disable(): void {
-  _search_kit_civix_civicrm_config();
-  if ($upgrader = _search_kit_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_kit_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _search_kit_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_Search_Upgrader
- */
-function _search_kit_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/Search/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_Search_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -275,25 +198,3 @@ function _search_kit_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID
     }
   }
 }
-
-/**
- * (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_kit_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, [
-    'CRM_Search_DAO_SearchDisplay' => [
-      'name' => 'SearchDisplay',
-      'class' => 'CRM_Search_DAO_SearchDisplay',
-      'table' => 'civicrm_search_display',
-    ],
-    'CRM_Search_DAO_SearchSegment' => [
-      'name' => 'SearchSegment',
-      'class' => 'CRM_Search_DAO_SearchSegment',
-      'table' => 'civicrm_search_segment',
-    ],
-  ]);
-}
diff --git a/civicrm/ext/sequentialcreditnotes/info.xml b/civicrm/ext/sequentialcreditnotes/info.xml
index 1fb3612d038f6e654d1b9d7fe42f94603080f402..d938f5f57288c534b2fcab9216ec289ff8fb6dbc 100644
--- a/civicrm/ext/sequentialcreditnotes/info.xml
+++ b/civicrm/ext/sequentialcreditnotes/info.xml
@@ -15,19 +15,19 @@
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
   <releaseDate>2020-01-28</releaseDate>
-  <version>5.59.4</version>
+  <version>5.60.0</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
   <develStage>stable</develStage>
   <compatibility>
-    <ver>5.59</ver>
+    <ver>5.60</ver>
   </compatibility>
   <mixins>
     <mixin>setting-php@1.0.0</mixin>
   </mixins>
   <civix>
     <namespace>CRM/Sequentialcreditnotes</namespace>
-    <format>22.10.0</format>
+    <format>23.02.0</format>
   </civix>
 </extension>
diff --git a/civicrm/ext/sequentialcreditnotes/sequentialcreditnotes.civix.php b/civicrm/ext/sequentialcreditnotes/sequentialcreditnotes.civix.php
index e17812d6abf4a43216fc19dce804313150885641..4b35c35b763420465b76accc1db356a161f15623 100644
--- a/civicrm/ext/sequentialcreditnotes/sequentialcreditnotes.civix.php
+++ b/civicrm/ext/sequentialcreditnotes/sequentialcreditnotes.civix.php
@@ -84,27 +84,17 @@ use CRM_Sequentialcreditnotes_ExtensionUtil as E;
  *
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config
  */
-function _sequentialcreditnotes_civix_civicrm_config(&$config = NULL) {
+function _sequentialcreditnotes_civix_civicrm_config($config = NULL) {
   static $configured = FALSE;
   if ($configured) {
     return;
   }
   $configured = TRUE;
 
-  $template = CRM_Core_Smarty::singleton();
-
   $extRoot = __DIR__ . 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);
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -114,35 +104,7 @@ function _sequentialcreditnotes_civix_civicrm_config(&$config = NULL) {
  */
 function _sequentialcreditnotes_civix_civicrm_install() {
   _sequentialcreditnotes_civix_civicrm_config();
-  if ($upgrader = _sequentialcreditnotes_civix_upgrader()) {
-    $upgrader->onInstall();
-  }
-}
-
-/**
- * Implements hook_civicrm_postInstall().
- *
- * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall
- */
-function _sequentialcreditnotes_civix_civicrm_postInstall() {
-  _sequentialcreditnotes_civix_civicrm_config();
-  if ($upgrader = _sequentialcreditnotes_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 _sequentialcreditnotes_civix_civicrm_uninstall(): void {
-  _sequentialcreditnotes_civix_civicrm_config();
-  if ($upgrader = _sequentialcreditnotes_civix_upgrader()) {
-    $upgrader->onUninstall();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -152,56 +114,7 @@ function _sequentialcreditnotes_civix_civicrm_uninstall(): void {
  */
 function _sequentialcreditnotes_civix_civicrm_enable(): void {
   _sequentialcreditnotes_civix_civicrm_config();
-  if ($upgrader = _sequentialcreditnotes_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 _sequentialcreditnotes_civix_civicrm_disable(): void {
-  _sequentialcreditnotes_civix_civicrm_config();
-  if ($upgrader = _sequentialcreditnotes_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 _sequentialcreditnotes_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
-  if ($upgrader = _sequentialcreditnotes_civix_upgrader()) {
-    return $upgrader->onUpgrade($op, $queue);
-  }
-}
-
-/**
- * @return CRM_Sequentialcreditnotes_Upgrader
- */
-function _sequentialcreditnotes_civix_upgrader() {
-  if (!file_exists(__DIR__ . '/CRM/Sequentialcreditnotes/Upgrader.php')) {
-    return NULL;
-  }
-  else {
-    return CRM_Sequentialcreditnotes_Upgrader_Base::instance();
-  }
+  // Based on <compatibility>, this does not currently require mixin/polyfill.php.
 }
 
 /**
@@ -285,14 +198,3 @@ function _sequentialcreditnotes_civix_fixNavigationMenuItems(&$nodes, &$maxNavID
     }
   }
 }
-
-/**
- * (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 _sequentialcreditnotes_civix_civicrm_entityTypes(&$entityTypes) {
-  $entityTypes = array_merge($entityTypes, []);
-}
diff --git a/civicrm/js/Common.js b/civicrm/js/Common.js
index 7d269480584a1e1f0c34d54079e713389b7eea51..021254d07ccc26769ae4c8d3c9e3771bf0a52594 100644
--- a/civicrm/js/Common.js
+++ b/civicrm/js/Common.js
@@ -1133,6 +1133,9 @@ if (!CRM.vars) CRM.vars = {};
       }
       $('.crm-select2:not(.select2-offscreen, .select2-container)', e.target).crmSelect2();
       $('.crm-form-entityref:not(.select2-offscreen, .select2-container)', e.target).crmEntityRef();
+      $('.crm-form-autocomplete:not(.select2-offscreen, .select2-container)[data-api-entity]', e.target).each(function() {
+        $(this).crmAutocomplete($(this).data('apiEntity'), $(this).data('apiParams'), $(this).data('selectParams'));
+      });
       $('select.crm-chain-select-control', e.target).off('.chainSelect').on('change.chainSelect', chainSelect);
       $('.crm-form-text[data-crm-datepicker]', e.target).each(function() {
         $(this).crmDatepicker($(this).data('crmDatepicker'));
diff --git a/civicrm/mixin/smarty-v2@1/mixin.php b/civicrm/mixin/smarty-v2@1/mixin.php
index 0b371057cce7efb4228c109e2c4aea8e34b24151..5972dbdc57ffd6badbb1eb84f628700ea9727b09 100644
--- a/civicrm/mixin/smarty-v2@1/mixin.php
+++ b/civicrm/mixin/smarty-v2@1/mixin.php
@@ -4,7 +4,7 @@
  * Auto-register "templates/" folder.
  *
  * @mixinName smarty-v2
- * @mixinVersion 1.0.0
+ * @mixinVersion 1.0.1
  * @since 5.59
  *
  * @param CRM_Extension_MixInfo $mixInfo
@@ -19,14 +19,9 @@ return function ($mixInfo, $bootCache) {
   }
 
   $register = function() use ($dir) {
-    // This implementation is useful for older versions of CiviCRM. It can be replaced/updated going forward (v1.1+).
-    $smarty = CRM_Core_Smarty::singleton();
-    if (!is_array($smarty->template_dir)) {
-      $this->template_dir = [$smarty->template_dir];
-    }
-    if (!in_array($dir, $smarty->template_dir)) {
-      array_unshift($smarty->template_dir, $dir);
-    }
+    // This implementation has a theoretical edge-case bug on older versions of CiviCRM where a template could
+    // be registered more than once.
+    CRM_Core_Smarty::singleton()->addTemplateDir($dir);
   };
 
   // Let's figure out what environment we're in -- so that we know the best way to call $register().
diff --git a/civicrm/release-notes.md b/civicrm/release-notes.md
index 7741436a04422056b77dddd1f31ee84a0276ead0..301b09319089ac999bcd414279b405e402329c0e 100644
--- a/civicrm/release-notes.md
+++ b/civicrm/release-notes.md
@@ -15,41 +15,16 @@ Other resources for identifying changes are:
     * https://github.com/civicrm/civicrm-joomla
     * https://github.com/civicrm/civicrm-wordpress
 
-## CiviCRM 5.59.4
+## CiviCRM 5.60.0
 
-Released March 27, 2023
+Released April 5, 2023
 
-- **[Synopsis](release-notes/5.59.4.md#synopsis)**
-- **[Bugs resolved](release-notes/5.59.4.md#bugs)**
-- **[Credits](release-notes/5.59.4.md#credits)**
-- **[Feedback](release-notes/5.59.4.md#feedback)**
-
-## CiviCRM 5.59.3
-
-Released March 15, 2023
-
-- **[Synopsis](release-notes/5.59.3.md#synopsis)**
-- **[Bugs resolved](release-notes/5.59.3.md#bugs)**
-- **[Credits](release-notes/5.59.3.md#credits)**
-- **[Feedback](release-notes/5.59.3.md#feedback)**
-
-## CiviCRM 5.59.2
-
-Released March 10, 2023
-
-- **[Synopsis](release-notes/5.59.2.md#synopsis)**
-- **[Bugs resolved](release-notes/5.59.2.md#bugs)**
-- **[Credits](release-notes/5.59.2.md#credits)**
-- **[Feedback](release-notes/5.59.2.md#feedback)**
-
-## CiviCRM 5.59.1
-
-Released March 3, 2023
-
-- **[Synopsis](release-notes/5.59.1.md#synopsis)**
-- **[Bugs resolved](release-notes/5.59.1.md#bugs)**
-- **[Credits](release-notes/5.59.1.md#credits)**
-- **[Feedback](release-notes/5.59.1.md#feedback)**
+- **[Synopsis](release-notes/5.60.0.md#synopsis)**
+- **[Features](release-notes/5.60.0.md#features)**
+- **[Bugs resolved](release-notes/5.60.0.md#bugs)**
+- **[Miscellany](release-notes/5.60.0.md#misc)**
+- **[Credits](release-notes/5.60.0.md#credits)**
+- **[Feedback](release-notes/5.60.0.md#feedback)**
 
 ## CiviCRM 5.59.0
 
diff --git a/civicrm/release-notes/5.59.1.md b/civicrm/release-notes/5.59.1.md
deleted file mode 100644
index e66582b65e396e223d8a6aa1555820207b3d91f9..0000000000000000000000000000000000000000
--- a/civicrm/release-notes/5.59.1.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# CiviCRM 5.59.1
-
-Released March 3, 2023
-
-- **[Synopsis](#synopsis)**
-- **[Bugs resolved](#bugs)**
-- **[Credits](#credits)**
-- **[Feedback](#feedback)**
-
-## <a name="synopsis"></a>Synopsis
-
-| *Does this version...?*                                         |          |
-| --------------------------------------------------------------- | -------- |
-| Change the database schema?                                     | no       |
-| Alter the API?                                                  | no       |
-| Require attention to configuration options?                     | no       |
-| **Fix problems installing or upgrading to a previous version?** | **yes**  |
-| Introduce features?                                             | no       |
-| **Fix bugs?**                                                   | **yes**  |
-| Fix security vulnerabilities?                                   | no       |
-
-## <a name="bugs"></a>Bugs resolved
-
-* **_CiviContribute_: Contribution pages crash if CiviMember is disabled ([#25729](https://github.com/civicrm/civicrm-core/pull/25729))**
-* **_Tokens_: Abbreviated state/province does not display ([dev/core#4147](https://lab.civicrm.org/dev/core/-/issues/4147): [#25704](https://github.com/civicrm/civicrm-core/pull/25704))**
-* **_Multilingual_: Site reports `DB Error: -32` after running upgrade ([dev/core#4155](https://lab.civicrm.org/dev/core/-/issues/4155): [#25733](https://github.com/civicrm/civicrm-core/pull/25733))**
-
-  If you previously ran the 5.59.0 upgrade on a multilingual site and have a persistent fatal error, then you may wish to (a) consult the Gitlab discussion for resolution steps or (b) rollback to the original DB and upgrade to 5.59.1 instead.
-
-## <a name="credits"></a>Credits
-
-This release was developed by the following authors and reviewers:
-
-Wikimedia Foundation - Eileen McNaughton; Megaphone Technology Consulting - Jon Goldberg;
-JMA Consulting - Seamus Lee; CiviCRM - Tim Otten; Agileware - Justin Freeman
-
-## <a name="feedback"></a>Feedback
-
-These release notes are edited by Tim Otten and Andie Hunt.  If you'd like to
-provide feedback on them, please login to https://chat.civicrm.org/civicrm and
-contact `@agh1`.
diff --git a/civicrm/release-notes/5.59.2.md b/civicrm/release-notes/5.59.2.md
deleted file mode 100644
index b807de68d19afe28d4530b188ebf26716830da4d..0000000000000000000000000000000000000000
--- a/civicrm/release-notes/5.59.2.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# CiviCRM 5.59.2
-
-Released March 10, 2023
-
-- **[Synopsis](#synopsis)**
-- **[Bugs resolved](#bugs)**
-- **[Credits](#credits)**
-- **[Feedback](#feedback)**
-
-## <a name="synopsis"></a>Synopsis
-
-| *Does this version...?*                                         |          |
-| --------------------------------------------------------------- | -------- |
-| Change the database schema?                                     | no       |
-| Alter the API?                                                  | no       |
-| Require attention to configuration options?                     | no       |
-| Fix problems installing or upgrading to a previous version?     | no       |
-| Introduce features?                                             | no       |
-| **Fix bugs?**                                                   | **yes**  |
-| Fix security vulnerabilities?                                   | no       |
-
-## <a name="bugs"></a>Bugs resolved
-
-* **_Import_: Contribution note does not accept empty string ([dev/core#4105](https://lab.civicrm.org/dev/core/-/issues/4105): [#25569](https://github.com/civicrm/civicrm-core/pull/25569))**
-* **_PayPal Pro_: New recurring payments recorded twice ([dev/core#4158](https://lab.civicrm.org/dev/core/-/issues/4158): [#25775](https://github.com/civicrm/civicrm-core/pull/25775))**
-
-## <a name="credits"></a>Credits
-
-This release was developed by the following authors and reviewers:
-
-Wikimedia Foundation - Eileen McNaughton; PERORA SRL - Samuele Masetto; MJW Consulting -
-Matthew Wire; Megaphone Technology Consulting - Jon Goldberg; Korlon - Stuart Gaston; JMA
-Consulting - Seamus Lee; Dave D; CiviCRM - Tim Otten
-
-## <a name="feedback"></a>Feedback
-
-These release notes are edited by Tim Otten and Andie Hunt.  If you'd like to
-provide feedback on them, please login to https://chat.civicrm.org/civicrm and
-contact `@agh1`.
diff --git a/civicrm/release-notes/5.59.3.md b/civicrm/release-notes/5.59.3.md
deleted file mode 100644
index ccb3b0de0498ff511d538f3a791d72f11e6c0c8c..0000000000000000000000000000000000000000
--- a/civicrm/release-notes/5.59.3.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# CiviCRM 5.59.3
-
-Released March 15, 2023
-
-- **[Synopsis](#synopsis)**
-- **[Bugs resolved](#bugs)**
-- **[Credits](#credits)**
-- **[Feedback](#feedback)**
-
-## <a name="synopsis"></a>Synopsis
-
-| *Does this version...?*                                         |          |
-| --------------------------------------------------------------- | -------- |
-| Change the database schema?                                     | no       |
-| Alter the API?                                                  | no       |
-| Require attention to configuration options?                     | no       |
-| Fix problems installing or upgrading to a previous version?     | no       |
-| Introduce features?                                             | no       |
-| **Fix bugs?**                                                   | **yes**  |
-| Fix security vulnerabilities?                                   | no       |
-
-## <a name="bugs"></a>Bugs resolved
-
-* **_Import_: Contribution import fails on empty soft credit ([dev/core#4166](https://lab.civicrm.org/dev/core/-/issues/4166): [#25806](https://github.com/civicrm/civicrm-core/pull/25806))**
-* **_Dedupe_: Employer name becomes stale after merging organizations (partial fix) ([dev/core#4156](https://lab.civicrm.org/dev/core/-/issues/4156): [#25778](https://github.com/civicrm/civicrm-core/pull/25778))**
-
-## <a name="credits"></a>Credits
-
-This release was developed by the following authors and reviewers:
-
-Wikimedia Foundation - Eileen McNaughton; lkuttner; Coop SymbioTIC - Mathieu Lutfy;
-CiviCRM - Tim Otten; CiviCoop - Erik Hommel; Agileware - Justin Freeman
-
-## <a name="feedback"></a>Feedback
-
-These release notes are edited by Tim Otten and Andie Hunt.  If you'd like to
-provide feedback on them, please login to https://chat.civicrm.org/civicrm and
-contact `@agh1`.
diff --git a/civicrm/release-notes/5.59.4.md b/civicrm/release-notes/5.59.4.md
deleted file mode 100644
index 59272f37d8b8c08918f70007307945148dcd8c75..0000000000000000000000000000000000000000
--- a/civicrm/release-notes/5.59.4.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# CiviCRM 5.59.4
-
-Released March 27, 2023
-
-- **[Synopsis](#synopsis)**
-- **[Bugs resolved](#bugs)**
-- **[Credits](#credits)**
-- **[Feedback](#feedback)**
-
-## <a name="synopsis"></a>Synopsis
-
-| *Does this version...?*                                         |          |
-| --------------------------------------------------------------- | -------- |
-| Change the database schema?                                     | no       |
-| Alter the API?                                                  | no       |
-| Require attention to configuration options?                     | no       |
-| Fix problems installing or upgrading to a previous version?     | no       |
-| Introduce features?                                             | no       |
-| **Fix bugs?**                                                   | **yes**  |
-| Fix security vulnerabilities?                                   | no       |
-
-## <a name="bugs"></a>Bugs resolved
-
-* **_CiviCase_: "Delete Case" leads to "Network Error" ([dev/core#4190](https://lab.civicrm.org/dev/core/-/issues/4190): [#25849](https://github.com/civicrm/civicrm-core/pull/25849))**
-* **_CiviContribute_: Sorting by soft-credit name/date leads to AJAX error ([dev/core#4187](https://lab.civicrm.org/dev/core/-/issues/4187): [#25846](https://github.com/civicrm/civicrm-core/pull/25846))**
-* **_CiviContribute_: Confirmation screen displays extraneous debit fields ([dev/core#4189](https://lab.civicrm.org/dev/core/-/issues/4189): [#25910](https://github.com/civicrm/civicrm-core/pull/25910))**
-* **_CiviEvent_: Event created via API incorrectly marked as template ([dev/core#4205](https://lab.civicrm.org/dev/core/-/issues/4205): [#25932](https://github.com/civicrm/civicrm-core/pull/25932))**
-* **_Dedupe/Merge_: Harden against possibility of inconsistent value for `is_view` ([dev/core#4197](https://lab.civicrm.org/dev/core/-/issues/4197): [#25912](https://github.com/civicrm/civicrm-core/pull/25912))**
-
-## <a name="credits"></a>Credits
-
-This release was developed by the following authors and reviewers:
-
-Wikimedia Foundation - Eileen McNaughton; Romy Ebert; MJW Consulting - Matthew Wire;
-Lemniscus - Noah Miller; Korlon - Stuart Gaston; JMA Consulting - Seamus Lee; Coop
-SymbioTIC - Mathieu Lutfy; composerjk; CiviCRM - Tim Otten; Black Brick Software - David
-Hayes; Benjamin W; Andreas Howiller
-
-## <a name="feedback"></a>Feedback
-
-These release notes are edited by Tim Otten and Andie Hunt.  If you'd like to
-provide feedback on them, please login to https://chat.civicrm.org/civicrm and
-contact `@agh1`.
diff --git a/civicrm/release-notes/5.60.0.md b/civicrm/release-notes/5.60.0.md
new file mode 100644
index 0000000000000000000000000000000000000000..cfb559d35c96056d042f017e0de348e460f98d76
--- /dev/null
+++ b/civicrm/release-notes/5.60.0.md
@@ -0,0 +1,696 @@
+# CiviCRM 5.60.0
+
+Released April 5, 2023
+
+- **[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
+
+### CiviCRM Core
+
+- **Add Entity Reference custom field type (implementing EntityRef QuickForm
+  element type)
+  ([dev/core#3721](https://lab.civicrm.org/dev/core/-/issues/3721):
+  [25471](https://github.com/civicrm/civicrm-core/pull/25471))**
+
+  Adds a new type of custom field "EntityReference" which creates an
+  autocomplete widget.
+
+- **Add 'SavePoint' to import to statusBounce back to
+  ([25602](https://github.com/civicrm/civicrm-core/pull/25602))**
+
+  Adds a 'SavePoint' to import so that the user can be bounced back to the save
+  point if the import fails later down the road.
+
+- **SearchKit: Decide on available actions per display (funded)
+  ([dev/core#4118](https://lab.civicrm.org/dev/core/-/issues/4118):
+  [25521](https://github.com/civicrm/civicrm-core/pull/25521))**
+
+  Allows the admin to limit the available choices of bulk actions (tasks) in a
+  Search Display.
+
+- **Make dedupe exceptions searchable in SearchKit
+  ([25522](https://github.com/civicrm/civicrm-core/pull/25522))**
+
+  Makes dedupe exceptions searchable in SearchKit.
+
+- **Filter Searches by 'is_current' / expires_date (Work Towards
+  [dev/core#4117](https://lab.civicrm.org/dev/core/-/issues/4117):
+  [25516](https://github.com/civicrm/civicrm-core/pull/25516))**
+
+  Adds `is_current` filter to searches.
+
+- **Log Smarty debug to it's own channel
+  ([25682](https://github.com/civicrm/civicrm-core/pull/25682))**
+
+   Makes it so output from smarty secure mode goes into it's own file.
+
+- **Allow OAuth redirect URI to be overridden in client or provider config
+  ([25445](https://github.com/civicrm/civicrm-core/pull/25445))**
+
+  Makes the oauth-client extension respect whatever Redirect URI is set in the
+  "client" or "provider" configuration.
+
+- **Make `activity_type_id` available to `links` hook, test
+  ([25565](https://github.com/civicrm/civicrm-core/pull/25565))**
+
+  Improves hook_civicrm_links by making activity_type_id available (in relevant
+  contexts).
+
+- **Add setting to disable Smarty in Scheduled reminders
+  ([dev/core#4100](https://lab.civicrm.org/dev/core/-/issues/4100):
+  [25444](https://github.com/civicrm/civicrm-core/pull/25444))**
+
+  Adds a setting to disable Smarty in Scheduled reminders.
+
+### CiviCase
+
+- **Afform: Allow selecting case for relationship
+  ([25618](https://github.com/civicrm/civicrm-core/pull/25618))**
+
+  Makes it so one can select a case for a relationship on Afforms.
+
+### CiviContribute
+
+- **Add filter on pledge payment in contribution report templates
+  ([dev/core#4106](https://lab.civicrm.org/dev/core/-/issues/4106):
+  [25629](https://github.com/civicrm/civicrm-core/pull/25629))**
+
+  Adds a pledge payment filter to the Contribution Detail and Summary reports.
+
+- **PaymentProcessor - Use ajax refresh
+  ([25512](https://github.com/civicrm/civicrm-core/pull/25512))**
+
+  Makes page refresh a bit nicer when changing payment processor types on the
+  admin form.
+
+### CiviMail
+
+- **HTML definition for subscription history date
+  ([25655](https://github.com/civicrm/civicrm-core/pull/25655))**
+
+  Adds an HTML type to the "Group Membership Action Date" field so it can be
+  used in FormBuilder as a filter.
+
+### Drupal Integration
+
+- **Epic: Drupal 10 readiness (Work Towards
+  [dev/drupal#176](https://lab.civicrm.org/dev/drupal/-/issues/176):
+  [25499](https://github.com/civicrm/civicrm-core/pull/25499) and
+  [25054](https://github.com/civicrm/civicrm-core/pull/25054))**
+
+  Work towards drupal 10 readiness by allowing symfony 6.
+
+- **CMS Database Integration - Per-table prefixes are no longer supported
+  ([dev/core#4028](https://lab.civicrm.org/dev/core/-/issues/4028):
+  [25328](https://github.com/civicrm/civicrm-core/pull/25328))**
+
+  Makes the CRM_Admin_Form_Setting_UF more CMS agnostic by removing the Drupal
+  7 mapping table.
+
+## <a name="bugs"></a>Bugs resolved
+
+### Core CiviCRM
+
+- **Make 5.60 upgrade rerunnable
+  ([25950](https://github.com/civicrm/civicrm-core/pull/25950))**
+
+- **Import search authorization issues
+  ([dev/core#4184](https://lab.civicrm.org/dev/core/-/issues/4184):
+  [25810](https://github.com/civicrm/civicrm-core/pull/25810))**
+
+- **Individual's Employer and Membership by Relationship Are Not Updated After
+  Merging Their Organization
+  ([dev/core#4156](https://lab.civicrm.org/dev/core/-/issues/4156):
+  [25778](https://github.com/civicrm/civicrm-core/pull/25778))**
+
+- **Fatal error on merge
+  ([dev/core#4197](https://lab.civicrm.org/dev/core/-/issues/4197):
+  [25912](https://github.com/civicrm/civicrm-core/pull/25912))**
+
+- **Finish making legacycustomsearches optional (Work Towards
+  [dev/core#4112](https://lab.civicrm.org/dev/core/-/issues/4112):
+  [25392](https://github.com/civicrm/civicrm-core/pull/25392))**
+
+  Privatize `prevNextCache` functions.
+
+- **Administrator > Communications > Schedule Reminders error when CiviEvent
+  disabled ([dev/core#4145](https://lab.civicrm.org/dev/core/-/issues/4145):
+  [25703](https://github.com/civicrm/civicrm-core/pull/25703))**
+
+- **Upgrade Smarty
+  (Work Towards [dev/core#4146](https://lab.civicrm.org/dev/core/-/issues/4146):
+  [25669](https://github.com/civicrm/civicrm-core/pull/25669))**
+
+  Removes (old) Smarty-forward incompatible syntax from Address.tpl.
+
+- **When using PHP 8.1, the Contact token for the Address State / Province field
+  returns no value when used in Message Templates. Works fine on PHP 7.4
+  ([dev/core#4147](https://lab.civicrm.org/dev/core/-/issues/4147):
+  [25704](https://github.com/civicrm/civicrm-core/pull/25704))**
+
+- **Fix for 5.59 upgrade on multilingual
+  ([25733](https://github.com/civicrm/civicrm-core/pull/25733))**
+
+- **Load submitted `fieldSeparator` on back on `Import Datasource` screen
+  ([25758](https://github.com/civicrm/civicrm-core/pull/25758))**
+
+- **API4: Allow save() to match on null values
+  ([24971](https://github.com/civicrm/civicrm-core/pull/24971))**
+
+- **API v4 explorer: boolean params don't render correctly for CV (short)
+  ([dev/core#4129](https://lab.civicrm.org/dev/core/-/issues/4129):
+  [25589](https://github.com/civicrm/civicrm-core/pull/25589))**
+
+- **Stricter typing in Apiv4
+  ([25706](https://github.com/civicrm/civicrm-core/pull/25706))**
+
+- **Afform - Fix validateBySavedSearch
+  ([25822](https://github.com/civicrm/civicrm-core/pull/25822))**
+
+- **Afform: Fix broken syntax for saving reciprocal relationships
+  ([25620](https://github.com/civicrm/civicrm-core/pull/25620))**
+
+- **Afform - Fix editing search filters nested within multiple containers
+  ([25742](https://github.com/civicrm/civicrm-core/pull/25742))**
+
+- **Form Builder - autocomplete input does not use configured filtering
+  ([dev/core#4138](https://lab.civicrm.org/dev/core/-/issues/4138):
+  [25646](https://github.com/civicrm/civicrm-core/pull/25646))**
+
+- **Filter expired searches from search kit results
+  ([25568](https://github.com/civicrm/civicrm-core/pull/25568))**
+
+- **SearchKit - Fix handling of new Custom EntityReference fields
+  ([25927](https://github.com/civicrm/civicrm-core/pull/25927))**
+
+- **SearchKit - Clarify how to view SQL output
+  ([25671](https://github.com/civicrm/civicrm-core/pull/25671))**
+
+- **Always show "add more buttons" dropdown in searchkit view
+  ([25596](https://github.com/civicrm/civicrm-core/pull/25596))**
+
+- **Make job ID accessible to searchkit for joblog
+  ([24746](https://github.com/civicrm/civicrm-core/pull/24746))**
+
+- **Add Activity Target consistently
+  ([dev/core#4142](https://lab.civicrm.org/dev/core/-/issues/4142):
+  [25650](https://github.com/civicrm/civicrm-core/pull/25650))**
+
+- **Allow (some) permissions with colons
+  ([23782](https://github.com/civicrm/civicrm-core/pull/23782))**
+
+- **Update Smarty addTemplateDir function signature to future smarty
+  ([25248](https://github.com/civicrm/civicrm-core/pull/25248))**
+
+- **Add `crmUrl function` to smarty in the standard way
+  ([25661](https://github.com/civicrm/civicrm-core/pull/25661))**
+
+- **Reduce unneeded DNS queries during OAuth flow
+  ([25446](https://github.com/civicrm/civicrm-core/pull/25446))**
+
+- **Angular Coder: Fix unescaping of quotes breaking attributes  
+  ([25630](https://github.com/civicrm/civicrm-core/pull/25630))**
+
+- **EntityRef - Format custom field display value on QuickForms
+  ([25632](https://github.com/civicrm/civicrm-core/pull/25632))**
+
+- **getStatus() should be returning a string
+  ([25441](https://github.com/civicrm/civicrm-core/pull/25441))**
+
+- **Regression: Fix DB syntax error on Parse address scheduled  job
+  ([25616](https://github.com/civicrm/civicrm-core/pull/25616))**
+
+- **update entity mapping logic for profile fields to include contact subtypes
+  ([25570](https://github.com/civicrm/civicrm-core/pull/25570))**
+
+- **Smarty {ts} - For extensions, use fallback similar to E::ts() and JS ts()
+  ([25383](https://github.com/civicrm/civicrm-core/pull/25383))**
+
+- **Disambiguate `Address.state_province_id:abbr` (PHP asort)
+  ([25552](https://github.com/civicrm/civicrm-core/pull/25552) and
+  [25550](https://github.com/civicrm/civicrm-core/pull/25550))**
+
+- **Default to supporting partial locales
+  ([25063](https://github.com/civicrm/civicrm-core/pull/25063))**
+
+- **Do not use reference for Object parameter, death to `_NULLObject`
+  ([25541](https://github.com/civicrm/civicrm-core/pull/25541))**
+
+- **only load dedupe rules for the chosen entity
+  ([25515](https://github.com/civicrm/civicrm-core/pull/25515))**
+
+- **A couple of minor smarty notice fixes
+  ([25514](https://github.com/civicrm/civicrm-core/pull/25514))**
+
+- **Use mysql DATABASE() function instead of php code
+  ([25530](https://github.com/civicrm/civicrm-core/pull/25530))**
+
+- **Use `DATABASE()` function rather than lossa code
+  ([25528](https://github.com/civicrm/civicrm-core/pull/25528))**
+
+- **Fix more schema checks to use mysql DATABASE() function, deprecate php
+  function ([25593](https://github.com/civicrm/civicrm-core/pull/25593))**
+
+- **Fix a couple more places to use mysql DATABASE() function
+  ([25537](https://github.com/civicrm/civicrm-core/pull/25537))**
+
+- **Fix Scheduled reminders form to use tokenProcessor to get token list
+  ([25052](https://github.com/civicrm/civicrm-core/pull/25052))**
+
+- **fix the id help instead use class help
+  ([25487](https://github.com/civicrm/civicrm-core/pull/25487))**
+
+- **Fix user profile file fields not saving.
+  ([80](https://github.com/civicrm/civicrm-drupal-8/pull/80))**
+
+- **Fix fatal error on upgrade success screen
+  ([25554](https://github.com/civicrm/civicrm-core/pull/25554))**
+
+- **Typo executequery => executeQuery
+  ([25539](https://github.com/civicrm/civicrm-core/pull/25539))**
+
+- **Smarty - Fix warnings about 'mb_truncate' modifier
+  ([25651](https://github.com/civicrm/civicrm-core/pull/25651))**
+
+- **Smarty - {htxt} blocks should not be evaluated unless needed
+  ([25653](https://github.com/civicrm/civicrm-core/pull/25653))**
+
+- **Smarty notice fix
+  ([25544](https://github.com/civicrm/civicrm-core/pull/25544))**
+
+- **E-notice fix Ical display
+  ([25612](https://github.com/civicrm/civicrm-core/pull/25612))**
+
+- **Fix e-notice pattern around location not defined in formButtons.tpl
+  ([25668](https://github.com/civicrm/civicrm-core/pull/25668))**
+
+- **Fix Contact import e-notice on preview screen
+  ([25302](https://github.com/civicrm/civicrm-core/pull/25302))**
+
+- **Fix mixin to use `addTemplateDir`
+  ([25667](https://github.com/civicrm/civicrm-core/pull/25667))**
+
+- **Fix add version for civicrm_custom_field.fk_entity
+  ([25642](https://github.com/civicrm/civicrm-core/pull/25642))**
+
+- **Fix handling of invalid sql query during import
+  ([25600](https://github.com/civicrm/civicrm-core/pull/25600))**
+
+- **Enotice fix, pledge block on UserDashboard
+  ([25546](https://github.com/civicrm/civicrm-core/pull/25546))**
+
+- **Notice fix on preferred_language, when null
+  ([25656](https://github.com/civicrm/civicrm-core/pull/25656))**
+
+### CiviCase
+
+- **PHP8: Delete Case link causes fatal error
+  ([dev/core#4190](https://lab.civicrm.org/dev/core/-/issues/4190):
+  [25849](https://github.com/civicrm/civicrm-core/pull/25849))**
+
+- **CiviCase Dashboard exports all records instead of the results
+  ([dev/core#4126](https://lab.civicrm.org/dev/core/-/issues/4126):
+  [25649](https://github.com/civicrm/civicrm-core/pull/25649))**
+
+### CiviContribute
+
+- **Around 5.49 pan_truncation and card_type_id (containing card type VISA/MC
+  and last 4 digits last four digits stopped getting recorded
+  ([dev/core#4029](https://lab.civicrm.org/dev/core/-/issues/4029):
+  [25963](https://github.com/civicrm/civicrm-core/pull/25963))**
+
+- **Contribution Confirm incorrectly debit card information even if using
+  credit card ([dev/core#4189](https://lab.civicrm.org/dev/core/-/issues/4189):
+  [25910](https://github.com/civicrm/civicrm-core/pull/25910))**
+
+- **Import contribution fails if using soft-credit and a row has an empty field
+  ([dev/core#4166](https://lab.civicrm.org/dev/core/-/issues/4166):
+  [25806](https://github.com/civicrm/civicrm-core/pull/25806))**
+
+- **Fix empty values of Note on Import contribution
+  ([25569](https://github.com/civicrm/civicrm-core/pull/25569))**
+
+- **Fix for issue when optional contact_id is blank importing contributions
+  ([25897](https://github.com/civicrm/civicrm-core/pull/25897))**
+
+- **New PaypalPro Recurring Payments (PPRP) creating duplicate contributions
+  and inaccurate status
+  ([dev/core#4158](https://lab.civicrm.org/dev/core/-/issues/4158):
+  [25775](https://github.com/civicrm/civicrm-core/pull/25775))**
+
+- **PayPal Prp IPN - Fix incorrect option_group update
+  ([25724](https://github.com/civicrm/civicrm-core/pull/25724))**
+
+- **CiviContribute - Fix warning about 'suppressedEmails' when generating PDF
+  ([25576](https://github.com/civicrm/civicrm-core/pull/25576))**
+
+- **Contribution pages crash for logged-in users when CiviMember is disabled
+  ([25729](https://github.com/civicrm/civicrm-core/pull/25729))**
+
+- **Enotice fix on userDashboard with contributions, replace deprecated
+  functions with api4v calls
+  ([24861](https://github.com/civicrm/civicrm-core/pull/24861))**
+
+- **Smarty notice fix on bank_account_number
+  ([25680](https://github.com/civicrm/civicrm-core/pull/25680))**
+
+- **AJAX error when sorting soft credit by name or date
+  ([dev/core#4187](https://lab.civicrm.org/dev/core/-/issues/4187):
+  [25846](https://github.com/civicrm/civicrm-core/pull/25846))**
+
+### CiviEvent
+
+- **Can't create new events from a template using the API
+  ([dev/core#4205](https://lab.civicrm.org/dev/core/-/issues/4205):
+  [25932](https://github.com/civicrm/civicrm-core/pull/25932))**
+
+### CiviMail
+
+- **Mailing Summary Report: support pseudofields
+  ([25560](https://github.com/civicrm/civicrm-core/pull/25560))**
+
+- **FlexMailer: Default Return Path not respected
+  ([dev/core#4070](https://lab.civicrm.org/dev/core/-/issues/4070):
+  [25309](https://github.com/civicrm/civicrm-core/pull/25309))**
+
+### Drupal Integration
+
+- **is_drupal: move functionality that calls this deprecated variable to System
+  classes ([dev/core#4127](https://lab.civicrm.org/dev/core/-/issues/4127):
+  [25585](https://github.com/civicrm/civicrm-core/pull/25585),
+  [25571](https://github.com/civicrm/civicrm-core/pull/25571),
+  [25587](https://github.com/civicrm/civicrm-core/pull/25587),
+  [25573](https://github.com/civicrm/civicrm-core/pull/25573),
+  [25586](https://github.com/civicrm/civicrm-core/pull/25586) and
+  [25572](https://github.com/civicrm/civicrm-core/pull/25572))**
+
+### WordPress Integration
+
+- **distmaker - Fix export of WordPress patches
+  ([25599](https://github.com/civicrm/civicrm-core/pull/25599))**
+
+## <a name="misc"></a>Miscellany
+
+- **Simplify steps of loading SimpleCacheTest from cache/integration-tests
+  ([25942](https://github.com/civicrm/civicrm-core/pull/25942))**
+
+- **composer require --dev yoast/phpunit-polyfills
+  ([25631](https://github.com/civicrm/civicrm-core/pull/25631))**
+
+- **Move form specific code to the form
+  ([25457](https://github.com/civicrm/civicrm-core/pull/25457))**
+
+- **Make private function private
+  ([25604](https://github.com/civicrm/civicrm-core/pull/25604))**
+
+- **Make unshared `getTree` function private, remove never passed params
+  ([25517](https://github.com/civicrm/civicrm-core/pull/25517))**
+
+- **APIv4 - Limited support for casting
+  ([25595](https://github.com/civicrm/civicrm-core/pull/25595))**
+
+- **Split up CRM_Utils_System_Base::theme
+  ([dev/core#4076](https://lab.civicrm.org/dev/core/-/issues/4076):
+  [25329](https://github.com/civicrm/civicrm-core/pull/25329))**
+
+- **Preliminary cleanup, comments, type hints
+  ([25564](https://github.com/civicrm/civicrm-core/pull/25564))**
+
+- **run `civix update` on `recaptcha`, `legacyCustomSearches`, `financialacls`
+  ([25663](https://github.com/civicrm/civicrm-core/pull/25663))**
+
+- **ext - General update to civix v23.02.0
+  ([25659](https://github.com/civicrm/civicrm-core/pull/25659))**
+
+- **CiviCRM Stable Version bump
+  ([291](https://github.com/civicrm/civicrm-wordpress/pull/291))**
+
+- **Post divide code tidy up
+  ([25195](https://github.com/civicrm/civicrm-core/pull/25195))**
+
+- **Add gentle deprecation to `debug_log_message`
+  ([25683](https://github.com/civicrm/civicrm-core/pull/25683))**
+
+- **Stop loading unused `relatedObjects`
+  ([25617](https://github.com/civicrm/civicrm-core/pull/25617))**
+
+- **Bump dompdf/dompdf from 2.0.2 to 2.0.3
+  ([25520](https://github.com/civicrm/civicrm-core/pull/25520))**
+
+- **Add noisy deprecation to `replaceHookTokens`
+  ([25510](https://github.com/civicrm/civicrm-core/pull/25510))**
+
+- **Deprecate unused, exception class with non-standard name-spacing
+  ([25641](https://github.com/civicrm/civicrm-core/pull/25641))**
+
+- **Fully deprecate `CRM_Utils_Token::getMembershipTokenDetails`,
+  `CRM_Utils_Token::replaceEntityTokens`
+  ([25507](https://github.com/civicrm/civicrm-core/pull/25507))**
+
+- **Extra deprecation for clarity on token function
+  `convertPseudoConstantsUsingMetadata`
+  ([25511](https://github.com/civicrm/civicrm-core/pull/25511))**
+
+- **Pull over a few more deprecations + blockDelete deprecation
+  ([25695](https://github.com/civicrm/civicrm-core/pull/25695))**
+
+- **Address portions of deprecated code replacement
+  ([25693](https://github.com/civicrm/civicrm-core/pull/25693))**
+
+- **Deprecate unused `CRM_Core_BAO_UFField::copy()` function
+  ([25594](https://github.com/civicrm/civicrm-core/pull/25594))**
+
+- **Deprecations for Mailing::delete functions
+  ([25691](https://github.com/civicrm/civicrm-core/pull/25691))**
+
+- **Deprecations for delete on ACL entities
+  ([25690](https://github.com/civicrm/civicrm-core/pull/25690))**
+
+- **Fully deprecate legacy contributionTokens
+  ([25505](https://github.com/civicrm/civicrm-core/pull/25505))**
+
+- **Fully deprecate `CRM_Core_SelectValues::eventTokens()`
+  ([25509](https://github.com/civicrm/civicrm-core/pull/25509))**
+
+- **Fully deprecate `CRM_Core_SelectValues::membershipTokens()`
+  ([25506](https://github.com/civicrm/civicrm-core/pull/25506))**
+
+- **Add noisy deprecation to deprecated function, after universe search
+  ([25662](https://github.com/civicrm/civicrm-core/pull/25662))**
+
+- **Remove legacy handling of locks for discontinued mysql/mariaDB versions
+  ([25654](https://github.com/civicrm/civicrm-core/pull/25654))**
+
+- **Remove forward incompatible syntax from template  
+  ([25664](https://github.com/civicrm/civicrm-core/pull/25664))**
+
+- **Remove conditional assignment around `is_deductible`
+  ([25237](https://github.com/civicrm/civicrm-core/pull/25237))**
+
+- **Remove unnecessary pass-by-reference
+  ([25678](https://github.com/civicrm/civicrm-core/pull/25678))**
+
+- **Remove empty on location - it should always be set now
+  ([25675](https://github.com/civicrm/civicrm-core/pull/25675))**
+
+- **Remove always-NULL `$singleRecord` variable
+  ([25519](https://github.com/civicrm/civicrm-core/pull/25519))**
+
+- **Remove 2020-deprecated handling of `legacyAddressCreate`
+  ([25686](https://github.com/civicrm/civicrm-core/pull/25686))**
+
+- **Remove code deprecated in 2018
+  ([25679](https://github.com/civicrm/civicrm-core/pull/25679))**
+
+- **Remove function noisily deprecated when the world was young (2020)
+  ([25685](https://github.com/civicrm/civicrm-core/pull/25685))**
+
+- **Remove deprecated function call
+  ([25684](https://github.com/civicrm/civicrm-core/pull/25684))**
+
+- **Remove deprecated functions from tests
+  ([25688](https://github.com/civicrm/civicrm-core/pull/25688))**
+
+- **Remove deprecated function calls to Event::del, Membership functions del,
+  OptionValue ([25705](https://github.com/civicrm/civicrm-core/pull/25705))**
+
+- **Remove function deprecated a year ago
+  ([25615](https://github.com/civicrm/civicrm-core/pull/25615))**
+
+- **Remove deprecated use of $ids
+  ([25698](https://github.com/civicrm/civicrm-core/pull/25698))**
+
+- **Delete long-deprecated functions
+  ([25696](https://github.com/civicrm/civicrm-core/pull/25696))**
+
+- **Remove the correct extraneous field separator field
+  ([25753](https://github.com/civicrm/civicrm-core/pull/25753))**
+
+- **Remove never-true IF
+  ([25518](https://github.com/civicrm/civicrm-core/pull/25518))**
+
+- **Remove unused constants left over from import cleanup
+  ([25601](https://github.com/civicrm/civicrm-core/pull/25601))**
+
+- **Remove interaction with complex legacy `getTree` function
+  ([25395](https://github.com/civicrm/civicrm-core/pull/25395))**
+
+- **Remove empty function
+  ([25592](https://github.com/civicrm/civicrm-core/pull/25592))**
+
+- **Remove unused variable
+  ([25508](https://github.com/civicrm/civicrm-core/pull/25508))**
+
+- **Remove unused variables
+  ([25670](https://github.com/civicrm/civicrm-core/pull/25670))**
+
+- **Remove unused property - not used since import code refactor `_lineCount`
+  ([25466](https://github.com/civicrm/civicrm-core/pull/25466))**
+
+- **(REF) Tidy up unused params in api_v3_ContributionSoftTest
+  ([25322](https://github.com/civicrm/civicrm-core/pull/25322))**
+
+- **[REF] Fix undefined variable notice in WordPress tests
+  ([25591](https://github.com/civicrm/civicrm-core/pull/25591))**
+
+- **(REF) Remove test reference to property which no longer exists
+  ([25624](https://github.com/civicrm/civicrm-core/pull/25624))**
+
+- **(REF) Remove unused setup from AdhocMailingTest
+  ([25500](https://github.com/civicrm/civicrm-core/pull/25500))**
+
+- **[REF] Add support for composer installers v2 which seems to be needed for
+  d10 ([79](https://github.com/civicrm/civicrm-drupal-8/pull/79))**
+
+- **Php8.x compatibility - do not try to count NULL
+  ([25652](https://github.com/civicrm/civicrm-core/pull/25652))**
+
+- **[REF][PHP8.2] Remove use of dynamic properties in
+  CRM_Contact_Form_Task_EmailTest
+  ([25562](https://github.com/civicrm/civicrm-core/pull/25562))**
+
+- **[REF][PHP8.2] Get rid of dynamic properties in
+  CRM_Core_Payment_AuthorizeNetTest
+  ([25561](https://github.com/civicrm/civicrm-core/pull/25561))**
+
+- **[REF][PHP8.2] Stop use of dynamic property in AdditionalPaymentTest
+  ([25559](https://github.com/civicrm/civicrm-core/pull/25559))**
+
+- **[REF][PHP8.2] Use variable instead of dynmaic property
+  (CRM_Group_Page_AjaxTest)
+  ([25558](https://github.com/civicrm/civicrm-core/pull/25558))**
+
+- **[REF][PHP8.2] Declare property in CRM_Pledge_BAO_PledgeBlockTest
+  ([25557](https://github.com/civicrm/civicrm-core/pull/25557))**
+
+- **[REF][PHP8.2] Declare property in CRM_Utils_FakeObject
+  ([25556](https://github.com/civicrm/civicrm-core/pull/25556))**
+
+- **[REF][PHP8.2] Refactor properties on CRM_Event_Form_SearchTest
+  ([25501](https://github.com/civicrm/civicrm-core/pull/25501))**
+
+- **[REF][PHP8.2] Declare dynamic property in two SMS tests
+  ([25502](https://github.com/civicrm/civicrm-core/pull/25502))**
+
+- **[REF][PHP8.2] Remove unnecessary dynamic property
+  ([25622](https://github.com/civicrm/civicrm-core/pull/25622))**
+
+- **[REF][PHP8.2] Fix use of self in callables deprecation
+  ([25625](https://github.com/civicrm/civicrm-core/pull/25625))**
+
+- **[REF][PHP8.2] Tidy up properties on api_v3_GroupContactTest
+  ([25626](https://github.com/civicrm/civicrm-core/pull/25626))**
+
+- **[REF][PHP8.2] Tidy up properties in CRM_Event_BAO_EventPermissionsTest
+  ([25623](https://github.com/civicrm/civicrm-core/pull/25623))**
+
+- **update CiviCrmTestBase setup() method declaration to match BrowserTestBase
+  ([78](https://github.com/civicrm/civicrm-drupal-8/pull/78))**
+
+- **(NFC) crmURL - Add examples and whitespace. Crosslink docs.
+  ([25665](https://github.com/civicrm/civicrm-core/pull/25665))**
+
+- **[NFC] php8.2 support in test class `ActivitySearchTest`
+  ([25607](https://github.com/civicrm/civicrm-core/pull/25607))**
+
+- **NFC cleanup in test class
+  ([25524](https://github.com/civicrm/civicrm-core/pull/25524))**
+
+- **(NFC) Version Check Test - Fix multi-user and concurrent execution
+  [25909](https://github.com/civicrm/civicrm-core/pull/25909)**
+
+- **[NFC] doc block fix
+  ([25610](https://github.com/civicrm/civicrm-core/pull/25610))**
+
+- **Fix test to test the thing it was written to test
+  ([25608](https://github.com/civicrm/civicrm-core/pull/25608))**
+
+- **Event test cleanup, fix test to use submitted form values, rather than
+  require a lot of contorting
+  ([25621](https://github.com/civicrm/civicrm-core/pull/25621))**
+
+- **Remove invalid  'world_region' => 'India' from test
+  ([25540](https://github.com/civicrm/civicrm-core/pull/25540))**
+
+- **Php8.2 test fix, remove one instance of undeclared property
+  ([25619](https://github.com/civicrm/civicrm-core/pull/25619))**
+
+- **Extend testing for ContributionConfirm & consolidate
+  `isSeparateMembershipPayment`
+  ([25270](https://github.com/civicrm/civicrm-core/pull/25270))**
+
+- **Upgrade test to use more recent methodology for testing forms
+  ([25551](https://github.com/civicrm/civicrm-core/pull/25551))**
+
+- **Add extra test, comment how it could be used to fix bug
+  ([25503](https://github.com/civicrm/civicrm-core/pull/25503))**
+
+- **Reduce processing load in test `assertAPIFailure`
+  ([25648](https://github.com/civicrm/civicrm-core/pull/25648))**
+
+## <a name="credits"></a>Credits
+
+This release was developed by the following code authors:
+
+AGH Strategies - Alice Frumin, Andie Hunt; BrightMinded Ltd - Bradley Taylor;
+CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku; Coop SymbioTIC -
+Mathieu Lutfy; Dave D; ES-Progress - Sandor Semsey; Freeform Solutions - Herb
+van den Dool; Fuzion - Jitendra Purohit; Giant Rabbit - Anthony Nemirovsky;
+iXiam - Luciano Spiegel; JMA Consulting - Seamus Lee; Lemniscus - Noah Miller;
+Megaphone Technology Consulting - Jon Goldberg; MJW Consulting - Matthew Wire;
+PERORA SRL - Samuele Masetto; Progressive Technology Project - Jamie McClelland;
+Skvare - Mark Hanna; Tadpole Collective - Kevin Cristiano; Third Sector
+Design - Kurund Jalmi, Michael McAndrew; Wikimedia Foundation - Eileen
+McNaughton
+
+Most authors also reviewed code for this release; in addition, the following
+reviewers contributed their comments:
+
+Agileware - Francis Whittle, Justin Freeman; Andreas Howiller; Becca Tregenna;
+Blackfly Solutions - Alan Dixon; Fuzion - Luke Stewart; Jens Schuppe;
+JMA Consulting - Joe Murray; Megaphone Technology Consulting - Brienne Kordis;
+Squiffle Consulting - Aidan Saunders; Stephen Palmstrom
+
+## <a name="feedback"></a>Feedback
+
+These release notes are edited by Alice Frumin and Andie 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/Localization.setting.php b/civicrm/settings/Localization.setting.php
index 19e0be3164c0eb6ca130fd3bde3d2b14c2e61794..c18312366a2af21355335cffa4c91e2b060d4c56 100644
--- a/civicrm/settings/Localization.setting.php
+++ b/civicrm/settings/Localization.setting.php
@@ -415,7 +415,7 @@ return [
     'name' => 'partial_locales',
     'type' => 'Boolean',
     'quick_form_type' => 'YesNo',
-    'default' => '0',
+    'default' => '1',
     'add' => '5.54',
     'title' => ts('Partial Locales'),
     'is_domain' => 1,
diff --git a/civicrm/settings/Mailing.setting.php b/civicrm/settings/Mailing.setting.php
index 97904cad54eb352d45fe7b538100a22bdcf0330c..434e78a7ee38d823dcd302ad0b6d030588f63359 100644
--- a/civicrm/settings/Mailing.setting.php
+++ b/civicrm/settings/Mailing.setting.php
@@ -393,4 +393,18 @@ return [
     'description' => ts('The frequency that CiviMail updates its sent mail database.'),
     'help_text' => NULL,
   ],
+  'scheduled_reminder_smarty' => [
+    'group_name' => 'Mailing Preferences',
+    'group' => 'mailing',
+    'name' => 'scheduled_reminder_smarty',
+    'type' => 'Boolean',
+    'html_type' => 'checkbox',
+    'default' => 0,
+    'title' => ts('Use Smarty in scheduled reminders'),
+    'add' => '5.60',
+    'is_domain' => 1,
+    'is_contact' => 0,
+    'description' => ts('Controls whether scheduled reminders will attempt to process smarty tokens.'),
+    'help_text' => NULL,
+  ],
 ];
diff --git a/civicrm/sql/civicrm.mysql b/civicrm/sql/civicrm.mysql
index eacd6acbbfb9ad06f5103cc9c1b2d9c7089ad673..f0efa3f59eeba6bce0d7bfd42431f58996ea30c0 100644
--- a/civicrm/sql/civicrm.mysql
+++ b/civicrm/sql/civicrm.mysql
@@ -1476,6 +1476,7 @@ CREATE TABLE `civicrm_custom_field` (
   `serialize` int unsigned NOT NULL DEFAULT 0 COMMENT 'Serialization method - a non-zero value indicates a multi-valued field.',
   `filter` varchar(255) COMMENT 'Stores Contact Get API params contact reference custom fields. May be used for other filters in the future.',
   `in_selector` tinyint NOT NULL DEFAULT 0 COMMENT 'Should the multi-record custom field values be displayed in tab table listing',
+  `fk_entity` varchar(255) DEFAULT NULL COMMENT 'Name of entity being referenced.',
   PRIMARY KEY (`id`),
   UNIQUE INDEX `UI_label_custom_group_id`(label, custom_group_id),
   UNIQUE INDEX `UI_name_custom_group_id`(name, custom_group_id),
@@ -1614,13 +1615,14 @@ CREATE TABLE `civicrm_job_log` (
   `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Job log entry ID',
   `domain_id` int unsigned NOT NULL COMMENT 'Which Domain is this scheduled job for',
   `run_time` timestamp COMMENT 'Log entry date',
-  `job_id` int unsigned COMMENT 'Pointer to job id - not a FK though, just for logging purposes',
+  `job_id` int unsigned COMMENT 'Pointer to job id',
   `name` varchar(255) COMMENT 'Title of the job',
   `command` varchar(255) COMMENT 'Full path to file containing job script',
   `description` varchar(255) COMMENT 'Title line of log entry',
   `data` longtext COMMENT 'Potential extended data for specific job run (e.g. tracebacks).',
   PRIMARY KEY (`id`),
-  CONSTRAINT FK_civicrm_job_log_domain_id FOREIGN KEY (`domain_id`) REFERENCES `civicrm_domain`(`id`)
+  CONSTRAINT FK_civicrm_job_log_domain_id FOREIGN KEY (`domain_id`) REFERENCES `civicrm_domain`(`id`),
+  CONSTRAINT FK_civicrm_job_log_job_id FOREIGN KEY (`job_id`) REFERENCES `civicrm_job`(`id`) ON DELETE SET NULL
 )
 ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
 
diff --git a/civicrm/sql/civicrm_data.mysql b/civicrm/sql/civicrm_data.mysql
index ee8acfbe9fda97794348fe32759d9cac56e6be5d..e095141abd4f71132278360024d08297b341d3ca 100644
--- a/civicrm/sql/civicrm_data.mysql
+++ b/civicrm/sql/civicrm_data.mysql
@@ -8119,7 +8119,7 @@ INSERT INTO civicrm_msg_template
   {$contact_phone}
 {/if}
 {/if}
-{if !empty($is_deductible) AND !empty($price)}
+{if $is_deductible AND !empty($price)}
 
 {ts 1=$price|crmMoney:$currency}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}{/if}
 {/if}
@@ -8541,7 +8541,7 @@ INSERT INTO civicrm_msg_template
         </td>
        </tr>
       {/if}
-      {if !empty($is_deductible) AND !empty($price)}
+      {if $is_deductible AND !empty($price)}
         <tr>
          <td colspan="2" {$valueStyle}>
           <p>{ts 1=$price|crmMoney:$currency}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}</p>
@@ -8774,7 +8774,7 @@ INSERT INTO civicrm_msg_template
   {$contact_phone}
 {/if}
 {/if}
-{if !empty($is_deductible) AND !empty($price)}
+{if $is_deductible AND !empty($price)}
 
 {ts 1=$price|crmMoney:$currency}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}{/if}
 {/if}
@@ -9196,7 +9196,7 @@ INSERT INTO civicrm_msg_template
         </td>
        </tr>
       {/if}
-      {if !empty($is_deductible) AND !empty($price)}
+      {if $is_deductible AND !empty($price)}
         <tr>
          <td colspan="2" {$valueStyle}>
           <p>{ts 1=$price|crmMoney:$currency}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}</p>
@@ -18847,7 +18847,7 @@ or want to inquire about reinstating your registration for this event.{/ts}</p>
   {$contact_phone}
 {/if}
 {/if}
-{if !empty($is_deductible) AND !empty($price)}
+{if $is_deductible AND !empty($price)}
 
 {ts 1=$price|crmMoney}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}{/if}
 {/if}
@@ -19374,7 +19374,7 @@ or want to inquire about reinstating your registration for this event.{/ts}</p>
         </td>
        </tr>
       {/if}
-      {if !empty($is_deductible) AND !empty($price)}
+      {if $is_deductible AND !empty($price)}
         <tr>
          <td colspan="2" {$valueStyle}>
           <p>{ts 1=$price|crmMoney}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}</p>
@@ -19641,7 +19641,7 @@ or want to inquire about reinstating your registration for this event.{/ts}</p>
   {$contact_phone}
 {/if}
 {/if}
-{if !empty($is_deductible) AND !empty($price)}
+{if $is_deductible AND !empty($price)}
 
 {ts 1=$price|crmMoney}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}{/if}
 {/if}
@@ -20168,7 +20168,7 @@ or want to inquire about reinstating your registration for this event.{/ts}</p>
         </td>
        </tr>
       {/if}
-      {if !empty($is_deductible) AND !empty($price)}
+      {if $is_deductible AND !empty($price)}
         <tr>
          <td colspan="2" {$valueStyle}>
           <p>{ts 1=$price|crmMoney}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}</p>
@@ -23678,4 +23678,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.59.4';
+UPDATE civicrm_domain SET version = '5.60.0';
diff --git a/civicrm/sql/civicrm_generated.mysql b/civicrm/sql/civicrm_generated.mysql
index d747b24a9b5977266410e4e7c33584d364b9d60b..25f18917687f0dcf20efd6ae3e1e7e623ff61f1d 100644
--- a/civicrm/sql/civicrm_generated.mysql
+++ b/civicrm/sql/civicrm_generated.mysql
@@ -3055,7 +3055,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.59.4',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
+ (1,'Default Domain Name',NULL,'5.60.0',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
 /*!40000 ALTER TABLE `civicrm_domain` ENABLE KEYS */;
 UNLOCK TABLES;
 
diff --git a/civicrm/templates/CRM/Admin/Form/ParticipantStatusType.tpl b/civicrm/templates/CRM/Admin/Form/ParticipantStatusType.tpl
index b7afd0d559fb6ab638b58859fd178dfddc317380..89508dfc705b168aaddbed82da8dc8af24c2b3b0 100644
--- a/civicrm/templates/CRM/Admin/Form/ParticipantStatusType.tpl
+++ b/civicrm/templates/CRM/Admin/Form/ParticipantStatusType.tpl
@@ -18,7 +18,7 @@
        {icon icon="fa-info-circle"}{/icon}
              {ts}WARNING: Deleting this Participant Status will remove all of its settings.{/ts} {ts}Do you want to continue?{/ts}
        </div>
-      <div>{include file="CRM/common/formButtons.tpl"}
+      <div>{include file="CRM/common/formButtons.tpl" location=''}
       </div>
     {else}
       <table class="form-layout-compressed">
diff --git a/civicrm/templates/CRM/Admin/Form/PaymentProcessor.tpl b/civicrm/templates/CRM/Admin/Form/PaymentProcessor.tpl
index 28827b440979d4156001877ab4fd5a521427db62..91e3ef405b205f19455c4db5518a6085ba355ff2 100644
--- a/civicrm/templates/CRM/Admin/Form/PaymentProcessor.tpl
+++ b/civicrm/templates/CRM/Admin/Form/PaymentProcessor.tpl
@@ -145,12 +145,15 @@
 {if $action eq 1  or $action eq 2}
   <script type="text/javascript">
   {literal}
-    function reload(refresh) {
-      var paymentProcessorType = cj("#payment_processor_type_id");
-      var url = {/literal}"{$refreshURL}"{literal} + "&pp=" + paymentProcessorType.val();
-      paymentProcessorType.closest('form').attr('data-warn-changes', 'false');
-      window.location.href = url;
-    }
+    CRM.$(function($) {
+      $('#payment_processor_type_id').change(function() {
+        var url = {/literal}"{$refreshURL}"{literal} + "&pp=" + $(this).val();
+        $(this).closest('form').attr('data-warn-changes', 'false')
+          // Ajax refresh (works in a popup or full-screen)
+          .closest('.crm-ajax-container, #crm-main-content-wrapper')
+          .crmSnippet({url: url}).crmSnippet('refresh');
+      });
+    });
   {/literal}
   </script>
 
diff --git a/civicrm/templates/CRM/Admin/Form/Setting/Smtp.tpl b/civicrm/templates/CRM/Admin/Form/Setting/Smtp.tpl
index 9df670fd0eee89f53756bf37cd60dd27e3c1cb56..83c2485186f15e5ead91adbd6d1fbc01411f25a5 100644
--- a/civicrm/templates/CRM/Admin/Form/Setting/Smtp.tpl
+++ b/civicrm/templates/CRM/Admin/Form/Setting/Smtp.tpl
@@ -85,7 +85,7 @@
         </div>
         <div class="spacer"></div>
         <div class="crm-submit-buttons">
-            {include file="CRM/common/formButtons.tpl"}
+            {include file="CRM/common/formButtons.tpl" location=''}
         </div>
 
 {literal}
diff --git a/civicrm/templates/CRM/Admin/Form/Setting/UF.tpl b/civicrm/templates/CRM/Admin/Form/Setting/UF.tpl
index be63b583363b045760ebbd1782e11255ddf950e8..9938e4d90c33f1a29f164589e4b6801708142d1c 100644
--- a/civicrm/templates/CRM/Admin/Form/Setting/UF.tpl
+++ b/civicrm/templates/CRM/Admin/Form/Setting/UF.tpl
@@ -34,12 +34,11 @@
         </table>
             <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
 <div class="spacer"></div>
-{if $tablePrefixes}
+{if $viewsIntegration}
 <div class="form-item">
 <fieldset>
     <legend>{ts}Views integration settings{/ts}</legend>
-    <div>{ts}To enable CiviCRM Views integration, add or update the following item in the <code>settings.php</code> file:{/ts}</div>
-    <pre>{$tablePrefixes}</pre>
+    <div>{$viewsIntegration}</div>
 </fieldset>
 </div>
 {/if}
diff --git a/civicrm/templates/CRM/Batch/Form/Search.tpl b/civicrm/templates/CRM/Batch/Form/Search.tpl
index dd1379ebdd2c32a06f7a082f61c04c3642292c6a..66054c2c9c6ea030b3b9c977bee322daf88ef3c9 100644
--- a/civicrm/templates/CRM/Batch/Form/Search.tpl
+++ b/civicrm/templates/CRM/Batch/Form/Search.tpl
@@ -17,7 +17,7 @@
         {ts}Complete OR partial batch name.{/ts}
         </span>
       </td>
-      <td>{include file="CRM/common/formButtons.tpl"}</td>
+      <td>{include file="CRM/common/formButtons.tpl" location=''}</td>
     </tr>
   </table>
 </div>
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/Address.tpl b/civicrm/templates/CRM/Contact/Form/Inline/Address.tpl
index 56bb901e2396a1ad5cfb92fd15750842edb45665..2a9ceebcea34169a3e70633dee613abf62e1c759 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/Address.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/Address.tpl
@@ -13,7 +13,7 @@
     <tr>
       <td>
         <div class="crm-submit-buttons">
-          {include file="CRM/common/formButtons.tpl"}
+          {include file="CRM/common/formButtons.tpl" location=''}
           {if $addressId}
             &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
             <a class="button delete-button" href="#" style="display:inline-block;float:none;"><div class="icon delete-icon"></div> {ts}Delete{/ts}</a>
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/CommunicationPreferences.tpl b/civicrm/templates/CRM/Contact/Form/Inline/CommunicationPreferences.tpl
index ccefcd680967c2e0ae8feab5c1cefcc15ba5af63..eda461fe0d72bcae868f1c517f254d72c02145c9 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/CommunicationPreferences.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/CommunicationPreferences.tpl
@@ -13,7 +13,7 @@
 
  <div class="crm-inline-edit-form">
     <div class="crm-inline-button">
-      {include file="CRM/common/formButtons.tpl"}
+      {include file="CRM/common/formButtons.tpl" location=''}
     </div>
     <div class="crm-clear">
       {foreach key=key item=item from=$commPreference}
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/ContactInfo.tpl b/civicrm/templates/CRM/Contact/Form/Inline/ContactInfo.tpl
index f9efa336c43ecaeb204654d120f1be9fb02f8867..137bd07e51a630f8ee4d725788b10a1c77bbcd1d 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/ContactInfo.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/ContactInfo.tpl
@@ -10,7 +10,7 @@
 {$form.oplock_ts.html}
 <div class="crm-inline-edit-form">
   <div class="crm-inline-button">
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
   </div>
 
   <div class="crm-clear">
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/ContactName.tpl b/civicrm/templates/CRM/Contact/Form/Inline/ContactName.tpl
index 021cff1c51e9ff1772311d527d1ea832af881be3..6b9aacfde0f24074119f6af5e61cd04ce9590e3c 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/ContactName.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/ContactName.tpl
@@ -11,7 +11,7 @@
 {$form.oplock_ts.html}
 <div class="crm-inline-edit-form">
   <div class="crm-inline-button">
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
   </div>
   {crmRegion name="contact-form-inline-contactname"}
     {if $contactType eq 'Individual'}
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/CustomData.tpl b/civicrm/templates/CRM/Contact/Form/Inline/CustomData.tpl
index 43961f65cae0bbc652b19cbf7f1d3b24372fb83c..0d3929be131454e8cd35b421e274a1a9f01ae0eb 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/CustomData.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/CustomData.tpl
@@ -11,7 +11,7 @@
 {$form.oplock_ts.html}
 <div class="crm-inline-edit-form">
   <div class="crm-inline-button">
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
   </div>
   {include file="CRM/Custom/Form/CustomData.tpl" skipTitle=true}
 </div> <!-- end of main -->
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/Demographics.tpl b/civicrm/templates/CRM/Contact/Form/Inline/Demographics.tpl
index a26dedfe54a4cec5d56b7bd9b4833fbdffd5fa8a..e4e8f2e701478d566e277b3e32e357e4b9519e4f 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/Demographics.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/Demographics.tpl
@@ -10,7 +10,7 @@
 {$form.oplock_ts.html}
 <div class="crm-inline-edit-form">
   <div class="crm-inline-button">
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
   </div>
 
   <div class="crm-clear">
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/Email.tpl b/civicrm/templates/CRM/Contact/Form/Inline/Email.tpl
index b4b11bc7285e3b6860ccd246732301a931808b5e..e84a75251141f71b35fc0e7a77f8b17de8ba4954 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/Email.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/Email.tpl
@@ -13,7 +13,7 @@
   <tr>
     <td colspan="5">
       <div class="crm-submit-buttons">
-        {include file="CRM/common/formButtons.tpl"}
+        {include file="CRM/common/formButtons.tpl" location=''}
       </div>
     </td>
   </tr>
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/IM.tpl b/civicrm/templates/CRM/Contact/Form/Inline/IM.tpl
index e76a1b15dc917f4d8d162c132791e6f5d7cba31c..5291433fbb4c1be208ae9ca3c4ee8452371d9298 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/IM.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/IM.tpl
@@ -13,7 +13,7 @@
     <tr>
       <td colspan="5">
         <div class="crm-submit-buttons">
-          {include file="CRM/common/formButtons.tpl"}
+          {include file="CRM/common/formButtons.tpl" location=''}
         </div>
       </td>
     </tr>
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/OpenID.tpl b/civicrm/templates/CRM/Contact/Form/Inline/OpenID.tpl
index bcb0a0947222ff044fe571351abaff07d9b99d82..7fd73b6118e37f4f02459eb71c04b1506b45bd50 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/OpenID.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/OpenID.tpl
@@ -13,7 +13,7 @@
   <tr>
     <td colspan="4">
       <div class="crm-submit-buttons">
-        {include file="CRM/common/formButtons.tpl"}
+        {include file="CRM/common/formButtons.tpl" location=''}
       </div>
     </td>
   </tr>
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/Phone.tpl b/civicrm/templates/CRM/Contact/Form/Inline/Phone.tpl
index 3e2c3af7c10ed16dabc39d140f217d8a39bbba54..fa90b955ced5d1b3ef9584d3c946439271d9c526 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/Phone.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/Phone.tpl
@@ -13,7 +13,7 @@
     <tr>
       <td colspan="5">
         <div class="crm-submit-buttons">
-          {include file="CRM/common/formButtons.tpl"}
+          {include file="CRM/common/formButtons.tpl" location=''}
         </div>
       </td>
     </tr>
diff --git a/civicrm/templates/CRM/Contact/Form/Inline/Website.tpl b/civicrm/templates/CRM/Contact/Form/Inline/Website.tpl
index e5d5bf5838f4277ebe48f1c833b4cad6f953dd69..f58852cc8dcb420fedb967c760fd11418098c2a9 100644
--- a/civicrm/templates/CRM/Contact/Form/Inline/Website.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Inline/Website.tpl
@@ -13,7 +13,7 @@
     <tr>
       <td colspan="5">
         <div class="crm-submit-buttons">
-          {include file="CRM/common/formButtons.tpl"}
+          {include file="CRM/common/formButtons.tpl" location=''}
         </div>
       </td>
     </tr>
diff --git a/civicrm/templates/CRM/Contact/Form/Task/AddToParentClass.tpl b/civicrm/templates/CRM/Contact/Form/Task/AddToParentClass.tpl
index a97a33642013541d75d96cb9ee9589a6080daacb..c014c75c2a447808206580f67a3e47732462a318 100644
--- a/civicrm/templates/CRM/Contact/Form/Task/AddToParentClass.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Task/AddToParentClass.tpl
@@ -84,7 +84,7 @@
                     <div class="description">
 
                     </div>
-               <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+               <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
             </div>
 
 
diff --git a/civicrm/templates/CRM/Contact/Form/Task/Delete.tpl b/civicrm/templates/CRM/Contact/Form/Task/Delete.tpl
index 436464f40e34b17c4c47b789a0244a533e3541b2..a8b01b8359608bbffb5ec99d7ae85cac7939dc3f 100644
--- a/civicrm/templates/CRM/Contact/Form/Task/Delete.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Task/Delete.tpl
@@ -22,5 +22,5 @@
 
 
     <h3>{include file="CRM/Contact/Form/Task.tpl"}</h3>
-  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 </div>
diff --git a/civicrm/templates/CRM/Contact/Form/Task/PickProfile.tpl b/civicrm/templates/CRM/Contact/Form/Task/PickProfile.tpl
index ddfe7ac4eed350e59cfe8d35d0b07ae9dae06897..4ddd6cf968ff0597a21cc6ce90f5aa508a8e36db 100644
--- a/civicrm/templates/CRM/Contact/Form/Task/PickProfile.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Task/PickProfile.tpl
@@ -21,6 +21,6 @@
         </td>
     </tr>
 </table>
-<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 </div>
 
diff --git a/civicrm/templates/CRM/Contact/Form/Task/Unhold.tpl b/civicrm/templates/CRM/Contact/Form/Task/Unhold.tpl
index e2f019db575d167bd4991f8d450a2a71bdb6341c..7012f282484fa5a1e25b4716a5efc5dd98ca1253 100644
--- a/civicrm/templates/CRM/Contact/Form/Task/Unhold.tpl
+++ b/civicrm/templates/CRM/Contact/Form/Task/Unhold.tpl
@@ -14,5 +14,5 @@
           <p>{ts}Are you sure you want to unhold email of selected contact(s)?.{/ts} {ts}This action cannot be undone.{/ts}</p>
       <p>{include file="CRM/Contact/Form/Task.tpl"}</p>
     </div>
-<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 </div>
diff --git a/civicrm/templates/CRM/Contact/Import/Form/DataSource.tpl b/civicrm/templates/CRM/Contact/Import/Form/DataSource.tpl
index eb3c00a42603c679e94ea779d4cb2c90c4cc3fee..7d9987b31d4da728fbe40b68952b140c3825afdd 100644
--- a/civicrm/templates/CRM/Contact/Import/Form/DataSource.tpl
+++ b/civicrm/templates/CRM/Contact/Import/Form/DataSource.tpl
@@ -53,10 +53,6 @@
           <td><span id="contact-dedupe_rule_id">{$form.dedupe_rule_id.html}</span> {help id='id-dedupe_rule'}</td>
         </tr>
       {/if}
-      <tr class="crm-import-datasource-form-block-fieldSeparator">
-        <td class="label">{$form.fieldSeparator.label}</td>
-        <td>{$form.fieldSeparator.html} {help id='id-fieldSeparator'}</td>
-      </tr>
       <tr>{include file="CRM/Core/Date.tpl"}</tr>
       <tr>
         <td></td><td class="description">{ts}Select the format that is used for date fields in your import data.{/ts}</td>
diff --git a/civicrm/templates/CRM/Contact/Import/Form/Preview.tpl b/civicrm/templates/CRM/Contact/Import/Form/Preview.tpl
index 47283e4dff1e6b8e819e6996d69d088914f8f009..eab53cdd04a76edf41c0c3740779d8bde503df49 100644
--- a/civicrm/templates/CRM/Contact/Import/Form/Preview.tpl
+++ b/civicrm/templates/CRM/Contact/Import/Form/Preview.tpl
@@ -143,13 +143,13 @@
 {literal}
 <script type="text/javascript">
 
-{/literal}{if $invalidGroupName}{literal}
-cj("#new-group.collapsed").crmAccordionToggle();
-{/literal}{/if}{literal}
+if (cj("#newGroupName").val()) {
+  cj("#new-group.collapsed").crmAccordionToggle();
+}
 
-{/literal}{if $invalidTagName}{literal}
-cj("#new-tag.collapsed").crmAccordionToggle();
-{/literal}{/if}{literal}
+if (cj("#newTagName").val()) {
+  cj("#new-tag.collapsed").crmAccordionToggle();
+}
 
 </script>
 {/literal}
diff --git a/civicrm/templates/CRM/Contact/Page/Inline/Address.tpl b/civicrm/templates/CRM/Contact/Page/Inline/Address.tpl
index 453d330884f9e6ef5908d0965707670ee8c7507c..9c248f2b65d41a906676f9a824bf87916180377a 100644
--- a/civicrm/templates/CRM/Contact/Page/Inline/Address.tpl
+++ b/civicrm/templates/CRM/Contact/Page/Inline/Address.tpl
@@ -31,7 +31,8 @@
               !empty($add.geo_code_2) AND
               is_numeric($add.geo_code_2)
           }
-          <br /><a href="{crmURL p='civicrm/contact/map' q="reset=1&cid=`$contactId`&lid=`$add.location_type_id`"}" title="{ts 1=`$add.location_type`}Map %1 Address{/ts}"><i class="crm-i fa-map-marker" aria-hidden="true"></i> {ts}Map{/ts}</a>
+          {assign var='mapLocationTypeID' value=$add.location_type_id}
+          <br /><a href="{crmURL p='civicrm/contact/map' q="reset=1&cid=$contactId&lid=$mapLocationTypeID"}" title="{ts 1=$add.location_type}Map %1 Address{/ts}"><i class="crm-i fa-map-marker" aria-hidden="true"></i> {ts}Map{/ts}</a>
           {/if}
         </div>
         <div class="crm-content">
diff --git a/civicrm/templates/CRM/Contact/Page/View/Delete.tpl b/civicrm/templates/CRM/Contact/Page/View/Delete.tpl
index 5b8c5115776ef71135a80470174500b8fb20c11d..63d60e06b6c1883341527613b6a8bebe97c21254 100644
--- a/civicrm/templates/CRM/Contact/Page/View/Delete.tpl
+++ b/civicrm/templates/CRM/Contact/Page/View/Delete.tpl
@@ -14,4 +14,4 @@
         <p>{ts}This action cannot be undone.{/ts}</p>
 </div>
 <p>
-<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
diff --git a/civicrm/templates/CRM/Contact/Page/View/Note.tpl b/civicrm/templates/CRM/Contact/Page/View/Note.tpl
index 2ea05f614e01eaae8cdb93ade7064bee58db07f3..cc11d8e9c2c1ac4bde024928558dee5f252c9c29 100644
--- a/civicrm/templates/CRM/Contact/Page/View/Note.tpl
+++ b/civicrm/templates/CRM/Contact/Page/View/Note.tpl
@@ -83,7 +83,7 @@
 {/if}
 {if ($action eq 8)}
 <div class=status>{ts 1=$notes.$id.note}Are you sure you want to delete the note '%1'?{/ts}</div>
-<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 
 {/if}
 
diff --git a/civicrm/templates/CRM/Contribute/Form/AdditionalPayment.tpl b/civicrm/templates/CRM/Contribute/Form/AdditionalPayment.tpl
index 88f1bdae27a98a8bf462255b80b0da328a723ef4..df7c946dba1910ff741092dc74933228af0b59f3 100644
--- a/civicrm/templates/CRM/Contribute/Form/AdditionalPayment.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/AdditionalPayment.tpl
@@ -11,7 +11,7 @@
   {include file="CRM/Contribute/Form/PaymentInfoBlock.tpl"}
   {if !$suppressPaymentFormButtons}
     <div class="crm-submit-buttons">
-       {include file="CRM/common/formButtons.tpl"}
+       {include file="CRM/common/formButtons.tpl" location=''}
     </div>
   {/if}
 {else}
@@ -39,7 +39,7 @@
     {/if}
   {/if}
   <div class="crm-submit-buttons">
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
   </div>
   <table class="form-layout-compressed">
     <tr>
diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution.tpl
index 1549c4f3153f5d0bf5396ed705f1752969b9dae4..702832d7c1253dce649c9846b2008e3d560210c2 100644
--- a/civicrm/templates/CRM/Contribute/Form/Contribution.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Contribution.tpl
@@ -56,7 +56,7 @@
       </div>
     {/if}
       <div class="crm-submit-buttons">
-        {include file="CRM/common/formButtons.tpl"}
+        {include file="CRM/common/formButtons.tpl" location=''}
       </div>
       {if !empty($isOnline)}{assign var=valueStyle value=" class='view-value'"}{else}{assign var=valueStyle value=""}{/if}
       <table class="form-layout-compressed">
diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl
index 0c46bda3780c62d9d784f40bb7f1e8ff3795e722..e5e6f688f786b2ac8ae6965979825eccc434dc93 100644
--- a/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl
@@ -287,7 +287,7 @@
       {include file="CRM/UF/Form/Block.tpl" fields=$customPost}
     </div>
 
-    {if $is_monetary and $form.bank_account_number}
+    {if $is_monetary && array_key_exists('bank_account_number', $form)}
       <div id="payment_notice">
         <fieldset class="crm-public-form-item crm-group payment_notice-group">
           <legend>{ts}Agreement{/ts}</legend>
diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution/MembershipBlock.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution/MembershipBlock.tpl
index 51d6ab1abd85bd99dad3fa3e0d4647bdad4576f1..796834c64bd9ccf9259d646b9018020cb044ad7e 100644
--- a/civicrm/templates/CRM/Contribute/Form/Contribution/MembershipBlock.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Contribution/MembershipBlock.tpl
@@ -34,7 +34,7 @@
           {if !empty($membershipTypes)}
             {foreach from=$membershipTypes item=row}
               {if array_key_exists( 'current_membership', $row )}
-                <div id='help'>
+                <div class='help'>
                   {* Lifetime memberships have no end-date so current_membership array key exists but is NULL *}
                   {if $row.current_membership}
                     {if $row.current_membership|crmDate:"%Y%m%d" LT $smarty.now|crmDate:"%Y%m%d"}
diff --git a/civicrm/templates/CRM/Contribute/Form/ContributionPage/AddProduct.tpl b/civicrm/templates/CRM/Contribute/Form/ContributionPage/AddProduct.tpl
index 3d282628fa6e1072ab9e6ecbbb128a47b6fb8e65..75725faa3928ffae80b15a1cbf4e238dc9bb57bf 100644
--- a/civicrm/templates/CRM/Contribute/Form/ContributionPage/AddProduct.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/ContributionPage/AddProduct.tpl
@@ -52,7 +52,7 @@
   </div>
 {else}
   <div class="crm-done-button">
-      {include file="CRM/common/formButtons.tpl"}
+      {include file="CRM/common/formButtons.tpl" location=''}
   </div>
 {/if} {* $action ne view *}
 </div>
diff --git a/civicrm/templates/CRM/Contribute/Form/ContributionPage/Delete.tpl b/civicrm/templates/CRM/Contribute/Form/ContributionPage/Delete.tpl
index 98544852fb0cca2747f23deab17bb667adbff758..aa7887845dba3e4e14694c15c3f5ca034630e662 100644
--- a/civicrm/templates/CRM/Contribute/Form/ContributionPage/Delete.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/ContributionPage/Delete.tpl
@@ -17,5 +17,5 @@
         {/if}
       </div>
 <div class="form-item">
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
 </div>
diff --git a/civicrm/templates/CRM/Contribute/Form/SearchContribution.tpl b/civicrm/templates/CRM/Contribute/Form/SearchContribution.tpl
index ae2d9650acfc5e3c99caf0d4d8e3edadd740ea0a..a282a719b506eeb6bc10ca8037924fe3484f9b54 100644
--- a/civicrm/templates/CRM/Contribute/Form/SearchContribution.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/SearchContribution.tpl
@@ -34,5 +34,5 @@
     campaignTrClass='' campaignTdClass=''}
 
  </table>
- <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+ <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 </div>
diff --git a/civicrm/templates/CRM/Contribute/Page/UserDashboard.tpl b/civicrm/templates/CRM/Contribute/Page/UserDashboard.tpl
index e662f120446f36648375ff253bb8cbcb707d8904..ec1c9e6559320bc1193945ae60d8afa76ce5440b 100644
--- a/civicrm/templates/CRM/Contribute/Page/UserDashboard.tpl
+++ b/civicrm/templates/CRM/Contribute/Page/UserDashboard.tpl
@@ -27,23 +27,23 @@
                 </tr>
 
                 {foreach from=$contribute_rows item=row}
-                    <tr id='rowid{$row.contribution_id}'
+                    <tr id='rowid{$row.id}'
                         class="{cycle values="odd-row,even-row"}{if !empty($row.cancel_date)} disabled{/if}">
-                        <td>{$row.total_amount|crmMoney:$row.currency} {if !empty($row.amount_level) && !is_array($row.amount_level)} - {$row.amount_level} {/if}
+                        <td>{$row.total_amount|crmMoney:$row.currency} {if !empty($row.amount_level) && !is_array($row.amount_level)} - {$row.amount_level|escape|smarty:nodefaults} {/if}
                             {if !empty($row.contribution_recur_id)}
                                 <br/>
                                 {ts}(Recurring Contribution){/ts}
                             {/if}
                         </td>
-                        <td>{$row.financial_type}</td>
+                        <td>{$row.financial_type|escape|smarty:nodefaults}</td>
                         <td>{$row.receive_date|truncate:10:''|crmDate}</td>
                         <td>{$row.receipt_date|truncate:10:''|crmDate}</td>
                         <td>{$row.balance_amount|crmMoney:$row.currency}</td>
-                        <td>{$row.contribution_status}</td>
+                        <td>{$row.contribution_status|escape|smarty:nodefaults}</td>
                         {if $isIncludeInvoiceLinks}
                           <td>
                             {* @todo Instead of this tpl handling assign actions as an array attached the row, iterate through - will better accomodate extension overrides and competition for scarce real estate on this page*}
-                            {assign var='id' value=$row.contribution_id}
+                            {assign var='id' value=$row.id}
                             {assign var='contact_id' value=$row.contact_id}
                             {assign var='urlParams' value="reset=1&id=$id&cid=$contact_id"}
                             {if call_user_func(array('CRM_Core_Permission','check'), 'view my invoices') OR call_user_func(array('CRM_Core_Permission','check'), 'access CiviContribute')}
@@ -80,42 +80,38 @@
         </div>
     {/if}
 
-
-    {if !empty($honor)}
-        {if $honorRows}
-            {strip}
-                <div class="help">
-                    {ts}Contributions made in your honor{/ts}:
-                </div>
-                <table class="selector">
-                    <tr class="columnheader">
-                        <th>{ts}Contributor{/ts}</th>
-                        <th>{ts}Amount{/ts}</th>
-                        <th>{ts}Type{/ts}</th>
-                        <th>{ts}Financial Type{/ts}</th>
-                        <th>{ts}Received date{/ts}</th>
-                        <th>{ts}Receipt Sent{/ts}</th>
-                        <th>{ts}Status{/ts}</th>
+    {if !empty($soft_credit_contributions)}
+        {strip}
+            <div class="help">
+                {ts}Contributions made in your honor{/ts}:
+            </div>
+            <table class="selector">
+                <tr class="columnheader">
+                    <th>{ts}Contributor{/ts}</th>
+                    <th>{ts}Amount{/ts}</th>
+                    <th>{ts}Type{/ts}</th>
+                    <th>{ts}Financial Type{/ts}</th>
+                    <th>{ts}Received date{/ts}</th>
+                    <th>{ts}Receipt Sent{/ts}</th>
+                    <th>{ts}Status{/ts}</th>
+                </tr>
+                {foreach from=$soft_credit_contributions item=row}
+                    <tr id='rowid{$row.contact_id}' class="{cycle values="odd-row,even-row"}">
+                        <td><a href="{crmURL p="civicrm/contact/view" q="reset=1&cid=`$row.contact_id`"}"
+                               id="view_contact">{$row.display_name|escape|smarty:nodefaults}</a></td>
+                        <td>{$row.total_amount|crmMoney:$row.currency}</td>
+                        <td>{$row.soft_credit_type|escape|smarty:nodefaults}</td>
+                        <td>{$row.financial_type|escape|smarty:nodefaults}</td>
+                        <td>{$row.receive_date|truncate:10:''|crmDate}</td>
+                        <td>{$row.receipt_date|truncate:10:''|crmDate}</td>
+                        <td>{$row.contribution_status|escape|smarty:nodefaults}</td>
                     </tr>
-                    {foreach from=$honorRows item=row}
-                        <tr id='rowid{$row.honorId}' class="{cycle values="odd-row,even-row"}">
-                            <td><a href="{crmURL p="civicrm/contact/view" q="reset=1&cid=`$row.honorId`"}"
-                                   id="view_contact">{$row.display_name}</a></td>
-                            <td>{$row.amount}</td>
-                            <td>{$row.honor_type}</td>
-                            <td>{$row.type}</td>
-                            <td>{$row.receive_date|truncate:10:''|crmDate}</td>
-                            <td>{$row.receipt_date|truncate:10:''|crmDate}</td>
-                            <td>{$row.contribution_status}</td>
-                        </tr>
-                    {/foreach}
-                </table>
-            {/strip}
-        {/if}
+                {/foreach}
+            </table>
+        {/strip}
     {/if}
 
-    {if !empty($recur)}
-        {if $recurRows}
+        {if !empty($recurRows)}
             {strip}
                 <div><label>{ts}Recurring Contribution(s){/ts}</label></div>
                 <table class="selector">
@@ -126,24 +122,23 @@
                         <th>{ts}Created{/ts}</th>
                         <th></th>
                     </tr>
-                    {foreach from=$recurRows item=row key=id}
+                    {foreach from=$recurRows item=row}
                         <tr class="{cycle values="odd-row,even-row"}">
-                            <td><label>{$recurRows.$id.amount|crmMoney}</label>
-                                every {$recurRows.$id.frequency_interval} {$recurRows.$id.frequency_unit}
-                                for {$recurRows.$id.installments} installments
+                            <td><label>{$row.amount|crmMoney}</label>
+                                every {$row.frequency_interval} {$row.frequency_unit} for {$row.installments} installments
                             </td>
-                            <td>{$recurRows.$id.recur_status}</td>
-                            <td>{if $recurRows.$id.completed}<a href="{$recurRows.$id.link}">{$recurRows.$id.completed}
-                                    /{$recurRows.$id.installments}</a>
-                                {else}0/{$recurRows.$id.installments} {/if}</td>
-                            <td>{$recurRows.$id.create_date|crmDate}</td>
-                            <td>{$recurRows.$id.action|replace:'xx':$recurRows.id}</td>
+                            <td>{$row.recur_status|escape|smarty:nodefaults}</td>
+                            <td>{if $row.completed}<a href="{$row.link}">{$row.completed}
+                                    /{$row.installments}</a>
+                                {else}0/{$row.installments} {/if}</td>
+                            <td>{$row.create_date|crmDate}</td>
+                            <td>{$row.action|replace:'xx':$row.id}</td>
                         </tr>
                     {/foreach}
                 </table>
             {/strip}
         {/if}
-    {/if}
+
 </div>
 {crmRegion name="crm-contribute-userdashboard-post"}
 {/crmRegion}
diff --git a/civicrm/templates/CRM/Custom/Form/Edit/CustomField.tpl b/civicrm/templates/CRM/Custom/Form/Edit/CustomField.tpl
index 119e8bbdabd8dcbc2e37ea045bf5bd47ae7fc220..fbf5ee179ef9ec2a378641cb1e7bc68fd74baa55 100644
--- a/civicrm/templates/CRM/Custom/Form/Edit/CustomField.tpl
+++ b/civicrm/templates/CRM/Custom/Form/Edit/CustomField.tpl
@@ -26,7 +26,7 @@
           {assign var="index" value="1"}
           {foreach name=outer key=key item=item from=$formElement}
           {if $index < 10}
-          {assign var="index" value=`$index+1`}
+          {assign var="index" value=$index+1}
           {else}
           <td class="labels font-light">{$formElement.$key.html}</td>
           {if $count == $element.options_per_line}
@@ -34,7 +34,7 @@
         <tr>
           {assign var="count" value="1"}
           {else}
-          {assign var="count" value=`$count+1`}
+          {assign var="count" value=$count+1}
           {/if}
           {/if}
           {/foreach}
diff --git a/civicrm/templates/CRM/Custom/Form/Field.tpl b/civicrm/templates/CRM/Custom/Form/Field.tpl
index c2e13ab33cc58917d593bf878a614cc0d07f0262..05df6d9dd1dc934fb67ba914d659f44272e199f1 100644
--- a/civicrm/templates/CRM/Custom/Form/Field.tpl
+++ b/civicrm/templates/CRM/Custom/Form/Field.tpl
@@ -26,6 +26,10 @@
       <td class="label">{$form.html_type.label}</td>
       <td class="html-adjust">{$form.html_type.html}</td>
     </tr>
+    <tr class="crm-custom-field-form-block-fk_entity">
+      <td class="label">{$form.fk_entity.label} <span class="crm-marker">*</span></td>
+      <td class="html-adjust">{$form.fk_entity.html}</td>
+    </tr>
     <tr class="crm-custom-field-form-block-serialize">
       <td class="label">{$form.serialize.label}</td>
       <td class="html-adjust">{$form.serialize.html}</td>
@@ -185,8 +189,13 @@
       if (!$('#html_type', $form).val()) {
         $('#html_type', $form).val(dataToHTML[dataType][0]).change();
       }
+      // Hide html_type if there is only one option
+      $('.crm-custom-field-form-block-html_type').toggle(allowedHtmlTypes.length > 1);
       customOptionHtmlType(dataType);
       makeDefaultValueField(dataType);
+
+      // Show/hide entityReference selector
+      $('.crm-custom-field-form-block-fk_entity').toggle(dataType === 'EntityReference');
     }
 
     function onChangeHtmlType() {
@@ -282,7 +291,7 @@
 
       $("#noteColumns, #noteRows, #noteLength", $form).toggle(dataType === 'Memo');
 
-      $(".crm-custom-field-form-block-serialize", $form).toggle(htmlType === 'Select' || htmlType === 'Autocomplete-Select');
+      $(".crm-custom-field-form-block-serialize", $form).toggle(htmlType === 'Select' || htmlType === 'Autocomplete-Select' && dataType !== 'EntityReference');
     }
 
     function makeDefaultValueField(dataType) {
diff --git a/civicrm/templates/CRM/Event/Form/Registration/AdditionalParticipant.tpl b/civicrm/templates/CRM/Event/Form/Registration/AdditionalParticipant.tpl
index a38d3f94b82debd5c4bf1732cd5e6f6f57d642fe..1c00dbfcc6b4f91a687ff4418b8074188908c2a9 100644
--- a/civicrm/templates/CRM/Event/Form/Registration/AdditionalParticipant.tpl
+++ b/civicrm/templates/CRM/Event/Form/Registration/AdditionalParticipant.tpl
@@ -55,7 +55,7 @@
 </div>
 
 <div id="crm-submit-buttons" class="crm-submit-buttons">
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
 </div>
 </div>
 
diff --git a/civicrm/templates/CRM/Event/Form/Search.tpl b/civicrm/templates/CRM/Event/Form/Search.tpl
index b89051349c4fcd7907062e849923b613a0e0a8fb..bd75709472b3270f6c7111ef823406c7d9465a1c 100644
--- a/civicrm/templates/CRM/Event/Form/Search.tpl
+++ b/civicrm/templates/CRM/Event/Form/Search.tpl
@@ -24,7 +24,7 @@
         {include file="CRM/Event/Form/Search/Common.tpl"}
 
         <tr>
-           <td colspan="2">{include file="CRM/common/formButtons.tpl"}</td>
+           <td colspan="2">{include file="CRM/common/formButtons.tpl" location=''}</td>
         </tr>
         </table>
     {/strip}
diff --git a/civicrm/templates/CRM/Event/Form/SearchEvent.tpl b/civicrm/templates/CRM/Event/Form/SearchEvent.tpl
index fb278a55bebad20ae1fdf6259e430ee792f92a01..fc064e42b976f4eafb03914a9454b1d3485b5a69 100644
--- a/civicrm/templates/CRM/Event/Form/SearchEvent.tpl
+++ b/civicrm/templates/CRM/Event/Form/SearchEvent.tpl
@@ -47,7 +47,7 @@
     {* campaign in event search *}
     {include file="CRM/Campaign/Form/addCampaignToSearch.tpl"
     campaignTrClass='crm-event-searchevent-form-block-campaign_id' campaignTdClass=''}
-    <td class="right">{include file="CRM/common/formButtons.tpl"}</td>
+    <td class="right">{include file="CRM/common/formButtons.tpl" location=''}</td>
   </table>
     </div>
   </div>
diff --git a/civicrm/templates/CRM/Event/Form/Task/Batch.tpl b/civicrm/templates/CRM/Event/Form/Task/Batch.tpl
index 895ec862e66f28ee8ac2e85ccf4694eb12fd2c77..27760a6b08f1008318c7bb0ad62d5b7d9cd96fe6 100644
--- a/civicrm/templates/CRM/Event/Form/Task/Batch.tpl
+++ b/civicrm/templates/CRM/Event/Form/Task/Batch.tpl
@@ -93,7 +93,7 @@
            </tr>
          </table>
 <div class="crm-submit-buttons">
-{if $fields}{$form._qf_Batch_refresh.html}{/if}{include file="CRM/common/formButtons.tpl"}
+{if $fields}{$form._qf_Batch_refresh.html}{/if}{include file="CRM/common/formButtons.tpl" location=''}
 </div>
 </div>
 
diff --git a/civicrm/templates/CRM/Event/Form/Task/Print.tpl b/civicrm/templates/CRM/Event/Form/Task/Print.tpl
index 689580967b0044861ac459262566a48f38a32531..a321e22098d67c149959d9889a279c981406c855 100644
--- a/civicrm/templates/CRM/Event/Form/Task/Print.tpl
+++ b/civicrm/templates/CRM/Event/Form/Task/Print.tpl
@@ -54,7 +54,7 @@
 </table>
 
 <div class="form-item">
-     <span class="element-right">{include file="CRM/common/formButtons.tpl"}</span>
+     <span class="element-right">{include file="CRM/common/formButtons.tpl" location=''}</span>
 </div>
 
 {else}
diff --git a/civicrm/templates/CRM/Event/Form/Task/Result.tpl b/civicrm/templates/CRM/Event/Form/Task/Result.tpl
index ccd03315d4d165ca5b22c4d79916a7266c7e39c8..755e964d0e3e242697b0fb76b831fce60069d9f9 100644
--- a/civicrm/templates/CRM/Event/Form/Task/Result.tpl
+++ b/civicrm/templates/CRM/Event/Form/Task/Result.tpl
@@ -9,6 +9,6 @@
 *}
 <div class='spacer'></div>
 <div class="crm-submit-buttons">
-     {include file="CRM/common/formButtons.tpl"}
+     {include file="CRM/common/formButtons.tpl" location=''}
 </div>
 
diff --git a/civicrm/templates/CRM/Event/Page/iCalLinks.tpl b/civicrm/templates/CRM/Event/Page/iCalLinks.tpl
index 2f6a31556762d912b51d2076a2159935bb772dbb..932666e217b8087cb20e6000bb0f39d03601e80a 100644
--- a/civicrm/templates/CRM/Event/Page/iCalLinks.tpl
+++ b/civicrm/templates/CRM/Event/Page/iCalLinks.tpl
@@ -9,7 +9,7 @@
 *}
 {* Display icons / links for ical download and feed for EventInfo.tpl, ThankYou.tpl, DashBoard.tpl, and ManageEvent.tpl *}
   {foreach from=$iCal item="iCalItem"}
-  <a href="{$iCalItem.url}" {if !empty($event)} class="crm-event-feed-link"{/if}>
+  <a href="{$iCalItem.url}" {if $isShowICalIconsInline} class="crm-event-feed-link"{/if}>
     <span class="fa-stack" aria-hidden="true"><i class="crm-i fa-calendar-o fa-stack-2x"></i><i style="top: 15%;" class="crm-i {$iCalItem.icon} fa-stack-1x"></i></span>
     <span class="label">{$iCalItem.text}</span>
   </a>
diff --git a/civicrm/templates/CRM/Form/body.tpl b/civicrm/templates/CRM/Form/body.tpl
index 4f8e4667bf6de4a0a2496b29a5edef35fe160982..20b8b576297ab0b5f410c6d3c2cf3aff52aa4c9d 100644
--- a/civicrm/templates/CRM/Form/body.tpl
+++ b/civicrm/templates/CRM/Form/body.tpl
@@ -15,7 +15,7 @@
   <div>{$form.hidden}</div>
 {/if}
 
-{if ($snippet !== 'json') and !$suppressForm and count($form.errors) gt 0}
+{if ($snippet !== 'json') and !$suppressForm and $form.errors !== NULL && count($form.errors) gt 0}
    <div class="messages crm-error">
        <i class="crm-i fa-exclamation-triangle crm-i-red" aria-hidden="true"></i>
      {ts}Please correct the following errors in the form fields below:{/ts}
diff --git a/civicrm/templates/CRM/Mailing/Form/Approve.tpl b/civicrm/templates/CRM/Mailing/Form/Approve.tpl
index 2cc127b95056b7e571828d91550305b7c23125fb..18e4779649bb60cb5d8150d030cd692bfc788adb 100644
--- a/civicrm/templates/CRM/Mailing/Form/Approve.tpl
+++ b/civicrm/templates/CRM/Mailing/Form/Approve.tpl
@@ -22,7 +22,7 @@
     </tr>
   </tbody>
 </table>
-<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 
 <div class="crm-accordion-wrapper crm-plain_text_email-accordion collapsed">
     <div class="crm-accordion-header">
diff --git a/civicrm/templates/CRM/Mailing/Form/Component.tpl b/civicrm/templates/CRM/Mailing/Form/Component.tpl
index d176406c0299ef2471c1c45c65f933dfc0169fd9..ab135483bb8bbb91dd5759642a352dcd5e282bc2 100644
--- a/civicrm/templates/CRM/Mailing/Form/Component.tpl
+++ b/civicrm/templates/CRM/Mailing/Form/Component.tpl
@@ -20,5 +20,5 @@
     <tr class="crm-mailing-component-form-block-is_active"><td class="label">{$form.is_active.label}</td><td>{$form.is_active.html}</td>
   </table>
 </fieldset>
-<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 </div>
diff --git a/civicrm/templates/CRM/Mailing/MailingUI.hlp b/civicrm/templates/CRM/Mailing/MailingUI.hlp
index c8f4b148f868a4636baaa3c97bebee8b8dc517d6..f3c4038e2398ef37efb036019c43d64ab89b263d 100644
--- a/civicrm/templates/CRM/Mailing/MailingUI.hlp
+++ b/civicrm/templates/CRM/Mailing/MailingUI.hlp
@@ -15,7 +15,7 @@
 </p>
 {/htxt}
 
-{htxt id ="from_email"}
+{htxt id="from_email"}
   <p>{ts}Select the "FROM" Email Address for this mailing from the dropdown list. Available email addresses are configurable by users with Administer CiviCRM permission. EXAMPLE: "Client Services" &lt;clientservices@example.org&gt;{/ts}</p>
 {if call_user_func(array('CRM_Core_Permission','check'), 'administer CiviCRM') }
   {capture assign="fromConfig"}{crmURL p="civicrm/admin/options/from_email_address" q="reset=1"}{/capture}
diff --git a/civicrm/templates/CRM/Member/Form/Search.tpl b/civicrm/templates/CRM/Member/Form/Search.tpl
index 7db4c4bfc8b610342aeeb4190690b15b0630dc93..20b929411544215c02200a55cb9e94ee89e1b527 100644
--- a/civicrm/templates/CRM/Member/Form/Search.tpl
+++ b/civicrm/templates/CRM/Member/Form/Search.tpl
@@ -19,7 +19,7 @@
           {include file="CRM/Member/Form/Search/Common.tpl"}
 
           <tr>
-              <td colspan="2">{include file="CRM/common/formButtons.tpl"}</td>
+              <td colspan="2">{include file="CRM/common/formButtons.tpl" location=''}</td>
           </tr>
       </table>
   {/strip}
diff --git a/civicrm/templates/CRM/Member/Form/Task/Result.tpl b/civicrm/templates/CRM/Member/Form/Task/Result.tpl
index d3017428f48569e175cdeaa867e104133180c0e0..a7b04880d9512f5a5045ecbdca745b15aed89ef4 100644
--- a/civicrm/templates/CRM/Member/Form/Task/Result.tpl
+++ b/civicrm/templates/CRM/Member/Form/Task/Result.tpl
@@ -10,7 +10,7 @@
 <div class='spacer'></div>
 <div class="form-item">
     <p>
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
     </p>
 </div>
 
diff --git a/civicrm/templates/CRM/Price/Form/DeleteSet.tpl b/civicrm/templates/CRM/Price/Form/DeleteSet.tpl
index a53d480358788d0510f72282c7df372611654bcd..4dd00ba129e856d35478a844251862952734752f 100644
--- a/civicrm/templates/CRM/Price/Form/DeleteSet.tpl
+++ b/civicrm/templates/CRM/Price/Form/DeleteSet.tpl
@@ -11,9 +11,9 @@
     <div class="messages status no-popup">
      <img src="{$config->resourceBase}i/Inform.gif" alt="{ts}status{/ts}"/>
           {ts 1=$title}WARNING: Deleting this price set will result in the loss of all '%1' data.{/ts} {ts}This action cannot be undone.{/ts} {ts}Do you want to continue?{/ts}
-       
+
     </div>
 
 <div class="form-item">
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
 </div>
diff --git a/civicrm/templates/CRM/Price/Form/Option.tpl b/civicrm/templates/CRM/Price/Form/Option.tpl
index 6924efaad0fbaf981b8bee9db7aec29dd6d30e55..a7d351009d0f78f0aacab107fafcd121c4178e39 100644
--- a/civicrm/templates/CRM/Price/Form/Option.tpl
+++ b/civicrm/templates/CRM/Price/Form/Option.tpl
@@ -114,7 +114,7 @@
 
 
   <div class="crm-submit-buttons">
-    {include file="CRM/common/formButtons.tpl"}
+    {include file="CRM/common/formButtons.tpl" location=''}
   </div>
 
 </div>
diff --git a/civicrm/templates/CRM/Profile/Form/Dynamic.tpl b/civicrm/templates/CRM/Profile/Form/Dynamic.tpl
index ba9421498cf515a7882cc6bf1681f65c163c7dc8..c35fdce222ab6f390fffa05bad3b2b587f3e9e15 100644
--- a/civicrm/templates/CRM/Profile/Form/Dynamic.tpl
+++ b/civicrm/templates/CRM/Profile/Form/Dynamic.tpl
@@ -50,7 +50,7 @@
     {if $action eq 2 and $multiRecordFieldListing}
       <h1>{ts}Edit Details{/ts}</h1>
       <div class="crm-submit-buttons" style='float:right'>
-      {include file="CRM/common/formButtons.tpl"}{if $isDuplicate}{$form._qf_Edit_upload_duplicate.html}{/if}
+      {include file="CRM/common/formButtons.tpl" location=''}{if $isDuplicate}{$form._qf_Edit_upload_duplicate.html}{/if}
       </div>
     {/if}
 
@@ -201,7 +201,7 @@
         </div>
       {/if}
       <div class="crm-submit-buttons" style='{$floatStyle}'>
-        {include file="CRM/common/formButtons.tpl"}{if $isDuplicate}{$form._qf_Edit_upload_duplicate.html}{/if}
+        {include file="CRM/common/formButtons.tpl" location=''}{if $isDuplicate}{$form._qf_Edit_upload_duplicate.html}{/if}
         {if $includeCancelButton}
           <a class="button cancel" href="{$cancelURL}">
             <span>
diff --git a/civicrm/templates/CRM/Profile/Form/Search.tpl b/civicrm/templates/CRM/Profile/Form/Search.tpl
index 0b093ea4504798e77fa6334a8024d2df5db49ef4..a0b0f0c46b55536ee75aff8e02f019a2b8fd1fd3 100644
--- a/civicrm/templates/CRM/Profile/Form/Search.tpl
+++ b/civicrm/templates/CRM/Profile/Form/Search.tpl
@@ -75,7 +75,7 @@
       <tr><td colspan="2">{include file="CRM/Contact/Form/Task/ProximityCommon.tpl"}</td></tr>
     {/if}
 
-    <tr><td></td><td>{include file="CRM/common/formButtons.tpl"}</td></tr>
+    <tr><td></td><td>{include file="CRM/common/formButtons.tpl" location=''}</td></tr>
   </table>
 
   {if $groupId}
diff --git a/civicrm/templates/CRM/SMS/Form/Group.hlp b/civicrm/templates/CRM/SMS/Form/Group.hlp
index 1fc6605f4518678c8b0f99011dcb66155a038f35..99a8e5fc63bf69b38103649459c67bb55f1c81c3 100644
--- a/civicrm/templates/CRM/SMS/Form/Group.hlp
+++ b/civicrm/templates/CRM/SMS/Form/Group.hlp
@@ -47,10 +47,10 @@
 <p>{ts}If you have sent other Mass SMS - you can additionally Include (or Exclude) contacts who received those Mass SMS. CiviCRM will eliminate any duplications so that contacts who are in an Included Group AND were recipients of an Included Mailing will only be sent one copy of this SMS.{/ts}</p>
 {/htxt}
 
-{htxt id ="id-sms_provider-title"}
+{htxt id="id-sms_provider-title"}
   {ts}SMS Provider{/ts}
 {/htxt}
-{htxt id ="id-sms_provider"}
+{htxt id="id-sms_provider"}
 <p>{ts}Select the SMS provider for this mass message from the dropdown list.{/ts}</p>
 {if $params.isAdmin}
     {capture assign="fromConfig"}{crmURL p="civicrm/admin/sms/provider" q="reset=1"}{/capture}
diff --git a/civicrm/templates/CRM/SMS/Form/Group.tpl b/civicrm/templates/CRM/SMS/Form/Group.tpl
index 2ce5486d8c75d96b3c9557c023c64dfeb506324b..3df24931d5d89dadc239dcf8be3a352b5c725582 100644
--- a/civicrm/templates/CRM/SMS/Form/Group.tpl
+++ b/civicrm/templates/CRM/SMS/Form/Group.tpl
@@ -49,7 +49,7 @@
  </div><!-- /.crm-accordion-body -->
 </div><!-- /.crm-accordion-wrapper -->
 
-  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 </div>
 
 </div>
diff --git a/civicrm/templates/CRM/SMS/Form/Schedule.tpl b/civicrm/templates/CRM/SMS/Form/Schedule.tpl
index 7e168f26e3bbdd5637cc0bd3bc0420872d069624..fe5af9ab515700e701a283d289f711fa7f0d60a8 100644
--- a/civicrm/templates/CRM/SMS/Form/Schedule.tpl
+++ b/civicrm/templates/CRM/SMS/Form/Schedule.tpl
@@ -24,7 +24,7 @@
   </div>
   <div class="description">{ts}Set a date and time when you want CiviSMS to start sending this Mass SMS.{/ts}</div>
 </div>
-<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 
 {if $preview}
 <div class="crm-accordion-wrapper crm-plain_text_sms-accordion collapsed">
diff --git a/civicrm/templates/CRM/SMS/Form/Upload.tpl b/civicrm/templates/CRM/SMS/Form/Upload.tpl
index 617b0795824d73c0ea53432638fd59873522f46e..c9b425d4c5ce89c0e819930e786abf02b06e40d6 100644
--- a/civicrm/templates/CRM/SMS/Form/Upload.tpl
+++ b/civicrm/templates/CRM/SMS/Form/Upload.tpl
@@ -40,7 +40,7 @@
     </table>
   </fieldset>
 
-  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl"}</div>
+  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location=''}</div>
 </div><!-- / .crm-form-block -->
 
 {* -- Javascript for showing/hiding the upload/compose options -- *}
diff --git a/civicrm/templates/CRM/UF/Form/Preview.tpl b/civicrm/templates/CRM/UF/Form/Preview.tpl
index 5d01c52e83b7eac2e46aab44f81d75beaae70b05..056c2b278f559360b40010e9188571445b80146a 100644
--- a/civicrm/templates/CRM/UF/Form/Preview.tpl
+++ b/civicrm/templates/CRM/UF/Form/Preview.tpl
@@ -40,6 +40,6 @@
 {/if} {* fields array is not empty *}
 
   <div class="crm-submit-buttons">
-  {include file="CRM/common/formButtons.tpl"}
+  {include file="CRM/common/formButtons.tpl" location=''}
   </div>
 </div>
diff --git a/civicrm/templates/CRM/common/formButtons.tpl b/civicrm/templates/CRM/common/formButtons.tpl
index 649739f5741c709eccf1bd090535256cfbf10e4c..be89d454e1ebcf1d670d1c19c8b4fc82b9df09f0 100644
--- a/civicrm/templates/CRM/common/formButtons.tpl
+++ b/civicrm/templates/CRM/common/formButtons.tpl
@@ -30,7 +30,7 @@
 
 {foreach from=$form.buttons item=button key=key name=btns}
   {if $key|substring:0:4 EQ '_qf_'}
-    {if !empty($location)}
+    {if $location}
       {$form.buttons.$key.html|crmReplace:id:"$key-$location"}
     {else}
       {$form.buttons.$key.html}
diff --git a/civicrm/templates/CRM/common/success.tpl b/civicrm/templates/CRM/common/success.tpl
index 4ebd3f19c1ca43709ce524528adb6b82b9437f25..2fdc0b5eb2e662e4dbd213e49f287a0488d79dcc 100644
--- a/civicrm/templates/CRM/common/success.tpl
+++ b/civicrm/templates/CRM/common/success.tpl
@@ -48,10 +48,6 @@
         </p>
       </div>
       <p><span class="crm-status-icon success"> </span>{$message}</p>
-      {if !empty($afterUpgradeMessage)}
-        <h3>{ts}Important Notes{/ts}</h3>
-        <p>{$afterUpgradeMessage}</p>
-      {/if}
       <p><a href="{crmURL p='civicrm/dashboard' q='reset=1'}" title="{ts}CiviCRM home page{/ts}" style="text-decoration: underline;">{ts}Return to CiviCRM home page.{/ts}</a></p>
     </div>
 {/if}
diff --git a/civicrm/vendor/autoload.php b/civicrm/vendor/autoload.php
index e35fae40b4fc0375a873d5446ae556aee9ee7c29..38c3c6e530893d5ab97809298eb5662b778f92d8 100644
--- a/civicrm/vendor/autoload.php
+++ b/civicrm/vendor/autoload.php
@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInit85641ecafad0fd91a9bd9d9f47db5e32::getLoader();
+return ComposerAutoloaderInite1ca47ea801818fa75d60df4388c9f11::getLoader();
diff --git a/civicrm/vendor/cache/integration-tests/.gitignore b/civicrm/vendor/cache/integration-tests/.gitignore
deleted file mode 100755
index 77bed77d04129369e9214d1953a088c3dc14ad4d..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-.phpunit.result.cache
-composer.lock
-vendor
diff --git a/civicrm/vendor/cache/integration-tests/.travis.yml b/civicrm/vendor/cache/integration-tests/.travis.yml
deleted file mode 100644
index 52786a2a76c6ae5d03a5d49d68ead8eec699dbc4..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/.travis.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-dist: trusty
-language: php
-sudo: true
-
-env:
-  global:
-    - SYMFONY_PHPUNIT_VERSION=5.7
-
-matrix:
-  fast_finish: true
-  include:
-    - php: 7.0
-      env: SUITE=PHPCache
-    - php: 7.0
-      env: SUITE=Symfony
-    - php: 7.0
-      env: SUITE=Laravel
-    - php: 7.0
-      env: SUITE=Stash
-    - php: 7.1
-      env: SUITE=PHPCache
-    - php: 7.2
-      env: SUITE=PHPCache
-  allow_failures:
-    - env: SUITE=Stash
-
-services:
-  - redis
-  - memcached
-
-cache:
-  directories:
-    - "$HOME/.composer/cache"
-
-before_install:
-  - echo "Disable xdebug" && phpenv config-rm xdebug.ini
-
-install:
-  - echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
-  - echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
-  - composer update --prefer-source
-
-script:
-  - ./vendor/bin/simple-phpunit --testsuite $SUITE
-
diff --git a/civicrm/vendor/cache/integration-tests/LICENSE b/civicrm/vendor/cache/integration-tests/LICENSE
deleted file mode 100755
index f166f6e87e073203fc5e311c1545d39fa40fa45d..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2015 Aaron Scherer
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
diff --git a/civicrm/vendor/cache/integration-tests/README.md b/civicrm/vendor/cache/integration-tests/README.md
deleted file mode 100644
index d8c4901b4d8f065dca9aa9b539ab1b77b57b561c..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/README.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# PSR-6 and PSR-16 Integration tests
-[![Gitter](https://badges.gitter.im/php-cache/cache.svg)](https://gitter.im/php-cache/cache?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-[![Latest Stable Version](https://poser.pugx.org/cache/integration-tests/v/stable)](https://packagist.org/packages/cache/integration-tests)
-[![Total Downloads](https://poser.pugx.org/cache/integration-tests/downloads)](https://packagist.org/packages/cache/integration-tests)
-[![Monthly Downloads](https://poser.pugx.org/cache/integration-tests/d/monthly.png)](https://packagist.org/packages/cache/integration-tests)
-[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
-
-This repository contains integration tests to make sure your implementation of a PSR-6 and/or PSR-16 cache follows the rules by PHP-FIG.
-It is a part of the PHP Cache organisation. To read about us please read the shared documentation at [www.php-cache.com](http://www.php-cache.com).
-
-### Install
-
-```bash
-composer require --dev cache/integration-tests:dev-master
-```
-
-### Use
-
-Create a test that looks like this:
-
-```php
-class PoolIntegrationTest extends CachePoolTest
-{
-    public function createCachePool()
-    {
-        return new CachePool();
-    }
-}
-```
-
-You could also test your tag implementation:
-
-```php
-class TagIntegrationTest extends TaggableCachePoolTest
-{
-    public function createCachePool()
-    {
-        return new CachePool();
-    }
-}
-```
-
-You can also test a PSR-16 implementation:
-
-```php
-class CacheIntegrationTest extends SimpleCacheTest
-{
-    public function createSimpleCache()
-    {
-        return new SimpleCache();
-    }
-}
-```
-
-### Contribute
-
-Contributions are very welcome! Send a pull request or
-report any issues you find on the [issue tracker](http://issues.php-cache.com).
diff --git a/civicrm/vendor/cache/integration-tests/composer.json b/civicrm/vendor/cache/integration-tests/composer.json
deleted file mode 100755
index f242a6690d2539509a25a84be2078c7bc40f052b..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/composer.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-    "name":        "cache/integration-tests",
-    "type":        "library",
-    "description": "Integration tests for PSR-6 and PSR-16 cache implementations",
-    "keywords":    [
-        "cache",
-        "psr6",
-        "psr16",
-        "test"
-    ],
-    "homepage": "https://github.com/php-cache/integration-tests",
-    "license": "MIT",
-    "authors": [
-        {
-            "name":     "Aaron Scherer",
-            "email":    "aequasi@gmail.com",
-            "homepage": "https://github.com/aequasi"
-        },
-        {
-            "name":     "Tobias Nyholm",
-            "email":    "tobias.nyholm@gmail.com",
-            "homepage": "https://github.com/nyholm"
-        }
-    ],
-    "require":     {
-        "php": ">=5.5.9",
-        "psr/cache": "~1.0",
-        "cache/tag-interop": "^1.0"
-    },
-    "require-dev": {
-        "cache/cache": "^1.0",
-        "symfony/cache": "^3.4.31|^4.3.4|^5.0",
-        "symfony/phpunit-bridge": "^5.1",
-        "illuminate/cache": "^5.4|^5.5|^5.6",
-        "tedivm/stash": "^0.14",
-        "mockery/mockery": "^1.0"
-    },
-    "conflict": {
-        "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
-    },
-    "autoload":    {
-        "psr-4": {
-            "Cache\\IntegrationTests\\": "src/"
-        }
-    },
-    "minimum-stability": "dev",
-    "prefer-stable": true
-}
diff --git a/civicrm/vendor/cache/integration-tests/phpunit.xml.dist b/civicrm/vendor/cache/integration-tests/phpunit.xml.dist
deleted file mode 100644
index d2c2c2abcbd52e54328429f5f2ea058e5ad75c50..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/phpunit.xml.dist
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
-  backupGlobals="false"
-  backupStaticAttributes="false"
-  colors="true"
-  convertErrorsToExceptions="true"
-  convertNoticesToExceptions="true"
-  convertWarningsToExceptions="true"
-  processIsolation="false"
-  stopOnFailure="false"
-  syntaxCheck="false"
-  bootstrap="vendor/autoload.php"
-  >
-  <testsuites>
-    <testsuite name="PHPCache">
-      <directory>./vendor/cache/cache/src/Adapter/Redis/Tests/</directory>
-      <directory>./vendor/cache/cache/src/Bridge/SimpleCache/Tests/</directory>
-    </testsuite>
-
-    <testsuite name="Laravel">
-      <directory>./vendor/cache/cache/src/Adapter/Illuminate/Tests/</directory>
-    </testsuite>
-
-    <testsuite name="Symfony">
-      <file>./vendor/symfony/cache/Tests/Adapter/FilesystemAdapterTest.php</file>
-    </testsuite>
-
-    <testsuite name="Stash">
-      <file>./tests/StashTest.php</file>
-    </testsuite>
-  </testsuites>
-
-  <filter>
-    <whitelist>
-      <directory>./</directory>
-      <exclude>
-        <directory>./Tests</directory>
-        <directory>./vendor</directory>
-      </exclude>
-    </whitelist>
-  </filter>
-
-  <listeners>
-    <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener">
-      <arguments>
-        <array>
-          <element key="time-sensitive">
-            <array>
-              <element key="0"><string>Cache\IntegrationTests</string></element>
-              <element key="2"><string>Symfony\Component\Cache</string></element>
-              <element key="3"><string>Symfony\Component\Cache\Tests\Fixtures</string></element>
-              <element key="4"><string>Symfony\Component\Cache\Tests\Traits</string></element>
-              <element key="5"><string>Symfony\Component\Cache\Traits</string></element>
-            </array>
-          </element>
-        </array>
-      </arguments>
-    </listener>
-  </listeners>
-
-</phpunit>
diff --git a/civicrm/vendor/cache/integration-tests/src/CachePoolTest.php b/civicrm/vendor/cache/integration-tests/src/CachePoolTest.php
deleted file mode 100644
index e5cb3e0a86fb8a67629f28d0b64092fad4794500..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/src/CachePoolTest.php
+++ /dev/null
@@ -1,874 +0,0 @@
-<?php
-
-/*
- * This file is part of php-cache organization.
- *
- * (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
-
-namespace Cache\IntegrationTests;
-
-use PHPUnit\Framework\TestCase;
-use Psr\Cache\CacheItemInterface;
-use Psr\Cache\CacheItemPoolInterface;
-
-abstract class CachePoolTest extends TestCase
-{
-    /**
-     * @type array with functionName => reason.
-     */
-    protected $skippedTests = [];
-
-    /**
-     * @type CacheItemPoolInterface
-     */
-    protected $cache;
-
-    /**
-     * @return CacheItemPoolInterface that is used in the tests
-     */
-    abstract public function createCachePool();
-
-    /**
-     * @before
-     */
-    public function setupService()
-    {
-        $this->cache = $this->createCachePool();
-    }
-
-    /**
-     * @after
-     */
-    public function tearDownService()
-    {
-        if ($this->cache !== null) {
-            $this->cache->clear();
-        }
-    }
-
-    /**
-     * Data provider for invalid keys.
-     *
-     * @return array
-     */
-    public static function invalidKeys()
-    {
-        return [
-            [true],
-            [false],
-            [null],
-            [2],
-            [2.5],
-            ['{str'],
-            ['rand{'],
-            ['rand{str'],
-            ['rand}str'],
-            ['rand(str'],
-            ['rand)str'],
-            ['rand/str'],
-            ['rand\\str'],
-            ['rand@str'],
-            ['rand:str'],
-            [new \stdClass()],
-            [['array']],
-        ];
-    }
-
-    public function testBasicUsage()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('4711');
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key2');
-        $item->set('4712');
-        $this->cache->save($item);
-
-        $fooItem = $this->cache->getItem('key');
-        $this->assertTrue($fooItem->isHit());
-        $this->assertEquals('4711', $fooItem->get());
-
-        $barItem = $this->cache->getItem('key2');
-        $this->assertTrue($barItem->isHit());
-        $this->assertEquals('4712', $barItem->get());
-
-        // Remove 'key' and make sure 'key2' is still there
-        $this->cache->deleteItem('key');
-        $this->assertFalse($this->cache->getItem('key')->isHit());
-        $this->assertTrue($this->cache->getItem('key2')->isHit());
-
-        // Remove everything
-        $this->cache->clear();
-        $this->assertFalse($this->cache->getItem('key')->isHit());
-        $this->assertFalse($this->cache->getItem('key2')->isHit());
-    }
-
-    public function testBasicUsageWithLongKey()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $pool = $this->createCachePool();
-
-        $key = str_repeat('a', 300);
-
-        $item = $pool->getItem($key);
-        $this->assertFalse($item->isHit());
-        $this->assertSame($key, $item->getKey());
-
-        $item->set('value');
-        $this->assertTrue($pool->save($item));
-
-        $item = $pool->getItem($key);
-        $this->assertTrue($item->isHit());
-        $this->assertSame($key, $item->getKey());
-        $this->assertSame('value', $item->get());
-
-        $this->assertTrue($pool->deleteItem($key));
-
-        $item = $pool->getItem($key);
-        $this->assertFalse($item->isHit());
-    }
-
-    public function testItemModifiersReturnsStatic()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $this->assertSame($item, $item->set('4711'));
-        $this->assertSame($item, $item->expiresAfter(2));
-        $this->assertSame($item, $item->expiresAt(new \DateTime('+2hours')));
-    }
-
-    public function testGetItem()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->save($item);
-
-        // get existing item
-        $item = $this->cache->getItem('key');
-        $this->assertEquals('value', $item->get(), 'A stored item must be returned from cached.');
-        $this->assertEquals('key', $item->getKey(), 'Cache key can not change.');
-
-        // get non-existent item
-        $item = $this->cache->getItem('key2');
-        $this->assertFalse($item->isHit());
-        $this->assertNull($item->get(), "Item's value must be null when isHit is false.");
-    }
-
-    public function testGetItems()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $keys  = ['foo', 'bar', 'baz'];
-        $items = $this->cache->getItems($keys);
-
-        $count = 0;
-
-        /** @type CacheItemInterface $item */
-        foreach ($items as $i => $item) {
-            $item->set($i);
-            $this->cache->save($item);
-
-            $count++;
-        }
-
-        $this->assertSame(3, $count);
-
-        $keys[] = 'biz';
-        /** @type CacheItemInterface[] $items */
-        $items = $this->cache->getItems($keys);
-        $count = 0;
-        foreach ($items as $key => $item) {
-            $itemKey = $item->getKey();
-            $this->assertEquals($itemKey, $key, 'Keys must be preserved when fetching multiple items');
-            $this->assertEquals($key !== 'biz', $item->isHit());
-            $this->assertTrue(in_array($key, $keys), 'Cache key can not change.');
-
-            // Remove $key for $keys
-            foreach ($keys as $k => $v) {
-                if ($v === $key) {
-                    unset($keys[$k]);
-                }
-            }
-
-            $count++;
-        }
-
-        $this->assertSame(4, $count);
-    }
-
-    public function testGetItemsEmpty()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $items = $this->cache->getItems([]);
-        $this->assertTrue(
-            is_array($items) || $items instanceof \Traversable,
-            'A call to getItems with an empty array must always return an array or \Traversable.'
-        );
-
-        $count = 0;
-        foreach ($items as $item) {
-            $count++;
-        }
-
-        $this->assertSame(0, $count);
-    }
-
-    public function testHasItem()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->save($item);
-
-        // has existing item
-        $this->assertTrue($this->cache->hasItem('key'));
-
-        // has non-existent item
-        $this->assertFalse($this->cache->hasItem('key2'));
-    }
-
-    public function testClear()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->save($item);
-
-        $return = $this->cache->clear();
-
-        $this->assertTrue($return, 'clear() must return true if cache was cleared. ');
-        $this->assertFalse($this->cache->getItem('key')->isHit(), 'No item should be a hit after the cache is cleared. ');
-        $this->assertFalse($this->cache->hasItem('key2'), 'The cache pool should be empty after it is cleared.');
-    }
-
-    public function testClearWithDeferredItems()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->saveDeferred($item);
-
-        $this->cache->clear();
-        $this->cache->commit();
-
-        $this->assertFalse($this->cache->getItem('key')->isHit(), 'Deferred items must be cleared on clear(). ');
-    }
-
-    public function testDeleteItem()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->save($item);
-
-        $this->assertTrue($this->cache->deleteItem('key'));
-        $this->assertFalse($this->cache->getItem('key')->isHit(), 'A deleted item should not be a hit.');
-        $this->assertFalse($this->cache->hasItem('key'), 'A deleted item should not be a in cache.');
-
-        $this->assertTrue($this->cache->deleteItem('key2'), 'Deleting an item that does not exist should return true.');
-    }
-
-    public function testDeleteItems()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $items = $this->cache->getItems(['foo', 'bar', 'baz']);
-
-        /** @type CacheItemInterface $item */
-        foreach ($items as $idx => $item) {
-            $item->set($idx);
-            $this->cache->save($item);
-        }
-
-        // All should be a hit but 'biz'
-        $this->assertTrue($this->cache->getItem('foo')->isHit());
-        $this->assertTrue($this->cache->getItem('bar')->isHit());
-        $this->assertTrue($this->cache->getItem('baz')->isHit());
-        $this->assertFalse($this->cache->getItem('biz')->isHit());
-
-        $return = $this->cache->deleteItems(['foo', 'bar', 'biz']);
-        $this->assertTrue($return);
-
-        $this->assertFalse($this->cache->getItem('foo')->isHit());
-        $this->assertFalse($this->cache->getItem('bar')->isHit());
-        $this->assertTrue($this->cache->getItem('baz')->isHit());
-        $this->assertFalse($this->cache->getItem('biz')->isHit());
-    }
-
-    public function testSave()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $return = $this->cache->save($item);
-
-        $this->assertTrue($return, 'save() should return true when items are saved.');
-        $this->assertEquals('value', $this->cache->getItem('key')->get());
-    }
-
-    public function testSaveExpired()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $item->expiresAt(\DateTime::createFromFormat('U', time() + 10));
-        $this->cache->save($item);
-        $item->expiresAt(\DateTime::createFromFormat('U', time() - 1));
-        $this->cache->save($item);
-        $item = $this->cache->getItem('key');
-        $this->assertFalse($item->isHit(), 'Cache should not save expired items');
-    }
-
-    public function testSaveWithoutExpire()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('test_ttl_null');
-        $item->set('data');
-        $this->cache->save($item);
-
-        // Use a new pool instance to ensure that we don't hit any caches
-        $pool = $this->createCachePool();
-        $item = $pool->getItem('test_ttl_null');
-
-        $this->assertTrue($item->isHit(), 'Cache should have retrieved the items');
-        $this->assertEquals('data', $item->get());
-    }
-
-    public function testDeferredSave()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('4711');
-        $return = $this->cache->saveDeferred($item);
-        $this->assertTrue($return, 'save() should return true when items are saved.');
-
-        $item = $this->cache->getItem('key2');
-        $item->set('4712');
-        $this->cache->saveDeferred($item);
-
-        // They are not saved yet but should be a hit
-        $this->assertTrue($this->cache->hasItem('key'), 'Deferred items should be considered as a part of the cache even before they are committed');
-        $this->assertTrue($this->cache->getItem('key')->isHit(), 'Deferred items should be a hit even before they are committed');
-        $this->assertTrue($this->cache->getItem('key2')->isHit());
-
-        $this->cache->commit();
-
-        // They should be a hit after the commit as well
-        $this->assertTrue($this->cache->getItem('key')->isHit());
-        $this->assertTrue($this->cache->getItem('key2')->isHit());
-    }
-
-    public function testDeferredExpired()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('4711');
-        $item->expiresAt(\DateTime::createFromFormat('U', time() - 1));
-        $this->cache->saveDeferred($item);
-
-        $this->assertFalse($this->cache->hasItem('key'), 'Cache should not have expired deferred item');
-        $this->cache->commit();
-        $item = $this->cache->getItem('key');
-        $this->assertFalse($item->isHit(), 'Cache should not save expired items');
-    }
-
-    public function testDeleteDeferredItem()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('4711');
-        $this->cache->saveDeferred($item);
-        $this->assertTrue($this->cache->getItem('key')->isHit());
-
-        $this->cache->deleteItem('key');
-        $this->assertFalse($this->cache->hasItem('key'), 'You must be able to delete a deferred item before committed. ');
-        $this->assertFalse($this->cache->getItem('key')->isHit(), 'You must be able to delete a deferred item before committed. ');
-
-        $this->cache->commit();
-        $this->assertFalse($this->cache->hasItem('key'), 'A deleted item should not reappear after commit. ');
-        $this->assertFalse($this->cache->getItem('key')->isHit(), 'A deleted item should not reappear after commit. ');
-    }
-
-    public function testDeferredSaveWithoutCommit()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->prepareDeferredSaveWithoutCommit();
-        gc_collect_cycles();
-
-        $cache = $this->createCachePool();
-        $this->assertTrue($cache->getItem('key')->isHit(), 'A deferred item should automatically be committed on CachePool::__destruct().');
-    }
-
-    private function prepareDeferredSaveWithoutCommit()
-    {
-        $cache       = $this->cache;
-        $this->cache = null;
-
-        $item = $cache->getItem('key');
-        $item->set('4711');
-        $cache->saveDeferred($item);
-    }
-
-    public function testCommit()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->saveDeferred($item);
-        $return = $this->cache->commit();
-
-        $this->assertTrue($return, 'commit() should return true on successful commit. ');
-        $this->assertEquals('value', $this->cache->getItem('key')->get());
-
-        $return = $this->cache->commit();
-        $this->assertTrue($return, 'commit() should return true even if no items were deferred. ');
-    }
-
-    /**
-     * @medium
-     */
-    public function testExpiration()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $item->expiresAfter(2);
-        $this->cache->save($item);
-
-        sleep(3);
-        $item = $this->cache->getItem('key');
-        $this->assertFalse($item->isHit());
-        $this->assertNull($item->get(), "Item's value must be null when isHit() is false.");
-    }
-
-    public function testExpiresAt()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $item->expiresAt(new \DateTime('+2hours'));
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue($item->isHit());
-    }
-
-    public function testExpiresAtWithNull()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $item->expiresAt(null);
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue($item->isHit());
-    }
-
-    public function testExpiresAfterWithNull()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $item->expiresAfter(null);
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue($item->isHit());
-    }
-
-    public function testKeyLength()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $key  = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.';
-        $item = $this->cache->getItem($key);
-        $item->set('value');
-        $this->assertTrue($this->cache->save($item), 'The implementation does not support a valid cache key');
-
-        $this->assertTrue($this->cache->hasItem($key));
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testGetItemInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\Cache\InvalidArgumentException');
-        $this->cache->getItem($key);
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testGetItemsInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\Cache\InvalidArgumentException');
-        $this->cache->getItems(['key1', $key, 'key2']);
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testHasItemInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\Cache\InvalidArgumentException');
-        $this->cache->hasItem($key);
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testDeleteItemInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\Cache\InvalidArgumentException');
-        $this->cache->deleteItem($key);
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testDeleteItemsInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\Cache\InvalidArgumentException');
-        $this->cache->deleteItems(['key1', $key, 'key2']);
-    }
-
-    public function testDataTypeString()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('5');
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue('5' === $item->get(), 'Wrong data type. If we store a string we must get an string back.');
-        $this->assertTrue(is_string($item->get()), 'Wrong data type. If we store a string we must get an string back.');
-    }
-
-    public function testDataTypeInteger()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set(5);
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue(5 === $item->get(), 'Wrong data type. If we store an int we must get an int back.');
-        $this->assertTrue(is_int($item->get()), 'Wrong data type. If we store an int we must get an int back.');
-    }
-
-    public function testDataTypeNull()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set(null);
-        $this->cache->save($item);
-
-        $this->assertTrue($this->cache->hasItem('key'), 'Null is a perfectly fine cache value. hasItem() should return true when null are stored. ');
-        $item = $this->cache->getItem('key');
-        $this->assertTrue(null === $item->get(), 'Wrong data type. If we store null we must get an null back.');
-        $this->assertTrue(is_null($item->get()), 'Wrong data type. If we store null we must get an null back.');
-        $this->assertTrue($item->isHit(), 'isHit() should return true when null are stored. ');
-    }
-
-    public function testDataTypeFloat()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $float = 1.23456789;
-        $item  = $this->cache->getItem('key');
-        $item->set($float);
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue(is_float($item->get()), 'Wrong data type. If we store float we must get an float back.');
-        $this->assertEquals($float, $item->get());
-        $this->assertTrue($item->isHit(), 'isHit() should return true when float are stored. ');
-    }
-
-    public function testDataTypeBoolean()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set(true);
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue(is_bool($item->get()), 'Wrong data type. If we store boolean we must get an boolean back.');
-        $this->assertTrue($item->get());
-        $this->assertTrue($item->isHit(), 'isHit() should return true when true are stored. ');
-    }
-
-    public function testDataTypeArray()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $array = ['a' => 'foo', 2 => 'bar'];
-        $item  = $this->cache->getItem('key');
-        $item->set($array);
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue(is_array($item->get()), 'Wrong data type. If we store array we must get an array back.');
-        $this->assertEquals($array, $item->get());
-        $this->assertTrue($item->isHit(), 'isHit() should return true when array are stored. ');
-    }
-
-    public function testDataTypeObject()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $object    = new \stdClass();
-        $object->a = 'foo';
-        $item      = $this->cache->getItem('key');
-        $item->set($object);
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue(is_object($item->get()), 'Wrong data type. If we store object we must get an object back.');
-        $this->assertEquals($object, $item->get());
-        $this->assertTrue($item->isHit(), 'isHit() should return true when object are stored. ');
-    }
-
-    public function testBinaryData()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $data = '';
-        for ($i = 0; $i < 256; $i++) {
-            $data .= chr($i);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set($data);
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue($data === $item->get(), 'Binary data must survive a round trip.');
-    }
-
-    public function testIsHit()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertTrue($item->isHit());
-    }
-
-    public function testIsHitDeferred()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->saveDeferred($item);
-
-        // Test accessing the value before it is committed
-        $item = $this->cache->getItem('key');
-        $this->assertTrue($item->isHit());
-
-        $this->cache->commit();
-        $item = $this->cache->getItem('key');
-        $this->assertTrue($item->isHit());
-    }
-
-    public function testSaveDeferredWhenChangingValues()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->saveDeferred($item);
-
-        $item = $this->cache->getItem('key');
-        $item->set('new value');
-
-        $item = $this->cache->getItem('key');
-        $this->assertEquals('value', $item->get(), 'Items that is put in the deferred queue should not get their values changed');
-
-        $this->cache->commit();
-        $item = $this->cache->getItem('key');
-        $this->assertEquals('value', $item->get(), 'Items that is put in the deferred queue should not get their values changed');
-    }
-
-    public function testSaveDeferredOverwrite()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $this->cache->saveDeferred($item);
-
-        $item = $this->cache->getItem('key');
-        $item->set('new value');
-        $this->cache->saveDeferred($item);
-
-        $item = $this->cache->getItem('key');
-        $this->assertEquals('new value', $item->get());
-
-        $this->cache->commit();
-        $item = $this->cache->getItem('key');
-        $this->assertEquals('new value', $item->get());
-    }
-
-    public function testSavingObject()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set(new \DateTime());
-        $this->cache->save($item);
-
-        $item  = $this->cache->getItem('key');
-        $value = $item->get();
-        $this->assertInstanceOf('DateTime', $value, 'You must be able to store objects in cache.');
-    }
-
-    /**
-     * @medium
-     */
-    public function testHasItemReturnsFalseWhenDeferredItemIsExpired()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key');
-        $item->set('value');
-        $item->expiresAfter(2);
-        $this->cache->saveDeferred($item);
-
-        sleep(3);
-        $this->assertFalse($this->cache->hasItem('key'));
-    }
-}
diff --git a/civicrm/vendor/cache/integration-tests/src/HierarchicalCachePoolTest.php b/civicrm/vendor/cache/integration-tests/src/HierarchicalCachePoolTest.php
deleted file mode 100644
index 7413d1ea1825dd4266c04f7d5c06eefdbe134e53..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/src/HierarchicalCachePoolTest.php
+++ /dev/null
@@ -1,150 +0,0 @@
-<?php
-
-/*
- * This file is part of php-cache organization.
- *
- * (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
-
-namespace Cache\IntegrationTests;
-
-use PHPUnit\Framework\TestCase;
-use Psr\Cache\CacheItemPoolInterface;
-
-/**
- * @author Tobias Nyholm <tobias.nyholm@gmail.com>
- */
-abstract class HierarchicalCachePoolTest extends TestCase
-{
-    /**
-     * @type array with functionName => reason.
-     */
-    protected $skippedTests = [];
-
-    /**
-     * @type CacheItemPoolInterface
-     */
-    protected $cache;
-
-    /**
-     * @return CacheItemPoolInterface that is used in the tests
-     */
-    abstract public function createCachePool();
-
-    /**
-     * @before
-     */
-    public function setupService()
-    {
-        $this->cache = $this->createCachePool();
-    }
-
-    /**
-     * @after
-     */
-    public function tearDownService()
-    {
-        if ($this->cache !== null) {
-            $this->cache->clear();
-        }
-    }
-
-    public function testBasicUsage()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $user = 4711;
-        for ($i = 0; $i < 10; $i++) {
-            $item = $this->cache->getItem(sprintf('|users|%d|followers|%d|likes', $user, $i));
-            $item->set('Justin Bieber');
-            $this->cache->save($item);
-        }
-
-        $this->assertTrue($this->cache->hasItem('|users|4711|followers|4|likes'));
-        $this->cache->deleteItem('|users|4711|followers');
-        $this->assertFalse($this->cache->hasItem('|users|4711|followers|4|likes'));
-    }
-
-    public function testChain()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('|aaa|bbb|ccc|ddd');
-        $item->set('value');
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('|aaa|bbb|ccc|xxx');
-        $item->set('value');
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('|aaa|bbb|zzz|ddd');
-        $item->set('value');
-        $this->cache->save($item);
-
-        $this->assertTrue($this->cache->hasItem('|aaa|bbb|ccc|ddd'));
-        $this->assertTrue($this->cache->hasItem('|aaa|bbb|ccc|xxx'));
-        $this->assertTrue($this->cache->hasItem('|aaa|bbb|zzz|ddd'));
-        $this->assertFalse($this->cache->hasItem('|aaa|bbb|ccc'));
-        $this->assertFalse($this->cache->hasItem('|aaa|bbb|zzz'));
-        $this->assertFalse($this->cache->hasItem('|aaa|bbb'));
-        $this->assertFalse($this->cache->hasItem('|aaa'));
-        $this->assertFalse($this->cache->hasItem('|'));
-
-        // This is a different thing
-        $this->cache->deleteItem('|aaa|bbb|cc');
-        $this->assertTrue($this->cache->hasItem('|aaa|bbb|ccc|ddd'));
-        $this->assertTrue($this->cache->hasItem('|aaa|bbb|ccc|xxx'));
-        $this->assertTrue($this->cache->hasItem('|aaa|bbb|zzz|ddd'));
-
-        $this->cache->deleteItem('|aaa|bbb|ccc');
-        $this->assertFalse($this->cache->hasItem('|aaa|bbb|ccc|ddd'));
-        $this->assertFalse($this->cache->hasItem('|aaa|bbb|ccc|xxx'));
-        $this->assertTrue($this->cache->hasItem('|aaa|bbb|zzz|ddd'));
-
-        $this->cache->deleteItem('|aaa');
-        $this->assertFalse($this->cache->hasItem('|aaa|bbb|zzz|ddd'));
-    }
-
-    public function testRemoval()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('foo');
-        $item->set('value');
-        $this->cache->save($item);
-
-        $item = $this->cache->getItem('|aaa|bbb');
-        $item->set('value');
-        $this->cache->save($item);
-
-        $this->cache->deleteItem('|');
-        $this->assertFalse($this->cache->hasItem('|aaa|bbb'), 'Hierarchy items should be removed when deleting root');
-        $this->assertTrue($this->cache->hasItem('foo'), 'All cache should not be cleared when deleting root');
-    }
-
-    public function testRemovalWhenDeferred()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('|aaa|bbb');
-        $item->set('value');
-        $this->cache->saveDeferred($item);
-
-        $this->cache->deleteItem('|');
-        $this->assertFalse($this->cache->hasItem('|aaa|bbb'), 'Deferred hierarchy items should be removed');
-
-        $this->cache->commit();
-        $this->assertFalse($this->cache->hasItem('|aaa|bbb'), 'Deferred hierarchy items should be removed');
-    }
-}
diff --git a/civicrm/vendor/cache/integration-tests/src/SimpleCacheTest.php b/civicrm/vendor/cache/integration-tests/src/SimpleCacheTest.php
deleted file mode 100644
index 1d3216e12c3f379e7f511f33bf6377d3b8adcc5b..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/src/SimpleCacheTest.php
+++ /dev/null
@@ -1,789 +0,0 @@
-<?php
-
-/*
- * This file is part of php-cache organization.
- *
- * (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
-
-namespace Cache\IntegrationTests;
-
-use PHPUnit\Framework\TestCase;
-use Psr\SimpleCache\CacheInterface;
-
-abstract class SimpleCacheTest extends TestCase
-{
-    /**
-     * @type array with functionName => reason.
-     */
-    protected $skippedTests = [];
-
-    /**
-     * @type CacheInterface
-     */
-    protected $cache;
-
-    /**
-     * @return CacheInterface that is used in the tests
-     */
-    abstract public function createSimpleCache();
-
-    /**
-     * Advance time perceived by the cache for the purposes of testing TTL.
-     *
-     * The default implementation sleeps for the specified duration,
-     * but subclasses are encouraged to override this,
-     * adjusting a mocked time possibly set up in {@link createSimpleCache()},
-     * to speed up the tests.
-     *
-     * @param int $seconds
-     */
-    public function advanceTime($seconds)
-    {
-        sleep($seconds);
-    }
-
-    /**
-     * @before
-     */
-    public function setupService()
-    {
-        $this->cache = $this->createSimpleCache();
-    }
-
-    /**
-     * @after
-     */
-    public function tearDownService()
-    {
-        if ($this->cache !== null) {
-            $this->cache->clear();
-        }
-    }
-
-    /**
-     * Data provider for invalid cache keys.
-     *
-     * @return array
-     */
-    public static function invalidKeys()
-    {
-        return array_merge(
-            self::invalidArrayKeys(),
-            [
-                [2],
-            ]
-        );
-    }
-
-    /**
-     * Data provider for invalid array keys.
-     *
-     * @return array
-     */
-    public static function invalidArrayKeys()
-    {
-        return [
-            [''],
-            [true],
-            [false],
-            [null],
-            [2.5],
-            ['{str'],
-            ['rand{'],
-            ['rand{str'],
-            ['rand}str'],
-            ['rand(str'],
-            ['rand)str'],
-            ['rand/str'],
-            ['rand\\str'],
-            ['rand@str'],
-            ['rand:str'],
-            [new \stdClass()],
-            [['array']],
-        ];
-    }
-
-    /**
-     * @return array
-     */
-    public static function invalidTtl()
-    {
-        return [
-            [''],
-            [true],
-            [false],
-            ['abc'],
-            [2.5],
-            [' 1'], // can be casted to a int
-            ['12foo'], // can be casted to a int
-            ['025'], // can be interpreted as hex
-            [new \stdClass()],
-            [['array']],
-        ];
-    }
-
-    /**
-     * Data provider for valid keys.
-     *
-     * @return array
-     */
-    public static function validKeys()
-    {
-        return [
-            ['AbC19_.'],
-            ['1234567890123456789012345678901234567890123456789012345678901234'],
-        ];
-    }
-
-    /**
-     * Data provider for valid data to store.
-     *
-     * @return array
-     */
-    public static function validData()
-    {
-        return [
-            ['AbC19_.'],
-            [4711],
-            [47.11],
-            [true],
-            [null],
-            [['key' => 'value']],
-            [new \stdClass()],
-        ];
-    }
-
-    public function testSet()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $result = $this->cache->set('key', 'value');
-        $this->assertTrue($result, 'set() must return true if success');
-        $this->assertEquals('value', $this->cache->get('key'));
-    }
-
-    /**
-     * @medium
-     */
-    public function testSetTtl()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $result = $this->cache->set('key1', 'value', 2);
-        $this->assertTrue($result, 'set() must return true if success');
-        $this->assertEquals('value', $this->cache->get('key1'));
-
-        $this->cache->set('key2', 'value', new \DateInterval('PT2S'));
-        $this->assertEquals('value', $this->cache->get('key2'));
-
-        $this->advanceTime(3);
-
-        $this->assertNull($this->cache->get('key1'), 'Value must expire after ttl.');
-        $this->assertNull($this->cache->get('key2'), 'Value must expire after ttl.');
-    }
-
-    public function testSetExpiredTtl()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->set('key0', 'value');
-        $this->cache->set('key0', 'value', 0);
-        $this->assertNull($this->cache->get('key0'));
-        $this->assertFalse($this->cache->has('key0'));
-
-        $this->cache->set('key1', 'value', -1);
-        $this->assertNull($this->cache->get('key1'));
-        $this->assertFalse($this->cache->has('key1'));
-    }
-
-    public function testGet()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->assertNull($this->cache->get('key'));
-        $this->assertEquals('foo', $this->cache->get('key', 'foo'));
-
-        $this->cache->set('key', 'value');
-        $this->assertEquals('value', $this->cache->get('key', 'foo'));
-    }
-
-    public function testDelete()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->assertTrue($this->cache->delete('key'), 'Deleting a value that does not exist should return true');
-        $this->cache->set('key', 'value');
-        $this->assertTrue($this->cache->delete('key'), 'Delete must return true on success');
-        $this->assertNull($this->cache->get('key'), 'Values must be deleted on delete()');
-    }
-
-    public function testClear()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->assertTrue($this->cache->clear(), 'Clearing an empty cache should return true');
-        $this->cache->set('key', 'value');
-        $this->assertTrue($this->cache->clear(), 'Delete must return true on success');
-        $this->assertNull($this->cache->get('key'), 'Values must be deleted on clear()');
-    }
-
-    public function testSetMultiple()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $result = $this->cache->setMultiple(['key0' => 'value0', 'key1' => 'value1']);
-        $this->assertTrue($result, 'setMultiple() must return true if success');
-        $this->assertEquals('value0', $this->cache->get('key0'));
-        $this->assertEquals('value1', $this->cache->get('key1'));
-    }
-
-    public function testSetMultipleWithIntegerArrayKey()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $result = $this->cache->setMultiple(['0' => 'value0']);
-        $this->assertTrue($result, 'setMultiple() must return true if success');
-        $this->assertEquals('value0', $this->cache->get('0'));
-    }
-
-    /**
-     * @medium
-     */
-    public function testSetMultipleTtl()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->setMultiple(['key2' => 'value2', 'key3' => 'value3'], 2);
-        $this->assertEquals('value2', $this->cache->get('key2'));
-        $this->assertEquals('value3', $this->cache->get('key3'));
-
-        $this->cache->setMultiple(['key4' => 'value4'], new \DateInterval('PT2S'));
-        $this->assertEquals('value4', $this->cache->get('key4'));
-
-        $this->advanceTime(3);
-        $this->assertNull($this->cache->get('key2'), 'Value must expire after ttl.');
-        $this->assertNull($this->cache->get('key3'), 'Value must expire after ttl.');
-        $this->assertNull($this->cache->get('key4'), 'Value must expire after ttl.');
-    }
-
-    public function testSetMultipleExpiredTtl()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->setMultiple(['key0' => 'value0', 'key1' => 'value1'], 0);
-        $this->assertNull($this->cache->get('key0'));
-        $this->assertNull($this->cache->get('key1'));
-    }
-
-    public function testSetMultipleWithGenerator()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $gen = function () {
-            yield 'key0' => 'value0';
-            yield 'key1' => 'value1';
-        };
-
-        $this->cache->setMultiple($gen());
-        $this->assertEquals('value0', $this->cache->get('key0'));
-        $this->assertEquals('value1', $this->cache->get('key1'));
-    }
-
-    public function testGetMultiple()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $result = $this->cache->getMultiple(['key0', 'key1']);
-        $keys   = [];
-        foreach ($result as $i => $r) {
-            $keys[] = $i;
-            $this->assertNull($r);
-        }
-        sort($keys);
-        $this->assertSame(['key0', 'key1'], $keys);
-
-        $this->cache->set('key3', 'value');
-        $result = $this->cache->getMultiple(['key2', 'key3', 'key4'], 'foo');
-        $keys   = [];
-        foreach ($result as $key => $r) {
-            $keys[] = $key;
-            if ($key === 'key3') {
-                $this->assertEquals('value', $r);
-            } else {
-                $this->assertEquals('foo', $r);
-            }
-        }
-        sort($keys);
-        $this->assertSame(['key2', 'key3', 'key4'], $keys);
-    }
-
-    public function testGetMultipleWithGenerator()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $gen = function () {
-            yield 1 => 'key0';
-            yield 1 => 'key1';
-        };
-
-        $this->cache->set('key0', 'value0');
-        $result = $this->cache->getMultiple($gen());
-        $keys   = [];
-        foreach ($result as $key => $r) {
-            $keys[] = $key;
-            if ($key === 'key0') {
-                $this->assertEquals('value0', $r);
-            } elseif ($key === 'key1') {
-                $this->assertNull($r);
-            } else {
-                $this->assertFalse(true, 'This should not happend');
-            }
-        }
-        sort($keys);
-        $this->assertSame(['key0', 'key1'], $keys);
-        $this->assertEquals('value0', $this->cache->get('key0'));
-        $this->assertNull($this->cache->get('key1'));
-    }
-
-    public function testDeleteMultiple()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->assertTrue($this->cache->deleteMultiple([]), 'Deleting a empty array should return true');
-        $this->assertTrue($this->cache->deleteMultiple(['key']), 'Deleting a value that does not exist should return true');
-
-        $this->cache->set('key0', 'value0');
-        $this->cache->set('key1', 'value1');
-        $this->assertTrue($this->cache->deleteMultiple(['key0', 'key1']), 'Delete must return true on success');
-        $this->assertNull($this->cache->get('key0'), 'Values must be deleted on deleteMultiple()');
-        $this->assertNull($this->cache->get('key1'), 'Values must be deleted on deleteMultiple()');
-    }
-
-    public function testDeleteMultipleGenerator()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $gen = function () {
-            yield 1 => 'key0';
-            yield 1 => 'key1';
-        };
-        $this->cache->set('key0', 'value0');
-        $this->assertTrue($this->cache->deleteMultiple($gen()), 'Deleting a generator should return true');
-
-        $this->assertNull($this->cache->get('key0'), 'Values must be deleted on deleteMultiple()');
-        $this->assertNull($this->cache->get('key1'), 'Values must be deleted on deleteMultiple()');
-    }
-
-    public function testHas()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->assertFalse($this->cache->has('key0'));
-        $this->cache->set('key0', 'value0');
-        $this->assertTrue($this->cache->has('key0'));
-    }
-
-    public function testBasicUsageWithLongKey()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $key = str_repeat('a', 300);
-
-        $this->assertFalse($this->cache->has($key));
-        $this->assertTrue($this->cache->set($key, 'value'));
-
-        $this->assertTrue($this->cache->has($key));
-        $this->assertSame('value', $this->cache->get($key));
-
-        $this->assertTrue($this->cache->delete($key));
-
-        $this->assertFalse($this->cache->has($key));
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testGetInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->get($key);
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testGetMultipleInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $result = $this->cache->getMultiple(['key1', $key, 'key2']);
-    }
-
-    public function testGetMultipleNoIterable()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $result = $this->cache->getMultiple('key');
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testSetInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->set($key, 'foobar');
-    }
-
-    /**
-     * @dataProvider invalidArrayKeys
-     */
-    public function testSetMultipleInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $values = function () use ($key) {
-            yield 'key1' => 'foo';
-            yield $key => 'bar';
-            yield 'key2' => 'baz';
-        };
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->setMultiple($values());
-    }
-
-    public function testSetMultipleNoIterable()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->setMultiple('key');
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testHasInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->has($key);
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testDeleteInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->delete($key);
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testDeleteMultipleInvalidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->deleteMultiple(['key1', $key, 'key2']);
-    }
-
-    public function testDeleteMultipleNoIterable()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->deleteMultiple('key');
-    }
-
-    /**
-     * @dataProvider invalidTtl
-     */
-    public function testSetInvalidTtl($ttl)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->set('key', 'value', $ttl);
-    }
-
-    /**
-     * @dataProvider invalidTtl
-     */
-    public function testSetMultipleInvalidTtl($ttl)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->expectException('Psr\SimpleCache\InvalidArgumentException');
-        $this->cache->setMultiple(['key' => 'value'], $ttl);
-    }
-
-    public function testNullOverwrite()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->set('key', 5);
-        $this->cache->set('key', null);
-
-        $this->assertNull($this->cache->get('key'), 'Setting null to a key must overwrite previous value');
-    }
-
-    public function testDataTypeString()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->set('key', '5');
-        $result = $this->cache->get('key');
-        $this->assertTrue('5' === $result, 'Wrong data type. If we store a string we must get an string back.');
-        $this->assertTrue(is_string($result), 'Wrong data type. If we store a string we must get an string back.');
-    }
-
-    public function testDataTypeInteger()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->set('key', 5);
-        $result = $this->cache->get('key');
-        $this->assertTrue(5 === $result, 'Wrong data type. If we store an int we must get an int back.');
-        $this->assertTrue(is_int($result), 'Wrong data type. If we store an int we must get an int back.');
-    }
-
-    public function testDataTypeFloat()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $float = 1.23456789;
-        $this->cache->set('key', $float);
-        $result = $this->cache->get('key');
-        $this->assertTrue(is_float($result), 'Wrong data type. If we store float we must get an float back.');
-        $this->assertEquals($float, $result);
-    }
-
-    public function testDataTypeBoolean()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->set('key', false);
-        $result = $this->cache->get('key');
-        $this->assertTrue(is_bool($result), 'Wrong data type. If we store boolean we must get an boolean back.');
-        $this->assertFalse($result);
-        $this->assertTrue($this->cache->has('key'), 'has() should return true when true are stored. ');
-    }
-
-    public function testDataTypeArray()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $array = ['a' => 'foo', 2 => 'bar'];
-        $this->cache->set('key', $array);
-        $result = $this->cache->get('key');
-        $this->assertTrue(is_array($result), 'Wrong data type. If we store array we must get an array back.');
-        $this->assertEquals($array, $result);
-    }
-
-    public function testDataTypeObject()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $object    = new \stdClass();
-        $object->a = 'foo';
-        $this->cache->set('key', $object);
-        $result = $this->cache->get('key');
-        $this->assertTrue(is_object($result), 'Wrong data type. If we store object we must get an object back.');
-        $this->assertEquals($object, $result);
-    }
-
-    public function testBinaryData()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $data = '';
-        for ($i = 0; $i < 256; $i++) {
-            $data .= chr($i);
-        }
-
-        $array = ['a' => 'foo', 2 => 'bar'];
-        $this->cache->set('key', $data);
-        $result = $this->cache->get('key');
-        $this->assertTrue($data === $result, 'Binary data must survive a round trip.');
-    }
-
-    /**
-     * @dataProvider validKeys
-     */
-    public function testSetValidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->set($key, 'foobar');
-        $this->assertEquals('foobar', $this->cache->get($key));
-    }
-
-    /**
-     * @dataProvider validKeys
-     */
-    public function testSetMultipleValidKeys($key)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->setMultiple([$key => 'foobar']);
-        $result = $this->cache->getMultiple([$key]);
-        $keys   = [];
-        foreach ($result as $i => $r) {
-            $keys[] = $i;
-            $this->assertEquals($key, $i);
-            $this->assertEquals('foobar', $r);
-        }
-        $this->assertSame([$key], $keys);
-    }
-
-    /**
-     * @dataProvider validData
-     */
-    public function testSetValidData($data)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->set('key', $data);
-        $this->assertEquals($data, $this->cache->get('key'));
-    }
-
-    /**
-     * @dataProvider validData
-     */
-    public function testSetMultipleValidData($data)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->setMultiple(['key' => $data]);
-        $result = $this->cache->getMultiple(['key']);
-        $keys   = [];
-        foreach ($result as $i => $r) {
-            $keys[] = $i;
-            $this->assertEquals($data, $r);
-        }
-        $this->assertSame(['key'], $keys);
-    }
-
-    public function testObjectAsDefaultValue()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $obj      = new \stdClass();
-        $obj->foo = 'value';
-        $this->assertEquals($obj, $this->cache->get('key', $obj));
-    }
-
-    public function testObjectDoesNotChangeInCache()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $obj      = new \stdClass();
-        $obj->foo = 'value';
-        $this->cache->set('key', $obj);
-        $obj->foo = 'changed';
-
-        $cacheObject = $this->cache->get('key');
-        $this->assertEquals('value', $cacheObject->foo, 'Object in cache should not have their values changed.');
-    }
-}
diff --git a/civicrm/vendor/cache/integration-tests/src/TaggableCachePoolTest.php b/civicrm/vendor/cache/integration-tests/src/TaggableCachePoolTest.php
deleted file mode 100644
index 73ed745af6166fcd7004e84694c3c89207b6379f..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/integration-tests/src/TaggableCachePoolTest.php
+++ /dev/null
@@ -1,277 +0,0 @@
-<?php
-
-/*
- * This file is part of php-cache organization.
- *
- * (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
-
-namespace Cache\IntegrationTests;
-
-use Cache\TagInterop\TaggableCacheItemPoolInterface;
-use PHPUnit\Framework\TestCase;
-
-/**
- * @author Tobias Nyholm <tobias.nyholm@gmail.com>
- */
-abstract class TaggableCachePoolTest extends TestCase
-{
-    /**
-     * @type array with functionName => reason.
-     */
-    protected $skippedTests = [];
-
-    /**
-     * @type TaggableCacheItemPoolInterface
-     */
-    protected $cache;
-
-    /**
-     * @return TaggableCacheItemPoolInterface that is used in the tests
-     */
-    abstract public function createCachePool();
-
-    /**
-     * @before
-     */
-    public function setupService()
-    {
-        $this->cache = $this->createCachePool();
-    }
-
-    /**
-     * @after
-     */
-    public function tearDownService()
-    {
-        if ($this->cache !== null) {
-            $this->cache->clear();
-        }
-    }
-
-    public function invalidKeys()
-    {
-        return CachePoolTest::invalidKeys();
-    }
-
-    public function testMultipleTags()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $this->cache->save($this->cache->getItem('key1')->set('value')->setTags(['tag1', 'tag2']));
-        $this->cache->save($this->cache->getItem('key2')->set('value')->setTags(['tag1', 'tag3']));
-        $this->cache->save($this->cache->getItem('key3')->set('value')->setTags(['tag2', 'tag3']));
-        $this->cache->save($this->cache->getItem('key4')->set('value')->setTags(['tag4', 'tag3']));
-
-        $this->cache->invalidateTags(['tag1']);
-        $this->assertFalse($this->cache->hasItem('key1'));
-        $this->assertFalse($this->cache->hasItem('key2'));
-        $this->assertTrue($this->cache->hasItem('key3'));
-        $this->assertTrue($this->cache->hasItem('key4'));
-
-        $this->cache->invalidateTags(['tag2']);
-        $this->assertFalse($this->cache->hasItem('key1'));
-        $this->assertFalse($this->cache->hasItem('key2'));
-        $this->assertFalse($this->cache->hasItem('key3'));
-        $this->assertTrue($this->cache->hasItem('key4'));
-    }
-
-    public function testPreviousTag()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key')->set('value');
-        $tags = $item->getPreviousTags();
-        $this->assertTrue(is_array($tags));
-        $this->assertCount(0, $tags);
-
-        $item->setTags(['tag0']);
-        $this->assertCount(0, $item->getPreviousTags());
-
-        $this->cache->save($item);
-        $this->assertCount(0, $item->getPreviousTags());
-
-        $item = $this->cache->getItem('key');
-        $this->assertCount(1, $item->getPreviousTags());
-    }
-
-    public function testPreviousTagDeferred()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key')->set('value');
-        $item->setTags(['tag0']);
-        $this->assertCount(0, $item->getPreviousTags());
-
-        $this->cache->saveDeferred($item);
-        $this->assertCount(0, $item->getPreviousTags());
-
-        $item = $this->cache->getItem('key');
-        $this->assertCount(1, $item->getPreviousTags());
-    }
-
-    public function testTagAccessorWithEmptyTag()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key')->set('value');
-        $this->expectException('Psr\Cache\InvalidArgumentException');
-        $item->setTags(['']);
-    }
-
-    /**
-     * @dataProvider invalidKeys
-     */
-    public function testTagAccessorWithInvalidTag($tag)
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key')->set('value');
-        $this->expectException('Psr\Cache\InvalidArgumentException');
-        $item->setTags([$tag]);
-    }
-
-    public function testTagAccessorDuplicateTags()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key')->set('value');
-        $item->setTags(['tag', 'tag', 'tag']);
-        $this->cache->save($item);
-        $item = $this->cache->getItem('key');
-
-        $this->assertCount(1, $item->getPreviousTags());
-    }
-
-    /**
-     * The tag must be removed whenever we remove an item. If not, when creating a new item
-     * with the same key will get the same tags.
-     */
-    public function testRemoveTagWhenItemIsRemoved()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key')->set('value');
-        $item->setTags(['tag1']);
-
-        // Save the item and then delete it
-        $this->cache->save($item);
-        $this->cache->deleteItem('key');
-
-        // Create a new item (same key) (no tags)
-        $item = $this->cache->getItem('key')->set('value');
-        $this->cache->save($item);
-
-        // Clear the tag, The new item should not be cleared
-        $this->cache->invalidateTags(['tag1']);
-        $this->assertTrue($this->cache->hasItem('key'), 'Item key should be removed from the tag list when the item is removed');
-    }
-
-    public function testClearPool()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key')->set('value');
-        $item->setTags(['tag1']);
-        $this->cache->save($item);
-
-        // Clear the pool
-        $this->cache->clear();
-
-        // Create a new item (no tags)
-        $item = $this->cache->getItem('key')->set('value');
-        $this->cache->save($item);
-        $this->cache->invalidateTags(['tag1']);
-
-        $this->assertTrue($this->cache->hasItem('key'), 'Tags should be removed when the pool was cleared.');
-    }
-
-    public function testInvalidateTag()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key')->set('value');
-        $item->setTags(['tag1', 'tag2']);
-        $this->cache->save($item);
-        $item = $this->cache->getItem('key2')->set('value');
-        $item->setTags(['tag1']);
-        $this->cache->save($item);
-
-        $this->cache->invalidateTag('tag2');
-        $this->assertFalse($this->cache->hasItem('key'), 'Item should be cleared when tag is invalidated');
-        $this->assertTrue($this->cache->hasItem('key2'), 'Item should be cleared when tag is invalidated');
-
-        // Create a new item (no tags)
-        $item = $this->cache->getItem('key')->set('value');
-        $this->cache->save($item);
-        $this->cache->invalidateTags(['tag2']);
-        $this->assertTrue($this->cache->hasItem('key'), 'Item key list should be removed when clearing the tags');
-
-        $this->cache->invalidateTags(['tag1']);
-        $this->assertTrue($this->cache->hasItem('key'), 'Item key list should be removed when clearing the tags');
-    }
-
-    public function testInvalidateTags()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $item = $this->cache->getItem('key')->set('value');
-        $item->setTags(['tag1', 'tag2']);
-        $this->cache->save($item);
-        $item = $this->cache->getItem('key2')->set('value');
-        $item->setTags(['tag1']);
-        $this->cache->save($item);
-
-        $this->cache->invalidateTags(['tag1', 'tag2']);
-        $this->assertFalse($this->cache->hasItem('key'), 'Item should be cleared when tag is invalidated');
-        $this->assertFalse($this->cache->hasItem('key2'), 'Item should be cleared when tag is invalidated');
-
-        // Create a new item (no tags)
-        $item = $this->cache->getItem('key')->set('value');
-        $this->cache->save($item);
-        $this->cache->invalidateTags(['tag1']);
-
-        $this->assertTrue($this->cache->hasItem('key'), 'Item k list should be removed when clearing the tags');
-    }
-
-    /**
-     * When an item is overwritten we need to clear tags for original item.
-     */
-    public function testTagsAreCleanedOnSave()
-    {
-        if (isset($this->skippedTests[__FUNCTION__])) {
-            $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
-        }
-
-        $pool = $this->cache;
-        $i    = $pool->getItem('key')->set('value');
-        $pool->save($i->setTags(['foo']));
-        $i = $pool->getItem('key');
-        $pool->save($i->setTags(['bar']));
-        $pool->invalidateTags(['foo']);
-        $this->assertTrue($pool->getItem('key')->isHit());
-    }
-}
diff --git a/civicrm/vendor/cache/tag-interop/.github/PULL_REQUEST_TEMPLATE.md b/civicrm/vendor/cache/tag-interop/.github/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index 4a339b4c2ffccf680771f7b6e1eca41c8409cf15..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/tag-interop/.github/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a READ ONLY repository. 
-
-Please make your pull request to https://github.com/php-cache/cache
-
-Thank you for contributing. 
diff --git a/civicrm/vendor/cache/tag-interop/.gitignore b/civicrm/vendor/cache/tag-interop/.gitignore
deleted file mode 100644
index 987e2a253ca5ad68180c3deae164ba6d1c3e2689..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/tag-interop/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-composer.lock
-vendor
diff --git a/civicrm/vendor/cache/tag-interop/.travis.yml b/civicrm/vendor/cache/tag-interop/.travis.yml
deleted file mode 100644
index 942fe27e9414354e7b7dd5d89bfd223695421e8f..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/tag-interop/.travis.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-language: php
-sudo: false
-
-matrix:
-    include:
-      - php: 7.0
-
-cache:
-    directories:
-      - "$HOME/.composer/cache"
-
-install:
-    - composer update --prefer-dist --prefer-stable
-
-script:
-    - ./vendor/bin/phpunit --coverage-clover=coverage.xml
-
-after_success:
-    - pip install --user codecov && codecov
-
-notifications:
-    email: false
diff --git a/civicrm/vendor/cache/tag-interop/Changelog.md b/civicrm/vendor/cache/tag-interop/Changelog.md
deleted file mode 100644
index 1596519c3c0b9187b8c4b15a732ab8dc6cbbaf21..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/tag-interop/Changelog.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Change Log
-
-The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release.
-
-## 1.0.0
-
-First release
-
-
diff --git a/civicrm/vendor/cache/tag-interop/LICENSE b/civicrm/vendor/cache/tag-interop/LICENSE
deleted file mode 100644
index 82f8feef6b90cc9682ffeec455562edd8e40f711..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/tag-interop/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2015 Aaron Scherer, Tobias Nyholm
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
diff --git a/civicrm/vendor/cache/tag-interop/README.md b/civicrm/vendor/cache/tag-interop/README.md
deleted file mode 100644
index 28511c91db3b83209f17eb59744a87710fdca4cd..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/tag-interop/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Tag support for PSR-6 Cache 
-[![Gitter](https://badges.gitter.im/php-cache/cache.svg)](https://gitter.im/php-cache/cache?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-[![Latest Stable Version](https://poser.pugx.org/cache/tag-interop/v/stable)](https://packagist.org/packages/cache/tag-interop)
-[![Total Downloads](https://poser.pugx.org/cache/tag-interop/downloads)](https://packagist.org/packages/cache/tag-interop)
-[![Monthly Downloads](https://poser.pugx.org/cache/tag-interop/d/monthly.png)](https://packagist.org/packages/cache/tag-interop)
-[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
-
-This repository holds two interfaces for tagging. These interfaces will make their 
-way into PHP Fig. Representatives from Symfony, PHP-cache and Drupal has worked 
-together to agree on these interfaces.   
-
-### Install
-
-```bash
-composer require cache/tag-interop
-```
-
-### Use
-
-Read the [documentation on usage](http://www.php-cache.com/).
-
-### Contribute
-
-Contributions are very welcome! Send a pull request to the [main repository](https://github.com/php-cache/cache) or
-report any issues you find on the [issue tracker](http://issues.php-cache.com).
diff --git a/civicrm/vendor/cache/tag-interop/TaggableCacheItemInterface.php b/civicrm/vendor/cache/tag-interop/TaggableCacheItemInterface.php
deleted file mode 100644
index 5823b0bb2b544906df9cdaea15155c6fb5dddeb1..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/tag-interop/TaggableCacheItemInterface.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/*
- * This file is part of php-cache organization.
- *
- * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
-
-namespace Cache\TagInterop;
-
-use Psr\Cache\CacheItemInterface;
-use Psr\Cache\InvalidArgumentException;
-
-/**
- * An item that supports tags. This interface is a soon-to-be-PSR.
- *
- * @author Tobias Nyholm <tobias.nyholm@gmail.com>
- * @author Nicolas Grekas <p@tchwork.com>
- */
-interface TaggableCacheItemInterface extends CacheItemInterface
-{
-    /**
-     * Get all existing tags. These are the tags the item has when the item is
-     * returned from the pool.
-     *
-     * @return array
-     */
-    public function getPreviousTags();
-
-    /**
-     * Overwrite all tags with a new set of tags.
-     *
-     * @param string[] $tags An array of tags
-     *
-     * @throws InvalidArgumentException When a tag is not valid.
-     *
-     * @return TaggableCacheItemInterface
-     */
-    public function setTags(array $tags);
-}
diff --git a/civicrm/vendor/cache/tag-interop/TaggableCacheItemPoolInterface.php b/civicrm/vendor/cache/tag-interop/TaggableCacheItemPoolInterface.php
deleted file mode 100644
index 055bf4b09d2db48d8ca302be62ff5dd09e21ff67..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/tag-interop/TaggableCacheItemPoolInterface.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-/*
- * This file is part of php-cache organization.
- *
- * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
-
-namespace Cache\TagInterop;
-
-use Psr\Cache\CacheItemPoolInterface;
-use Psr\Cache\InvalidArgumentException;
-
-/**
- * Interface for invalidating cached items using tags. This interface is a soon-to-be-PSR.
- *
- * @author Tobias Nyholm <tobias.nyholm@gmail.com>
- * @author Nicolas Grekas <p@tchwork.com>
- */
-interface TaggableCacheItemPoolInterface extends CacheItemPoolInterface
-{
-    /**
-     * Invalidates cached items using a tag.
-     *
-     * @param string $tag The tag to invalidate
-     *
-     * @throws InvalidArgumentException When $tags is not valid
-     *
-     * @return bool True on success
-     */
-    public function invalidateTag($tag);
-
-    /**
-     * Invalidates cached items using tags.
-     *
-     * @param string[] $tags An array of tags to invalidate
-     *
-     * @throws InvalidArgumentException When $tags is not valid
-     *
-     * @return bool True on success
-     */
-    public function invalidateTags(array $tags);
-
-    /**
-     * {@inheritdoc}
-     *
-     * @return TaggableCacheItemInterface
-     */
-    public function getItem($key);
-
-    /**
-     * {@inheritdoc}
-     *
-     * @return array|\Traversable|TaggableCacheItemInterface[]
-     */
-    public function getItems(array $keys = []);
-}
diff --git a/civicrm/vendor/cache/tag-interop/composer.json b/civicrm/vendor/cache/tag-interop/composer.json
deleted file mode 100644
index d0f7467afb61c4fd5694b7ff8adc95986067ac3c..0000000000000000000000000000000000000000
--- a/civicrm/vendor/cache/tag-interop/composer.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-    "name":        "cache/tag-interop",
-    "type":        "library",
-    "description": "Framework interoperable interfaces for tags",
-    "keywords":    [
-        "cache",
-        "psr6",
-        "tag",
-        "psr"
-    ],
-    "homepage":    "http://www.php-cache.com/en/latest/",
-    "license":     "MIT",
-    "authors":     [
-        {
-            "name":     "Tobias Nyholm",
-            "email":    "tobias.nyholm@gmail.com",
-            "homepage": "https://github.com/nyholm"
-        },
-        {
-            "name":     "Nicolas Grekas ",
-            "email":    "p@tchwork.com",
-            "homepage": "https://github.com/nicolas-grekas"
-        }
-    ],
-    "require":     {
-        "php":                   "^5.5 || ^7.0",
-        "psr/cache":             "^1.0"
-    },
-    "autoload":    {
-        "psr-4":                 {
-            "Cache\\TagInterop\\": ""
-        }
-    },
-    "extra": {
-        "branch-alias": {
-            "dev-master": "2.0-dev"
-        }
-    }
-}
diff --git a/civicrm/vendor/composer/autoload_psr4.php b/civicrm/vendor/composer/autoload_psr4.php
index 707471bcbdd481a51a2d5565f550507856f1ae1d..b335f02da74cf19632174be698c7f8c97563f39c 100644
--- a/civicrm/vendor/composer/autoload_psr4.php
+++ b/civicrm/vendor/composer/autoload_psr4.php
@@ -41,7 +41,6 @@ return array(
     'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'),
     'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
     'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
-    'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
     'PhpOffice\\PhpWord\\' => array($vendorDir . '/phpoffice/phpword/src/PhpWord'),
     'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'),
     'Padaliyajay\\PHPAutoprefixer\\' => array($vendorDir . '/padaliyajay/php-autoprefixer/src'),
@@ -67,8 +66,6 @@ return array(
     'Civi\\Cxn\\Rpc\\' => array($vendorDir . '/civicrm/civicrm-cxn-rpc/src'),
     'Civi\\CompilePlugin\\' => array($vendorDir . '/civicrm/composer-compile-plugin/src'),
     'Civi\\' => array($baseDir . '/', $baseDir . '/Civi', $baseDir . '/setup/src'),
-    'Cache\\TagInterop\\' => array($vendorDir . '/cache/tag-interop'),
-    'Cache\\IntegrationTests\\' => array($vendorDir . '/cache/integration-tests/src'),
     'CCL\\' => array($vendorDir . '/civicrm/composer-compile-lib/src'),
     'Brick\\Money\\' => array($vendorDir . '/brick/money/src'),
     'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
diff --git a/civicrm/vendor/composer/autoload_real.php b/civicrm/vendor/composer/autoload_real.php
index 1bd06617e6a5ad25e360d0d50af19bbd7e098155..e83293bd5cd7f772d513860435fb2d7d7601e4bc 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 ComposerAutoloaderInit85641ecafad0fd91a9bd9d9f47db5e32
+class ComposerAutoloaderInite1ca47ea801818fa75d60df4388c9f11
 {
     private static $loader;
 
@@ -24,9 +24,9 @@ class ComposerAutoloaderInit85641ecafad0fd91a9bd9d9f47db5e32
 
         require __DIR__ . '/platform_check.php';
 
-        spl_autoload_register(array('ComposerAutoloaderInit85641ecafad0fd91a9bd9d9f47db5e32', 'loadClassLoader'), true, true);
+        spl_autoload_register(array('ComposerAutoloaderInite1ca47ea801818fa75d60df4388c9f11', 'loadClassLoader'), true, true);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
-        spl_autoload_unregister(array('ComposerAutoloaderInit85641ecafad0fd91a9bd9d9f47db5e32', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInite1ca47ea801818fa75d60df4388c9f11', 'loadClassLoader'));
 
         $includePaths = require __DIR__ . '/include_paths.php';
         $includePaths[] = get_include_path();
@@ -36,7 +36,7 @@ class ComposerAutoloaderInit85641ecafad0fd91a9bd9d9f47db5e32
         if ($useStaticLoader) {
             require __DIR__ . '/autoload_static.php';
 
-            call_user_func(\Composer\Autoload\ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32::getInitializer($loader));
+            call_user_func(\Composer\Autoload\ComposerStaticInite1ca47ea801818fa75d60df4388c9f11::getInitializer($loader));
         } else {
             $map = require __DIR__ . '/autoload_namespaces.php';
             foreach ($map as $namespace => $path) {
@@ -57,12 +57,12 @@ class ComposerAutoloaderInit85641ecafad0fd91a9bd9d9f47db5e32
         $loader->register(true);
 
         if ($useStaticLoader) {
-            $includeFiles = Composer\Autoload\ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32::$files;
+            $includeFiles = Composer\Autoload\ComposerStaticInite1ca47ea801818fa75d60df4388c9f11::$files;
         } else {
             $includeFiles = require __DIR__ . '/autoload_files.php';
         }
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequire85641ecafad0fd91a9bd9d9f47db5e32($fileIdentifier, $file);
+            composerRequiree1ca47ea801818fa75d60df4388c9f11($fileIdentifier, $file);
         }
 
         return $loader;
@@ -74,7 +74,7 @@ class ComposerAutoloaderInit85641ecafad0fd91a9bd9d9f47db5e32
  * @param string $file
  * @return void
  */
-function composerRequire85641ecafad0fd91a9bd9d9f47db5e32($fileIdentifier, $file)
+function composerRequiree1ca47ea801818fa75d60df4388c9f11($fileIdentifier, $file)
 {
     if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
         $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
diff --git a/civicrm/vendor/composer/autoload_static.php b/civicrm/vendor/composer/autoload_static.php
index a58958a7322f3696c91fd4fec1c905d006fe7b90..b02011288d171b705fc2395243b5b5cf0e5aecee 100644
--- a/civicrm/vendor/composer/autoload_static.php
+++ b/civicrm/vendor/composer/autoload_static.php
@@ -4,7 +4,7 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32
+class ComposerStaticInite1ca47ea801818fa75d60df4388c9f11
 {
     public static $files = array (
         'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
@@ -151,7 +151,6 @@ class ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32
             'Psr\\Http\\Message\\' => 17,
             'Psr\\Http\\Client\\' => 16,
             'Psr\\Container\\' => 14,
-            'Psr\\Cache\\' => 10,
             'PhpOffice\\PhpWord\\' => 18,
             'PhpOffice\\PhpSpreadsheet\\' => 25,
             'Padaliyajay\\PHPAutoprefixer\\' => 28,
@@ -198,8 +197,6 @@ class ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32
             'Civi\\Cxn\\Rpc\\' => 13,
             'Civi\\CompilePlugin\\' => 19,
             'Civi\\' => 5,
-            'Cache\\TagInterop\\' => 17,
-            'Cache\\IntegrationTests\\' => 23,
             'CCL\\' => 4,
         ),
         'B' => 
@@ -351,10 +348,6 @@ class ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32
         array (
             0 => __DIR__ . '/..' . '/psr/container/src',
         ),
-        'Psr\\Cache\\' => 
-        array (
-            0 => __DIR__ . '/..' . '/psr/cache/src',
-        ),
         'PhpOffice\\PhpWord\\' => 
         array (
             0 => __DIR__ . '/..' . '/phpoffice/phpword/src/PhpWord',
@@ -459,14 +452,6 @@ class ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32
             1 => __DIR__ . '/../..' . '/Civi',
             2 => __DIR__ . '/../..' . '/setup/src',
         ),
-        'Cache\\TagInterop\\' => 
-        array (
-            0 => __DIR__ . '/..' . '/cache/tag-interop',
-        ),
-        'Cache\\IntegrationTests\\' => 
-        array (
-            0 => __DIR__ . '/..' . '/cache/integration-tests/src',
-        ),
         'CCL\\' => 
         array (
             0 => __DIR__ . '/..' . '/civicrm/composer-compile-lib/src',
@@ -744,11 +729,11 @@ class ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32
     public static function getInitializer(ClassLoader $loader)
     {
         return \Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32::$prefixDirsPsr4;
-            $loader->prefixesPsr0 = ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32::$prefixesPsr0;
-            $loader->fallbackDirsPsr0 = ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32::$fallbackDirsPsr0;
-            $loader->classMap = ComposerStaticInit85641ecafad0fd91a9bd9d9f47db5e32::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInite1ca47ea801818fa75d60df4388c9f11::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInite1ca47ea801818fa75d60df4388c9f11::$prefixDirsPsr4;
+            $loader->prefixesPsr0 = ComposerStaticInite1ca47ea801818fa75d60df4388c9f11::$prefixesPsr0;
+            $loader->fallbackDirsPsr0 = ComposerStaticInite1ca47ea801818fa75d60df4388c9f11::$fallbackDirsPsr0;
+            $loader->classMap = ComposerStaticInite1ca47ea801818fa75d60df4388c9f11::$classMap;
 
         }, null, ClassLoader::class);
     }
diff --git a/civicrm/vendor/composer/installed.json b/civicrm/vendor/composer/installed.json
index 317e579c0dd0df68cd7bf3c6eb4cf05a509f75b0..c70b2dc690460479362d01816a911c1548d13ed9 100644
--- a/civicrm/vendor/composer/installed.json
+++ b/civicrm/vendor/composer/installed.json
@@ -175,137 +175,6 @@
             ],
             "install-path": "../brick/money"
         },
-        {
-            "name": "cache/integration-tests",
-            "version": "0.17.0",
-            "version_normalized": "0.17.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/php-cache/integration-tests.git",
-                "reference": "eda2e6b8bc5abcd623c8047e2345cda38dd6479e"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/php-cache/integration-tests/zipball/eda2e6b8bc5abcd623c8047e2345cda38dd6479e",
-                "reference": "eda2e6b8bc5abcd623c8047e2345cda38dd6479e",
-                "shasum": ""
-            },
-            "require": {
-                "cache/tag-interop": "^1.0",
-                "php": ">=5.5.9",
-                "psr/cache": "~1.0"
-            },
-            "conflict": {
-                "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
-            },
-            "require-dev": {
-                "cache/cache": "^1.0",
-                "illuminate/cache": "^5.4|^5.5|^5.6",
-                "mockery/mockery": "^1.0",
-                "symfony/cache": "^3.4.31|^4.3.4|^5.0",
-                "symfony/phpunit-bridge": "^5.1",
-                "tedivm/stash": "^0.14"
-            },
-            "time": "2020-11-03T12:52:23+00:00",
-            "type": "library",
-            "installation-source": "dist",
-            "autoload": {
-                "psr-4": {
-                    "Cache\\IntegrationTests\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Aaron Scherer",
-                    "email": "aequasi@gmail.com",
-                    "homepage": "https://github.com/aequasi"
-                },
-                {
-                    "name": "Tobias Nyholm",
-                    "email": "tobias.nyholm@gmail.com",
-                    "homepage": "https://github.com/nyholm"
-                }
-            ],
-            "description": "Integration tests for PSR-6 and PSR-16 cache implementations",
-            "homepage": "https://github.com/php-cache/integration-tests",
-            "keywords": [
-                "cache",
-                "psr16",
-                "psr6",
-                "test"
-            ],
-            "support": {
-                "issues": "https://github.com/php-cache/integration-tests/issues",
-                "source": "https://github.com/php-cache/integration-tests/tree/0.17.0"
-            },
-            "install-path": "../cache/integration-tests"
-        },
-        {
-            "name": "cache/tag-interop",
-            "version": "1.0.0",
-            "version_normalized": "1.0.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/php-cache/tag-interop.git",
-                "reference": "c7496dd81530f538af27b4f2713cde97bc292832"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/php-cache/tag-interop/zipball/c7496dd81530f538af27b4f2713cde97bc292832",
-                "reference": "c7496dd81530f538af27b4f2713cde97bc292832",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.5 || ^7.0",
-                "psr/cache": "^1.0"
-            },
-            "time": "2017-03-13T09:14:27+00:00",
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.0-dev"
-                }
-            },
-            "installation-source": "dist",
-            "autoload": {
-                "psr-4": {
-                    "Cache\\TagInterop\\": ""
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Tobias Nyholm",
-                    "email": "tobias.nyholm@gmail.com",
-                    "homepage": "https://github.com/Nyholm"
-                },
-                {
-                    "name": "Nicolas Grekas",
-                    "email": "p@tchwork.com",
-                    "homepage": "https://github.com/nicolas-grekas"
-                }
-            ],
-            "description": "Framework interoperable interfaces for tags",
-            "homepage": "http://www.php-cache.com/en/latest/",
-            "keywords": [
-                "cache",
-                "psr",
-                "psr6",
-                "tag"
-            ],
-            "support": {
-                "issues": "https://github.com/php-cache/tag-interop/issues",
-                "source": "https://github.com/php-cache/tag-interop/tree/master"
-            },
-            "install-path": "../cache/tag-interop"
-        },
         {
             "name": "civicrm/civicrm-cxn-rpc",
             "version": "v0.20.12.01",
@@ -1607,9 +1476,13 @@
             ],
             "support": {
                 "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
-                "source": "https://github.com/maennchen/ZipStream-PHP/tree/master"
+                "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.1.0"
             },
             "funding": [
+                {
+                    "url": "https://github.com/maennchen",
+                    "type": "github"
+                },
                 {
                     "url": "https://opencollective.com/zipstream",
                     "type": "open_collective"
@@ -3195,58 +3068,6 @@
             ],
             "install-path": "../phpseclib/phpseclib"
         },
-        {
-            "name": "psr/cache",
-            "version": "1.0.1",
-            "version_normalized": "1.0.1.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/php-fig/cache.git",
-                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
-                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.3.0"
-            },
-            "time": "2016-08-06T20:24:11+00:00",
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0.x-dev"
-                }
-            },
-            "installation-source": "dist",
-            "autoload": {
-                "psr-4": {
-                    "Psr\\Cache\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
-                }
-            ],
-            "description": "Common interface for caching libraries",
-            "keywords": [
-                "cache",
-                "psr",
-                "psr-6"
-            ],
-            "support": {
-                "source": "https://github.com/php-fig/cache/tree/master"
-            },
-            "install-path": "../psr/cache"
-        },
         {
             "name": "psr/container",
             "version": "1.0.0",
diff --git a/civicrm/vendor/composer/installed.php b/civicrm/vendor/composer/installed.php
index 550f889dcf0c5b42e45a2f4d09471860505a76c5..59d8827d8372d679d9e1d85f1aa23190f65054b7 100644
--- a/civicrm/vendor/composer/installed.php
+++ b/civicrm/vendor/composer/installed.php
@@ -1,11 +1,11 @@
 <?php return array(
     'root' => array(
-        'pretty_version' => '5.59.x-dev',
-        'version' => '5.59.9999999.9999999-dev',
+        'pretty_version' => '5.60.x-dev',
+        'version' => '5.60.9999999.9999999-dev',
         'type' => 'library',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
-        'reference' => '5a40b8aac7a5b0cd7b8f432c31deae79c454b9be',
+        'reference' => 'fadbe2424f36072c5fa5dc633f11dfaf48942951',
         'name' => 'civicrm/civicrm-core',
         'dev' => true,
     ),
@@ -37,31 +37,13 @@
             'reference' => '49e6597470da74f6a9f1dd7d5286ea3b4756b7e0',
             'dev_requirement' => false,
         ),
-        'cache/integration-tests' => array(
-            'pretty_version' => '0.17.0',
-            'version' => '0.17.0.0',
-            'type' => 'library',
-            'install_path' => __DIR__ . '/../cache/integration-tests',
-            'aliases' => array(),
-            'reference' => 'eda2e6b8bc5abcd623c8047e2345cda38dd6479e',
-            'dev_requirement' => false,
-        ),
-        'cache/tag-interop' => array(
-            'pretty_version' => '1.0.0',
-            'version' => '1.0.0.0',
-            'type' => 'library',
-            'install_path' => __DIR__ . '/../cache/tag-interop',
-            'aliases' => array(),
-            'reference' => 'c7496dd81530f538af27b4f2713cde97bc292832',
-            'dev_requirement' => false,
-        ),
         'civicrm/civicrm-core' => array(
-            'pretty_version' => '5.59.x-dev',
-            'version' => '5.59.9999999.9999999-dev',
+            'pretty_version' => '5.60.x-dev',
+            'version' => '5.60.9999999.9999999-dev',
             'type' => 'library',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
-            'reference' => '5a40b8aac7a5b0cd7b8f432c31deae79c454b9be',
+            'reference' => 'fadbe2424f36072c5fa5dc633f11dfaf48942951',
             'dev_requirement' => false,
         ),
         'civicrm/civicrm-cxn-rpc' => array(
@@ -448,15 +430,6 @@
             'reference' => '233a920cb38636a43b18d428f9a8db1f0a1a08f4',
             'dev_requirement' => false,
         ),
-        'psr/cache' => array(
-            'pretty_version' => '1.0.1',
-            'version' => '1.0.1.0',
-            'type' => 'library',
-            'install_path' => __DIR__ . '/../psr/cache',
-            'aliases' => array(),
-            'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8',
-            'dev_requirement' => false,
-        ),
         'psr/container' => array(
             'pretty_version' => '1.0.0',
             'version' => '1.0.0.0',
diff --git a/civicrm/vendor/psr/cache/CHANGELOG.md b/civicrm/vendor/psr/cache/CHANGELOG.md
deleted file mode 100644
index 58ddab05aa1466153e77c25abbb4e7eb7905171a..0000000000000000000000000000000000000000
--- a/civicrm/vendor/psr/cache/CHANGELOG.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Changelog
-
-All notable changes to this project will be documented in this file, in reverse chronological order by release.
-
-## 1.0.1 - 2016-08-06
-
-### Fixed
-
-- Make spacing consistent in phpdoc annotations php-fig/cache#9 - chalasr
-- Fix grammar in phpdoc annotations php-fig/cache#10 - chalasr
-- Be more specific in docblocks that `getItems()` and `deleteItems()` take an array of strings (`string[]`) compared to just `array` php-fig/cache#8 - GrahamCampbell
-- For `expiresAt()` and `expiresAfter()` in CacheItemInterface fix docblock to specify null as a valid parameters as well as an implementation of DateTimeInterface php-fig/cache#7 - GrahamCampbell
-
-## 1.0.0 - 2015-12-11
-
-Initial stable release; reflects accepted PSR-6 specification
diff --git a/civicrm/vendor/psr/cache/LICENSE.txt b/civicrm/vendor/psr/cache/LICENSE.txt
deleted file mode 100644
index b1c2c97b9d42af7147b9a1a56848d7c746418f8c..0000000000000000000000000000000000000000
--- a/civicrm/vendor/psr/cache/LICENSE.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2015 PHP Framework Interoperability Group
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/civicrm/vendor/psr/cache/README.md b/civicrm/vendor/psr/cache/README.md
deleted file mode 100644
index c8706ceea6625ab367583434c1b640af977c61ba..0000000000000000000000000000000000000000
--- a/civicrm/vendor/psr/cache/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-PSR Cache
-=========
-
-This repository holds all interfaces defined by
-[PSR-6](http://www.php-fig.org/psr/psr-6/).
-
-Note that this is not a Cache implementation of its own. It is merely an
-interface that describes a Cache implementation. See the specification for more 
-details.
diff --git a/civicrm/vendor/psr/cache/composer.json b/civicrm/vendor/psr/cache/composer.json
deleted file mode 100644
index e828fec9430d6397ab4b17eb8c384e4db381e245..0000000000000000000000000000000000000000
--- a/civicrm/vendor/psr/cache/composer.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-    "name": "psr/cache",
-    "description": "Common interface for caching libraries",
-    "keywords": ["psr", "psr-6", "cache"],
-    "license": "MIT",
-    "authors": [
-        {
-            "name": "PHP-FIG",
-            "homepage": "http://www.php-fig.org/"
-        }
-    ],
-    "require": {
-        "php": ">=5.3.0"
-    },
-    "autoload": {
-        "psr-4": {
-            "Psr\\Cache\\": "src/"
-        }
-    },
-    "extra": {
-        "branch-alias": {
-            "dev-master": "1.0.x-dev"
-        }
-    }
-}
diff --git a/civicrm/vendor/psr/cache/src/CacheException.php b/civicrm/vendor/psr/cache/src/CacheException.php
deleted file mode 100644
index e27f22f8d6026cfa5bcb94bba6f316a88cb722b4..0000000000000000000000000000000000000000
--- a/civicrm/vendor/psr/cache/src/CacheException.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psr\Cache;
-
-/**
- * Exception interface for all exceptions thrown by an Implementing Library.
- */
-interface CacheException
-{
-}
diff --git a/civicrm/vendor/psr/cache/src/CacheItemInterface.php b/civicrm/vendor/psr/cache/src/CacheItemInterface.php
deleted file mode 100644
index 63d05dd1f464399289500ad3d5c89cebe97de9eb..0000000000000000000000000000000000000000
--- a/civicrm/vendor/psr/cache/src/CacheItemInterface.php
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-
-namespace Psr\Cache;
-
-/**
- * CacheItemInterface defines an interface for interacting with objects inside a cache.
- *
- * Each Item object MUST be associated with a specific key, which can be set
- * according to the implementing system and is typically passed by the
- * Cache\CacheItemPoolInterface object.
- *
- * The Cache\CacheItemInterface object encapsulates the storage and retrieval of
- * cache items. Each Cache\CacheItemInterface is generated by a
- * Cache\CacheItemPoolInterface object, which is responsible for any required
- * setup as well as associating the object with a unique Key.
- * Cache\CacheItemInterface objects MUST be able to store and retrieve any type
- * of PHP value defined in the Data section of the specification.
- *
- * Calling Libraries MUST NOT instantiate Item objects themselves. They may only
- * be requested from a Pool object via the getItem() method.  Calling Libraries
- * SHOULD NOT assume that an Item created by one Implementing Library is
- * compatible with a Pool from another Implementing Library.
- */
-interface CacheItemInterface
-{
-    /**
-     * Returns the key for the current cache item.
-     *
-     * The key is loaded by the Implementing Library, but should be available to
-     * the higher level callers when needed.
-     *
-     * @return string
-     *   The key string for this cache item.
-     */
-    public function getKey();
-
-    /**
-     * Retrieves the value of the item from the cache associated with this object's key.
-     *
-     * The value returned must be identical to the value originally stored by set().
-     *
-     * If isHit() returns false, this method MUST return null. Note that null
-     * is a legitimate cached value, so the isHit() method SHOULD be used to
-     * differentiate between "null value was found" and "no value was found."
-     *
-     * @return mixed
-     *   The value corresponding to this cache item's key, or null if not found.
-     */
-    public function get();
-
-    /**
-     * Confirms if the cache item lookup resulted in a cache hit.
-     *
-     * Note: This method MUST NOT have a race condition between calling isHit()
-     * and calling get().
-     *
-     * @return bool
-     *   True if the request resulted in a cache hit. False otherwise.
-     */
-    public function isHit();
-
-    /**
-     * Sets the value represented by this cache item.
-     *
-     * The $value argument may be any item that can be serialized by PHP,
-     * although the method of serialization is left up to the Implementing
-     * Library.
-     *
-     * @param mixed $value
-     *   The serializable value to be stored.
-     *
-     * @return static
-     *   The invoked object.
-     */
-    public function set($value);
-
-    /**
-     * Sets the expiration time for this cache item.
-     *
-     * @param \DateTimeInterface|null $expiration
-     *   The point in time after which the item MUST be considered expired.
-     *   If null is passed explicitly, a default value MAY be used. If none is set,
-     *   the value should be stored permanently or for as long as the
-     *   implementation allows.
-     *
-     * @return static
-     *   The called object.
-     */
-    public function expiresAt($expiration);
-
-    /**
-     * Sets the expiration time for this cache item.
-     *
-     * @param int|\DateInterval|null $time
-     *   The period of time from the present after which the item MUST be considered
-     *   expired. An integer parameter is understood to be the time in seconds until
-     *   expiration. If null is passed explicitly, a default value MAY be used.
-     *   If none is set, the value should be stored permanently or for as long as the
-     *   implementation allows.
-     *
-     * @return static
-     *   The called object.
-     */
-    public function expiresAfter($time);
-}
diff --git a/civicrm/vendor/psr/cache/src/CacheItemPoolInterface.php b/civicrm/vendor/psr/cache/src/CacheItemPoolInterface.php
deleted file mode 100644
index 0351419671e2642f9973f9fcfa023787b117780a..0000000000000000000000000000000000000000
--- a/civicrm/vendor/psr/cache/src/CacheItemPoolInterface.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Psr\Cache;
-
-/**
- * CacheItemPoolInterface generates CacheItemInterface objects.
- *
- * The primary purpose of Cache\CacheItemPoolInterface is to accept a key from
- * the Calling Library and return the associated Cache\CacheItemInterface object.
- * It is also the primary point of interaction with the entire cache collection.
- * All configuration and initialization of the Pool is left up to an
- * Implementing Library.
- */
-interface CacheItemPoolInterface
-{
-    /**
-     * Returns a Cache Item representing the specified key.
-     *
-     * This method must always return a CacheItemInterface object, even in case of
-     * a cache miss. It MUST NOT return null.
-     *
-     * @param string $key
-     *   The key for which to return the corresponding Cache Item.
-     *
-     * @throws InvalidArgumentException
-     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
-     *   MUST be thrown.
-     *
-     * @return CacheItemInterface
-     *   The corresponding Cache Item.
-     */
-    public function getItem($key);
-
-    /**
-     * Returns a traversable set of cache items.
-     *
-     * @param string[] $keys
-     *   An indexed array of keys of items to retrieve.
-     *
-     * @throws InvalidArgumentException
-     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
-     *   MUST be thrown.
-     *
-     * @return array|\Traversable
-     *   A traversable collection of Cache Items keyed by the cache keys of
-     *   each item. A Cache item will be returned for each key, even if that
-     *   key is not found. However, if no keys are specified then an empty
-     *   traversable MUST be returned instead.
-     */
-    public function getItems(array $keys = array());
-
-    /**
-     * Confirms if the cache contains specified cache item.
-     *
-     * Note: This method MAY avoid retrieving the cached value for performance reasons.
-     * This could result in a race condition with CacheItemInterface::get(). To avoid
-     * such situation use CacheItemInterface::isHit() instead.
-     *
-     * @param string $key
-     *   The key for which to check existence.
-     *
-     * @throws InvalidArgumentException
-     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
-     *   MUST be thrown.
-     *
-     * @return bool
-     *   True if item exists in the cache, false otherwise.
-     */
-    public function hasItem($key);
-
-    /**
-     * Deletes all items in the pool.
-     *
-     * @return bool
-     *   True if the pool was successfully cleared. False if there was an error.
-     */
-    public function clear();
-
-    /**
-     * Removes the item from the pool.
-     *
-     * @param string $key
-     *   The key to delete.
-     *
-     * @throws InvalidArgumentException
-     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
-     *   MUST be thrown.
-     *
-     * @return bool
-     *   True if the item was successfully removed. False if there was an error.
-     */
-    public function deleteItem($key);
-
-    /**
-     * Removes multiple items from the pool.
-     *
-     * @param string[] $keys
-     *   An array of keys that should be removed from the pool.
-
-     * @throws InvalidArgumentException
-     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
-     *   MUST be thrown.
-     *
-     * @return bool
-     *   True if the items were successfully removed. False if there was an error.
-     */
-    public function deleteItems(array $keys);
-
-    /**
-     * Persists a cache item immediately.
-     *
-     * @param CacheItemInterface $item
-     *   The cache item to save.
-     *
-     * @return bool
-     *   True if the item was successfully persisted. False if there was an error.
-     */
-    public function save(CacheItemInterface $item);
-
-    /**
-     * Sets a cache item to be persisted later.
-     *
-     * @param CacheItemInterface $item
-     *   The cache item to save.
-     *
-     * @return bool
-     *   False if the item could not be queued or if a commit was attempted and failed. True otherwise.
-     */
-    public function saveDeferred(CacheItemInterface $item);
-
-    /**
-     * Persists any deferred cache items.
-     *
-     * @return bool
-     *   True if all not-yet-saved items were successfully saved or there were none. False otherwise.
-     */
-    public function commit();
-}
diff --git a/civicrm/vendor/psr/cache/src/InvalidArgumentException.php b/civicrm/vendor/psr/cache/src/InvalidArgumentException.php
deleted file mode 100644
index be7c6fa0ef18689bb415312f579993a10f9b7943..0000000000000000000000000000000000000000
--- a/civicrm/vendor/psr/cache/src/InvalidArgumentException.php
+++ /dev/null
@@ -1,13 +0,0 @@
-<?php
-
-namespace Psr\Cache;
-
-/**
- * Exception interface for invalid cache arguments.
- *
- * Any time an invalid argument is passed into a method it must throw an
- * exception class which implements Psr\Cache\InvalidArgumentException.
- */
-interface InvalidArgumentException extends CacheException
-{
-}
diff --git a/civicrm/xml/schema/Contact/Relationship.xml b/civicrm/xml/schema/Contact/Relationship.xml
index ed9cb1ee3661a6e3faa3a3ce4efc5a0fc9f96fa8..fd121341d7b5ed2532678c9d58b98b8b3752eeb4 100644
--- a/civicrm/xml/schema/Contact/Relationship.xml
+++ b/civicrm/xml/schema/Contact/Relationship.xml
@@ -177,6 +177,7 @@
     <default>NULL</default>
     <comment>FK to civicrm_case</comment>
     <html>
+      <type>EntityRef</type>
       <label>Case</label>
     </html>
     <add>2.2</add>
diff --git a/civicrm/xml/schema/Contact/SubscriptionHistory.xml b/civicrm/xml/schema/Contact/SubscriptionHistory.xml
index cbf4d6858874ea14e677516a325dde82f3f2b2c3..7506e9bde31042898ca440c2d397de01fe398b92 100644
--- a/civicrm/xml/schema/Contact/SubscriptionHistory.xml
+++ b/civicrm/xml/schema/Contact/SubscriptionHistory.xml
@@ -70,6 +70,11 @@
     <required>true</required>
     <default>CURRENT_TIMESTAMP</default>
     <comment>Date of the (un)subscription</comment>
+    <html>
+      <label>Group Membership Action Date</label>
+      <type>Select Date</type>
+      <formatType>activityDateTime</formatType>
+    </html>
     <add>1.1</add>
   </field>
   <field>
diff --git a/civicrm/xml/schema/Core/CustomField.xml b/civicrm/xml/schema/Core/CustomField.xml
index f9fd912d76df47a6f16fe1a67cd30a8053d3e781..01ed1a33ec410fe854be201911140f77a73f1453 100644
--- a/civicrm/xml/schema/Core/CustomField.xml
+++ b/civicrm/xml/schema/Core/CustomField.xml
@@ -332,6 +332,15 @@
     <comment>Should the multi-record custom field values be displayed in tab table listing</comment>
     <add>4.5</add>
   </field>
+  <field>
+    <name>fk_entity</name>
+    <type>varchar</type>
+    <title>Entity</title>
+    <length>255</length>
+    <default>NULL</default>
+    <comment>Name of entity being referenced.</comment>
+    <add>5.60</add>
+  </field>
   <index>
     <name>UI_label_custom_group_id</name>
     <fieldName>label</fieldName>
diff --git a/civicrm/xml/schema/Core/JobLog.xml b/civicrm/xml/schema/Core/JobLog.xml
index 9fb80bfdacb66542c786e253ec77dc122c3f522a..56609faf20e6e9512f982dc2f73e26f2458742f7 100644
--- a/civicrm/xml/schema/Core/JobLog.xml
+++ b/civicrm/xml/schema/Core/JobLog.xml
@@ -55,9 +55,19 @@
     <name>job_id</name>
     <title>Job ID</title>
     <type>int unsigned</type>
-    <comment>Pointer to job id - not a FK though, just for logging purposes</comment>
+    <comment>Pointer to job id</comment>
+    <html>
+      <type>Number</type>
+    </html>
     <add>4.1</add>
   </field>
+  <foreignKey>
+    <name>job_id</name>
+    <table>civicrm_job</table>
+    <key>id</key>
+    <add>5.60</add>
+    <onDelete>SET NULL</onDelete>
+  </foreignKey>
   <field>
     <name>name</name>
     <title>Job Name</title>
diff --git a/civicrm/xml/templates/message_templates/contribution_online_receipt_html.tpl b/civicrm/xml/templates/message_templates/contribution_online_receipt_html.tpl
index 13623e7c15228c0cfa0fea6b8ec072c9a4fd85a1..a3d4ec3f883747f3299aed88f7e8d84bfbd3bffd 100644
--- a/civicrm/xml/templates/message_templates/contribution_online_receipt_html.tpl
+++ b/civicrm/xml/templates/message_templates/contribution_online_receipt_html.tpl
@@ -391,7 +391,7 @@
         </td>
        </tr>
       {/if}
-      {if !empty($is_deductible) AND !empty($price)}
+      {if $is_deductible AND !empty($price)}
         <tr>
          <td colspan="2" {$valueStyle}>
           <p>{ts 1=$price|crmMoney:$currency}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}</p>
diff --git a/civicrm/xml/templates/message_templates/contribution_online_receipt_text.tpl b/civicrm/xml/templates/message_templates/contribution_online_receipt_text.tpl
index 3927b5fed8c276f2cafbf8d0a23de94ca19589b6..c59a8976e89523b75aeb6063a7fb4f43d9dc41c7 100644
--- a/civicrm/xml/templates/message_templates/contribution_online_receipt_text.tpl
+++ b/civicrm/xml/templates/message_templates/contribution_online_receipt_text.tpl
@@ -176,7 +176,7 @@
   {$contact_phone}
 {/if}
 {/if}
-{if !empty($is_deductible) AND !empty($price)}
+{if $is_deductible AND !empty($price)}
 
 {ts 1=$price|crmMoney:$currency}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}{/if}
 {/if}
diff --git a/civicrm/xml/templates/message_templates/membership_online_receipt_html.tpl b/civicrm/xml/templates/message_templates/membership_online_receipt_html.tpl
index 3bb4b2f4173afa32be58811891dee99a1e86a0e8..efc2fa10a75b4c050a6f46a899c1117ad9d80a3b 100644
--- a/civicrm/xml/templates/message_templates/membership_online_receipt_html.tpl
+++ b/civicrm/xml/templates/message_templates/membership_online_receipt_html.tpl
@@ -496,7 +496,7 @@
         </td>
        </tr>
       {/if}
-      {if !empty($is_deductible) AND !empty($price)}
+      {if $is_deductible AND !empty($price)}
         <tr>
          <td colspan="2" {$valueStyle}>
           <p>{ts 1=$price|crmMoney}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}</p>
diff --git a/civicrm/xml/templates/message_templates/membership_online_receipt_text.tpl b/civicrm/xml/templates/message_templates/membership_online_receipt_text.tpl
index 02be9e48f3ed9b0186cc6cd1ec48d11f3cab5b0f..ce1b0e7566458de78e310c9350b93a138ad28c20 100644
--- a/civicrm/xml/templates/message_templates/membership_online_receipt_text.tpl
+++ b/civicrm/xml/templates/message_templates/membership_online_receipt_text.tpl
@@ -210,7 +210,7 @@
   {$contact_phone}
 {/if}
 {/if}
-{if !empty($is_deductible) AND !empty($price)}
+{if $is_deductible AND !empty($price)}
 
 {ts 1=$price|crmMoney}The value of this premium is %1. This may affect the amount of the tax deduction you can claim. Consult your tax advisor for more information.{/ts}{/if}
 {/if}
diff --git a/civicrm/xml/version.xml b/civicrm/xml/version.xml
index 22e99f76801b9e685ec9391fcacec34d8e8c53cf..35548fbca3b541edef33c53977962559076e8777 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.59.4</version_no>
+  <version_no>5.60.0</version_no>
 </version>
diff --git a/readme.txt b/readme.txt
index 97933e2c29ee98e66192c169bc6c4e41f9d9f298..76557b46db57f6e530094545a6baed1c394d9ff7 100644
--- a/readme.txt
+++ b/readme.txt
@@ -3,7 +3,7 @@ Contributors: needle
 Tags: civicrm, crm
 Requires at least: 4.9
 Tested up to: 6.1
-Stable tag: 5.59
+Stable tag: 5.60
 License: AGPL3
 License URI: http://www.gnu.org/licenses/agpl-3.0.html