From 0a385423c0d6b024d1cd5e2740a5c61f729ffcce Mon Sep 17 00:00:00 2001
From: Kevin Cristiano <kcristiano@kcristiano.com>
Date: Wed, 31 Jan 2024 08:51:23 -0500
Subject: [PATCH] civicrm release-5.69.4

---
 civicrm.php                                   |   4 +-
 .../CRM/Contribute/Form/Contribution/Main.php | 122 ++++++++++--------
 civicrm/CRM/Price/BAO/PriceField.php          |   4 +
 civicrm/civicrm-version.php                   |   2 +-
 civicrm/ext/afform/admin/info.xml             |   4 +-
 .../afform/core/ang/af/afField.component.js   |   8 +-
 .../afform/core/ang/af/fields/EntityRef.html  |   2 +-
 civicrm/ext/afform/core/info.xml              |   4 +-
 civicrm/ext/afform/html/info.xml              |   4 +-
 civicrm/ext/afform/mock/info.xml              |   4 +-
 civicrm/ext/authx/info.xml                    |   4 +-
 civicrm/ext/civi_campaign/info.xml            |   4 +-
 civicrm/ext/civi_case/info.xml                |   4 +-
 civicrm/ext/civi_contribute/info.xml          |   4 +-
 civicrm/ext/civi_event/info.xml               |   4 +-
 civicrm/ext/civi_mail/info.xml                |   4 +-
 civicrm/ext/civi_member/info.xml              |   4 +-
 civicrm/ext/civi_pledge/info.xml              |   4 +-
 civicrm/ext/civi_report/info.xml              |   4 +-
 civicrm/ext/civicrm_admin_ui/info.xml         |   4 +-
 civicrm/ext/civicrm_search_ui/info.xml        |   4 +-
 civicrm/ext/civigrant/info.xml                |   4 +-
 civicrm/ext/civiimport/info.xml               |   4 +-
 civicrm/ext/ckeditor4/info.xml                |   4 +-
 .../ext/contributioncancelactions/info.xml    |   4 +-
 civicrm/ext/elavon/info.xml                   |   4 +-
 civicrm/ext/eventcart/info.xml                |   4 +-
 civicrm/ext/ewaysingle/info.xml               |   4 +-
 civicrm/ext/financialacls/info.xml            |   4 +-
 civicrm/ext/flexmailer/info.xml               |   4 +-
 civicrm/ext/greenwich/info.xml                |   4 +-
 civicrm/ext/legacycustomsearches/info.xml     |   4 +-
 civicrm/ext/message_admin/info.xml            |   4 +-
 civicrm/ext/oauth-client/info.xml             |   4 +-
 civicrm/ext/payflowpro/info.xml               |   4 +-
 civicrm/ext/recaptcha/info.xml                |   4 +-
 civicrm/ext/scheduled_communications/info.xml |   4 +-
 civicrm/ext/search_kit/info.xml               |   4 +-
 civicrm/ext/sequentialcreditnotes/info.xml    |   4 +-
 civicrm/ext/standaloneusers/info.xml          |   4 +-
 civicrm/ext/user_dashboard/info.xml           |   4 +-
 civicrm/js/Common.js                          |  14 +-
 civicrm/js/version.json                       |   2 +-
 civicrm/release-notes.md                      |   9 ++
 civicrm/release-notes/5.69.4.md               |  47 +++++++
 civicrm/sql/civicrm_data.mysql                |   2 +-
 civicrm/sql/civicrm_generated.mysql           |   2 +-
 civicrm/templates/CRM/ACL/Header.tpl          |  37 ------
 .../CRM/Contact/Page/View/CustomDataView.tpl  |   2 +-
 .../CRM/Contribute/Form/Contribution/Main.tpl |  35 ++---
 .../Form/Contribution/MainMembershipBlock.tpl |   2 +-
 civicrm/vendor/autoload.php                   |   2 +-
 civicrm/vendor/composer/autoload_real.php     |  14 +-
 civicrm/vendor/composer/autoload_static.php   |  12 +-
 civicrm/vendor/composer/installed.php         |   4 +-
 civicrm/xml/version.xml                       |   2 +-
 56 files changed, 259 insertions(+), 209 deletions(-)
 create mode 100644 civicrm/release-notes/5.69.4.md

diff --git a/civicrm.php b/civicrm.php
index 493058e427..a901ea74f5 100644
--- a/civicrm.php
+++ b/civicrm.php
@@ -2,7 +2,7 @@
 /**
  * Plugin Name: CiviCRM
  * Description: CiviCRM - Growing and Sustaining Relationships
- * Version: 5.69.3
+ * Version: 5.69.4
  * 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.69.3');
+define('CIVICRM_PLUGIN_VERSION', '5.69.4');
 
 // Store reference to this file.
 if (!defined('CIVICRM_PLUGIN_FILE')) {
diff --git a/civicrm/CRM/Contribute/Form/Contribution/Main.php b/civicrm/CRM/Contribute/Form/Contribution/Main.php
index a39d1b0665..cd5fe3c026 100644
--- a/civicrm/CRM/Contribute/Form/Contribution/Main.php
+++ b/civicrm/CRM/Contribute/Form/Contribution/Main.php
@@ -629,12 +629,11 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
    * @throws \CRM_Core_Exception
    */
   private function buildMembershipBlock(): ?bool {
-    $cid = $this->_membershipContactID;
     $separateMembershipPayment = FALSE;
     $this->addOptionalQuickFormElement('auto_renew');
     $this->addExpectedSmartyVariable('renewal_mode');
     if ($this->_membershipBlock) {
-      $membershipTypeIds = $membershipTypes = $radio = [];
+      $membershipTypes = $radio = [];
       // This is always true if this line is reachable - remove along with the upcoming if.
       $membershipPriceset = TRUE;
 
@@ -643,69 +642,56 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
 
       $separateMembershipPayment = $this->_membershipBlock['is_separate_payment'] ?? NULL;
 
-      foreach ($this->getPriceFieldMetaData() as $pField) {
-        if (empty($pField['options'])) {
-          continue;
-        }
-        foreach ($pField['options'] as $opId => $opValues) {
-          if (empty($opValues['membership_type_id'])) {
-            continue;
-          }
-          $membershipTypeIds[$opValues['membership_type_id']] = $opValues['membership_type_id'];
-        }
-      }
+      $membershipTypeIds = $this->getAvailableMembershipTypeIDs();
 
       //because we take first membership record id for renewal
-      $memberships = $this->getExistingMemberships();
       if (!empty($membershipTypeIds)) {
+        // @todo = this hook should be called when loading the priceFieldMetadata in preProcess & incorporated
+        // There should be function to retrieve rather than property access.
         $membershipTypeValues = CRM_Member_BAO_Membership::buildMembershipTypeValues($this, $membershipTypeIds);
         $this->_membershipTypeValues = $membershipTypeValues;
-        $endDate = NULL;
-
-        $allowAutoRenewOpt = $this->isPageHasPaymentProcessorSupportForRecurring();
-        foreach ($membershipTypeIds as $value) {
-          $memType = $membershipTypeValues[$value];
-          if ($memType['is_active']) {
-            $autoRenewMembershipTypeOptions["autoRenewMembershipType_{$value}"] = $this->getConfiguredAutoRenewOptionForMembershipType($value);
-            if ($allowAutoRenewOpt) {
-              $allowAutoRenewMembership = TRUE;
+      }
+      $endDate = NULL;
+
+      foreach ($membershipTypeIds as $membershipTypeID) {
+        $memType = $membershipTypeValues[$membershipTypeID];
+        if ($memType['is_active']) {
+          $autoRenewMembershipTypeOptions["autoRenewMembershipType_{$membershipTypeID}"] = $this->getConfiguredAutoRenewOptionForMembershipType($membershipTypeID);
+          if ($this->isPageHasPaymentProcessorSupportForRecurring()) {
+            $allowAutoRenewMembership = TRUE;
+          }
+          else {
+            $javascriptMethod = NULL;
+          }
+
+          //add membership type.
+          $radio[$memType['id']] = NULL;
+          //show current membership, skip pending and cancelled membership records,
+          $membership = $this->getExistingMembership($membershipTypeID);
+          if ($membership) {
+            if ($membership["membership_type_id.duration_unit:name"] === 'lifetime') {
+              unset($radio[$memType['id']]);
+              $this->assign('hasExistingLifetimeMembership', TRUE);
+              continue;
             }
-            else {
-              $javascriptMethod = NULL;
+            $this->define('Membership', 'CurrentMembership', $membership);
+            $memType['current_membership'] = $membership['end_date'];
+            if (!$endDate) {
+              $endDate = $memType['current_membership'];
+              $this->_defaultMemTypeId = $memType['id'];
             }
-
-            //add membership type.
-            $radio[$memType['id']] = NULL;
-            if ($cid) {
-              //show current membership, skip pending and cancelled membership records,
-              $membership = $memberships[$memType['id']] ?? NULL;
-              if ($membership) {
-                if ($membership["membership_type_id.duration_unit:name"] === 'lifetime') {
-                  unset($radio[$memType['id']]);
-                  $this->assign('hasExistingLifetimeMembership', TRUE);
-                  unset($memberships[$memType['id']]);
-                  continue;
-                }
-                $this->define('Membership', 'CurrentMembership', $membership);
-                $memType['current_membership'] = $membership['end_date'];
-                if (!$endDate) {
-                  $endDate = $memType['current_membership'];
-                  $this->_defaultMemTypeId = $memType['id'];
-                }
-                if ($memType['current_membership'] < $endDate) {
-                  $endDate = $memType['current_membership'];
-                  $this->_defaultMemTypeId = $memType['id'];
-                }
-              }
+            if ($memType['current_membership'] < $endDate) {
+              $endDate = $memType['current_membership'];
+              $this->_defaultMemTypeId = $memType['id'];
             }
-            $membershipTypes[] = $memType;
           }
+          $membershipTypes[] = $memType;
         }
       }
 
       $this->assign('membershipBlock', $this->_membershipBlock);
       $this->assign('showRadio', TRUE);
-      $this->assign('renewal_mode', !empty($memberships));
+      $this->assign('renewal_mode', $this->contactHasRenewableMembership());
       $this->assign('membershipTypes', $membershipTypes);
       $this->assign('allowAutoRenewMembership', $allowAutoRenewMembership);
       $this->assign('autoRenewMembershipTypeOptions', json_encode($autoRenewMembershipTypeOptions));
@@ -1799,6 +1785,32 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
     return $this->existingMemberships ?? [];
   }
 
+  /**
+   * Get the first existing membership of the given type.
+   *
+   * @param int $membershipTypeID
+   * @return array|null
+   *
+   * @throws \CRM_Core_Exception
+   */
+  private function getExistingMembership(int $membershipTypeID): ?array {
+    foreach ($this->getExistingMemberships() as $membership) {
+      if ($membership['membership_type_id'] === $membershipTypeID) {
+        return $membership;
+      }
+    }
+    return NULL;
+  }
+
+  private function contactHasRenewableMembership(): bool {
+    foreach ($this->getExistingMemberships() as $membership) {
+      if ($membership['membership_type_id.duration_unit:name'] !== 'lifetime') {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
   /**
    * Get the membership type IDs available in the price set.
    *
@@ -1807,8 +1819,12 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
    */
   private function getAvailableMembershipTypeIDs(): array {
     $membershipTypeIDs = [];
-    foreach ($this->getMembershipLineItems() as $lineItem) {
-      $membershipTypeIDs[$lineItem['membership_type_id']] = $lineItem['membership_type_id'];
+    foreach ($this->getPriceFieldMetaData() as $priceField) {
+      foreach ($priceField['options'] ?? [] as $option) {
+        if (!empty($option['membership_type_id'])) {
+          $membershipTypeIDs[$option['membership_type_id']] = $option['membership_type_id'];
+        }
+      }
     }
     return $membershipTypeIDs;
   }
diff --git a/civicrm/CRM/Price/BAO/PriceField.php b/civicrm/CRM/Price/BAO/PriceField.php
index a161fae726..38629f0f03 100644
--- a/civicrm/CRM/Price/BAO/PriceField.php
+++ b/civicrm/CRM/Price/BAO/PriceField.php
@@ -389,6 +389,8 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
             'price' => json_encode([$elementName, '0|0']),
             'data-currency' => $currencyName,
             'onclick' => 'clearAmountOther();',
+            'data-amount' => 0,
+            'data-is-null-option' => TRUE,
           ];
         }
 
@@ -408,6 +410,8 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
           $choiceAttrs['0'] = [
             'price' => json_encode([$elementName, '0']),
             'data-membership-type-id' => NULL,
+            'data-amount' => 0,
+            'data-is-null-option' => TRUE,
           ] + $incomingExtra;
         }
 
diff --git a/civicrm/civicrm-version.php b/civicrm/civicrm-version.php
index 362820b041..e997f80511 100644
--- a/civicrm/civicrm-version.php
+++ b/civicrm/civicrm-version.php
@@ -1,7 +1,7 @@
 <?php
 /** @deprecated */
 function civicrmVersion( ) {
-  return array( 'version'  => '5.69.3',
+  return array( 'version'  => '5.69.4',
                 'cms'      => 'Wordpress',
                 'revision' => '' );
 }
diff --git a/civicrm/ext/afform/admin/info.xml b/civicrm/ext/afform/admin/info.xml
index 3762b1ff97..d348ae83c5 100644
--- a/civicrm/ext/afform/admin/info.xml
+++ b/civicrm/ext/afform/admin/info.xml
@@ -12,8 +12,8 @@
     <url desc="Chat">https://chat.civicrm.org/civicrm/channels/dev-afform</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>beta</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/afform/core/ang/af/afField.component.js b/civicrm/ext/afform/core/ang/af/afField.component.js
index c8c56c6e72..80d1d1ed05 100644
--- a/civicrm/ext/afform/core/ang/af/afField.component.js
+++ b/civicrm/ext/afform/core/ang/af/afField.component.js
@@ -256,8 +256,12 @@
         };
       };
 
-      ctrl.getAutocompleteFieldName = function() {
-        return ctrl.afFieldset.modelName + (ctrl.afJoin ? ('+' + ctrl.afJoin.entity) : '') + ':' + ctrl.fieldName;
+      ctrl.getAutocompleteParams = function() {
+        return {
+          formName: 'afform:' + ctrl.afFieldset.getFormName(),
+          fieldName: ctrl.afFieldset.getName() + ':' + ctrl.fieldName,
+          values: $scope.dataProvider.getFieldData()
+        };
       };
 
       $scope.getOptions = function () {
diff --git a/civicrm/ext/afform/core/ang/af/fields/EntityRef.html b/civicrm/ext/afform/core/ang/af/fields/EntityRef.html
index 28bd70cf82..57b830dc69 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.getName() + ':' + $ctrl.fieldName, values: dataProvider.getFieldData()}"
+       crm-autocomplete-params="$ctrl.getAutocompleteParams"
        multi="$ctrl.defn.input_attrs.multiple"
        auto-open="$ctrl.defn.input_attrs.autoOpen"
        quick-add="$ctrl.defn.input_attrs.quickAdd"
diff --git a/civicrm/ext/afform/core/info.xml b/civicrm/ext/afform/core/info.xml
index fcf0b28f5e..e4ad7d26ca 100644
--- a/civicrm/ext/afform/core/info.xml
+++ b/civicrm/ext/afform/core/info.xml
@@ -12,8 +12,8 @@
     <url desc="Chat">https://chat.civicrm.org/civicrm/channels/dev-afform</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <compatibility>
     <ver>5.69</ver>
   </compatibility>
diff --git a/civicrm/ext/afform/html/info.xml b/civicrm/ext/afform/html/info.xml
index dada7a31a7..0daf5d41e7 100644
--- a/civicrm/ext/afform/html/info.xml
+++ b/civicrm/ext/afform/html/info.xml
@@ -12,8 +12,8 @@
     <url desc="Chat">https://chat.civicrm.org/civicrm/channels/dev-afform</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>alpha</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/afform/mock/info.xml b/civicrm/ext/afform/mock/info.xml
index 795b178102..870bcec37d 100644
--- a/civicrm/ext/afform/mock/info.xml
+++ b/civicrm/ext/afform/mock/info.xml
@@ -11,8 +11,8 @@
   <urls>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
diff --git a/civicrm/ext/authx/info.xml b/civicrm/ext/authx/info.xml
index 5f0ef54f69..0710e14f5d 100644
--- a/civicrm/ext/authx/info.xml
+++ b/civicrm/ext/authx/info.xml
@@ -14,8 +14,8 @@
     <url desc="Issues">https://lab.civicrm.org/dev/core/-/issues</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>mgmt:required</tag>
diff --git a/civicrm/ext/civi_campaign/info.xml b/civicrm/ext/civi_campaign/info.xml
index 355cf41c72..f475c91923 100644
--- a/civicrm/ext/civi_campaign/info.xml
+++ b/civicrm/ext/civi_campaign/info.xml
@@ -12,8 +12,8 @@
     <url desc="Documentation">https://docs.civicrm.org/user/en/latest/campaign/what-is-civicampaign/</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>component</tag>
diff --git a/civicrm/ext/civi_case/info.xml b/civicrm/ext/civi_case/info.xml
index f2a0fff4c2..c4e96c16ce 100644
--- a/civicrm/ext/civi_case/info.xml
+++ b/civicrm/ext/civi_case/info.xml
@@ -12,8 +12,8 @@
     <url desc="Documentation">https://docs.civicrm.org/user/en/latest/case-management/what-is-civicase/</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>component</tag>
diff --git a/civicrm/ext/civi_contribute/info.xml b/civicrm/ext/civi_contribute/info.xml
index f01beb6924..5683d01adb 100644
--- a/civicrm/ext/civi_contribute/info.xml
+++ b/civicrm/ext/civi_contribute/info.xml
@@ -12,8 +12,8 @@
     <url desc="Documentation">https://docs.civicrm.org/user/en/latest/contributions/what-is-civicontribute/</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>component</tag>
diff --git a/civicrm/ext/civi_event/info.xml b/civicrm/ext/civi_event/info.xml
index ffcd3900c5..830280d698 100644
--- a/civicrm/ext/civi_event/info.xml
+++ b/civicrm/ext/civi_event/info.xml
@@ -12,8 +12,8 @@
     <url desc="Documentation">https://docs.civicrm.org/user/en/latest/events/what-is-civievent</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>component</tag>
diff --git a/civicrm/ext/civi_mail/info.xml b/civicrm/ext/civi_mail/info.xml
index aa0114e164..75e62746f9 100644
--- a/civicrm/ext/civi_mail/info.xml
+++ b/civicrm/ext/civi_mail/info.xml
@@ -12,8 +12,8 @@
     <url desc="Documentation">https://docs.civicrm.org/user/en/latest/email/what-is-civimail/</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>component</tag>
diff --git a/civicrm/ext/civi_member/info.xml b/civicrm/ext/civi_member/info.xml
index 4499c7671b..41cbf6656c 100644
--- a/civicrm/ext/civi_member/info.xml
+++ b/civicrm/ext/civi_member/info.xml
@@ -12,8 +12,8 @@
     <url desc="Documentation">https://docs.civicrm.org/user/en/latest/membership/what-is-civimember/</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>component</tag>
diff --git a/civicrm/ext/civi_pledge/info.xml b/civicrm/ext/civi_pledge/info.xml
index bb400f8b5f..ec90bd33f6 100644
--- a/civicrm/ext/civi_pledge/info.xml
+++ b/civicrm/ext/civi_pledge/info.xml
@@ -12,8 +12,8 @@
     <url desc="Documentation">https://docs.civicrm.org/user/en/latest/pledges/what-is-civipledge/</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>component</tag>
diff --git a/civicrm/ext/civi_report/info.xml b/civicrm/ext/civi_report/info.xml
index b9fa72fcf4..441a60ee83 100644
--- a/civicrm/ext/civi_report/info.xml
+++ b/civicrm/ext/civi_report/info.xml
@@ -12,8 +12,8 @@
     <url desc="Documentation">https://docs.civicrm.org/user/en/latest/reporting/what-is-civireport/</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>component</tag>
diff --git a/civicrm/ext/civicrm_admin_ui/info.xml b/civicrm/ext/civicrm_admin_ui/info.xml
index df1c2c867c..ffac302142 100644
--- a/civicrm/ext/civicrm_admin_ui/info.xml
+++ b/civicrm/ext/civicrm_admin_ui/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">https://lab.civicrm.org/dev/core/-/issues</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>beta</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/civicrm_search_ui/info.xml b/civicrm/ext/civicrm_search_ui/info.xml
index f025e92455..f11e9875e5 100644
--- a/civicrm/ext/civicrm_search_ui/info.xml
+++ b/civicrm/ext/civicrm_search_ui/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">https://lab.civicrm.org/dev/core/-/issues</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>alpha</develStage>
   <requires>
     <ext>org.civicrm.search_kit</ext>
diff --git a/civicrm/ext/civigrant/info.xml b/civicrm/ext/civigrant/info.xml
index f66d8f9941..90b59d8318 100644
--- a/civicrm/ext/civigrant/info.xml
+++ b/civicrm/ext/civigrant/info.xml
@@ -12,8 +12,8 @@
     <url desc="Documentation">https://docs.civicrm.org/user/en/latest/grants/what-is-civigrant/</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/civiimport/info.xml b/civicrm/ext/civiimport/info.xml
index 968163f372..cb27f39157 100644
--- a/civicrm/ext/civiimport/info.xml
+++ b/civicrm/ext/civiimport/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">http://civicrm.org</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>alpha</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/ckeditor4/info.xml b/civicrm/ext/ckeditor4/info.xml
index b1a57717b6..466f3a700e 100644
--- a/civicrm/ext/ckeditor4/info.xml
+++ b/civicrm/ext/ckeditor4/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">https://github.com/civicrm/civicrm-core/</url>
     <url desc="Licensing">https://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/contributioncancelactions/info.xml b/civicrm/ext/contributioncancelactions/info.xml
index 25113898e1..ff383a0753 100644
--- a/civicrm/ext/contributioncancelactions/info.xml
+++ b/civicrm/ext/contributioncancelactions/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">http://civicrm.org</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/elavon/info.xml b/civicrm/ext/elavon/info.xml
index b1c63a816a..3d72126b02 100644
--- a/civicrm/ext/elavon/info.xml
+++ b/civicrm/ext/elavon/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">https://lab.civicrm.org</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/eventcart/info.xml b/civicrm/ext/eventcart/info.xml
index 7df0d6a3b4..842f1b76b5 100644
--- a/civicrm/ext/eventcart/info.xml
+++ b/civicrm/ext/eventcart/info.xml
@@ -12,8 +12,8 @@
     <url desc="Main Extension Page">https://github.com/civicrm/civicrm-core/tree/master/ext/eventcart</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
diff --git a/civicrm/ext/ewaysingle/info.xml b/civicrm/ext/ewaysingle/info.xml
index 99447186b4..8a22ea3da7 100644
--- a/civicrm/ext/ewaysingle/info.xml
+++ b/civicrm/ext/ewaysingle/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">https://github.com/civicrm/civicrm-core/blob/master/ext/ewaysingle</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
diff --git a/civicrm/ext/financialacls/info.xml b/civicrm/ext/financialacls/info.xml
index e2d4b8dfab..3c2745b6fe 100644
--- a/civicrm/ext/financialacls/info.xml
+++ b/civicrm/ext/financialacls/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">http://FIXME</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/flexmailer/info.xml b/civicrm/ext/flexmailer/info.xml
index 036f9bb28d..62cdc79857 100644
--- a/civicrm/ext/flexmailer/info.xml
+++ b/civicrm/ext/flexmailer/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">http://civicrm.stackexchange.com/</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <comments>
     FlexMailer is an email delivery engine which replaces the internal guts
diff --git a/civicrm/ext/greenwich/info.xml b/civicrm/ext/greenwich/info.xml
index 15b35f61e5..b2d25e8e54 100644
--- a/civicrm/ext/greenwich/info.xml
+++ b/civicrm/ext/greenwich/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">http://civicrm.org</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
diff --git a/civicrm/ext/legacycustomsearches/info.xml b/civicrm/ext/legacycustomsearches/info.xml
index 3948283af2..bbb4a50b02 100644
--- a/civicrm/ext/legacycustomsearches/info.xml
+++ b/civicrm/ext/legacycustomsearches/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">http://FIXME</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/message_admin/info.xml b/civicrm/ext/message_admin/info.xml
index b8bd023d17..eeacdb52a8 100644
--- a/civicrm/ext/message_admin/info.xml
+++ b/civicrm/ext/message_admin/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">http://civicrm.org</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>alpha</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/oauth-client/info.xml b/civicrm/ext/oauth-client/info.xml
index e8e73931d5..8ff12cd597 100644
--- a/civicrm/ext/oauth-client/info.xml
+++ b/civicrm/ext/oauth-client/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">https://lab.civicrm.org/dev/core/-/issues</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/payflowpro/info.xml b/civicrm/ext/payflowpro/info.xml
index 5d9b36d5cb..34ac80c3e1 100644
--- a/civicrm/ext/payflowpro/info.xml
+++ b/civicrm/ext/payflowpro/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">https://lab.civicrm.org</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/recaptcha/info.xml b/civicrm/ext/recaptcha/info.xml
index 548b52d5f0..6dc337bc5f 100644
--- a/civicrm/ext/recaptcha/info.xml
+++ b/civicrm/ext/recaptcha/info.xml
@@ -12,8 +12,8 @@
     <url desc="Main Extension Page">https://github.com/civicrm/civicrm-core/tree/master/ext/recaptcha</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
diff --git a/civicrm/ext/scheduled_communications/info.xml b/civicrm/ext/scheduled_communications/info.xml
index 4659c4d5f7..247f4b1135 100644
--- a/civicrm/ext/scheduled_communications/info.xml
+++ b/civicrm/ext/scheduled_communications/info.xml
@@ -12,8 +12,8 @@
     <url desc="Chat">https://chat.civicrm.org/civicrm/channels/search-improvements</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>beta</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/search_kit/info.xml b/civicrm/ext/search_kit/info.xml
index b64fb79767..eb596f7c58 100644
--- a/civicrm/ext/search_kit/info.xml
+++ b/civicrm/ext/search_kit/info.xml
@@ -14,8 +14,8 @@
     <url desc="Issues">https://lab.civicrm.org/dev/report/-/issues</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>stable</develStage>
   <tags>
     <tag>mgmt:required</tag>
diff --git a/civicrm/ext/sequentialcreditnotes/info.xml b/civicrm/ext/sequentialcreditnotes/info.xml
index b64cbf4cfe..741fdef45d 100644
--- a/civicrm/ext/sequentialcreditnotes/info.xml
+++ b/civicrm/ext/sequentialcreditnotes/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">https://lab.civicrm.org/extensions/sequentialcreditnotes</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <tags>
     <tag>mgmt:hidden</tag>
   </tags>
diff --git a/civicrm/ext/standaloneusers/info.xml b/civicrm/ext/standaloneusers/info.xml
index 083bf03bab..f3bdec8bae 100644
--- a/civicrm/ext/standaloneusers/info.xml
+++ b/civicrm/ext/standaloneusers/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">http://FIXME</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>alpha</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/ext/user_dashboard/info.xml b/civicrm/ext/user_dashboard/info.xml
index 20b53aa576..6965e257d1 100644
--- a/civicrm/ext/user_dashboard/info.xml
+++ b/civicrm/ext/user_dashboard/info.xml
@@ -14,8 +14,8 @@
     <url desc="Support">http://FIXME</url>
     <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
   </urls>
-  <releaseDate>2024-01-25</releaseDate>
-  <version>5.69.3</version>
+  <releaseDate>2024-01-30</releaseDate>
+  <version>5.69.4</version>
   <develStage>alpha</develStage>
   <compatibility>
     <ver>5.69</ver>
diff --git a/civicrm/js/Common.js b/civicrm/js/Common.js
index 1b5c08f9d1..dd084d11e1 100644
--- a/civicrm/js/Common.js
+++ b/civicrm/js/Common.js
@@ -570,6 +570,12 @@ if (!CRM.vars) CRM.vars = {};
 
   // Autocomplete based on APIv4 and Select2.
   $.fn.crmAutocomplete = function(entityName, apiParams, select2Options) {
+    function getApiParams() {
+      if (typeof apiParams === 'function') {
+        return apiParams();
+      }
+      return apiParams || {};
+    }
     if (entityName === 'destroy') {
       return $(this).off('.crmEntity').crmSelect2('destroy');
     }
@@ -578,8 +584,7 @@ if (!CRM.vars) CRM.vars = {};
       const $el = $(this).off('.crmEntity');
       let staticItems = getStaticOptions(select2Options.static),
         quickAddLinks = select2Options.quickAdd,
-        multiple = !!select2Options.multiple,
-        key = apiParams.key || 'id';
+        multiple = !!select2Options.multiple;
 
       $el.crmSelect2(_.extend({
         ajax: {
@@ -589,7 +594,7 @@ if (!CRM.vars) CRM.vars = {};
             return {params: JSON.stringify(_.assign({
               input: input,
               page: pageNum || 1
-            }, apiParams))};
+            }, getApiParams()))};
           },
           results: function(data) {
             return {
@@ -615,7 +620,7 @@ if (!CRM.vars) CRM.vars = {};
           if (!idsNeeded.length) {
             callback(multiple ? existing : existing[0]);
           } else {
-            var params = $.extend({}, apiParams || {}, {ids: idsNeeded});
+            var params = $.extend({}, getApiParams(), {ids: idsNeeded});
             CRM.api4(entityName, 'autocomplete', params).then(function (result) {
               callback(multiple ? result.concat(existing) : result[0]);
             });
@@ -663,6 +668,7 @@ if (!CRM.vars) CRM.vars = {};
               const response = data.submissionResponse && data.submissionResponse[0];
               let createdId;
               if (typeof response === 'object') {
+                let key = getApiParams().key || 'id';
                 // Loop through entities created by the afform (there should be only one)
                 Object.keys(response).forEach((entity) => {
                   if (Array.isArray(response[entity]) && response[entity][0] && response[entity][0][key]) {
diff --git a/civicrm/js/version.json b/civicrm/js/version.json
index ff623e264c..0d71536118 100644
--- a/civicrm/js/version.json
+++ b/civicrm/js/version.json
@@ -1 +1 @@
-"5.69.3"
+"5.69.4"
diff --git a/civicrm/release-notes.md b/civicrm/release-notes.md
index 75fc12be41..1252ff831a 100644
--- a/civicrm/release-notes.md
+++ b/civicrm/release-notes.md
@@ -15,6 +15,15 @@ Other resources for identifying changes are:
     * https://github.com/civicrm/civicrm-joomla
     * https://github.com/civicrm/civicrm-wordpress
 
+## CiviCRM 5.69.4
+
+Released January 31, 2024
+
+- **[Synopsis](release-notes/5.69.4.md#synopsis)**
+- **[Bugs resolved](release-notes/5.69.4.md#bugs)**
+- **[Credits](release-notes/5.69.4.md#credits)**
+- **[Feedback](release-notes/5.69.4.md#feedback)**
+
 ## CiviCRM 5.69.3
 
 Released January 26, 2024
diff --git a/civicrm/release-notes/5.69.4.md b/civicrm/release-notes/5.69.4.md
new file mode 100644
index 0000000000..1ee031bfec
--- /dev/null
+++ b/civicrm/release-notes/5.69.4.md
@@ -0,0 +1,47 @@
+# CiviCRM 5.69.4
+
+Released January 31, 2024
+
+- **[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
+
+* **_CiviContribute_: On a contribution page with the "Other" option, there may be Javascript error ([dev/core#4912](https://lab.civicrm.org/dev/core/-/issues/4912): [#29113](https://github.com/civicrm/civicrm-core/pull/29113))**
+* **_CiviContribute_: On a contribution page with the "Other" option, the total may show incorrectly ([dev/core#4917](https://lab.civicrm.org/dev/core/-/issues/4917): [#29115](https://github.com/civicrm/civicrm-core/pull/29115))**
+* **_CiviMember_: On a membership page with an "Auto Renew" option, the checkbox may not initially appear ([#29118](https://github.com/civicrm/civicrm-core/pull/29118))**
+* **_CiviMember_: Informative alert for existing members is not displayed ([dev/core#4939](https://lab.civicrm.org/dev/core/-/issues/4939): [#29112](https://github.com/civicrm/civicrm-core/pull/29112))**
+* **_Form Builder_: Forms with "Yes/No" and autocomplete fail in Firefox ([dev/core#4937](https://lab.civicrm.org/dev/core/-/issues/4937): [#29092](https://github.com/civicrm/civicrm-core/pull/29092))**
+* **_Smarty v3_: Remove inert code that causes fatal ([#29107](https://github.com/civicrm/civicrm-core/pull/29107))**
+* **_View Contact_: Custom fields are incorrectly collapsed by default ([#29091](https://github.com/civicrm/civicrm-core/pull/29091))**
+
+## <a name="credits"></a>Credits
+
+This release was developed by the following authors and reviewers:
+
+Wikimedia Foundation - Eileen McNaughton; University of Waterloo - Patrick Lam; tresero;
+Third Sector Design - William Mortada; Tadpole Collective - Kevin Cristiano; Nicol
+Wistreich; Megaphone Technology Consulting - Jon Goldberg; JMA Consulting - Seamus Lee;
+Humanists UK - Andrew West; Fuzion - Peter Davis; Francesc Bassas i Bullich; Dave D;
+CiviCRM - Tim Otten, Coleman Watts; Christian Wach; Chabadrichmond; Botanical Society of
+America - Rob Brandt; Artful Robot - Rich Lott
+
+## <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/sql/civicrm_data.mysql b/civicrm/sql/civicrm_data.mysql
index d77cb76bf3..9830b28a56 100644
--- a/civicrm/sql/civicrm_data.mysql
+++ b/civicrm/sql/civicrm_data.mysql
@@ -21703,4 +21703,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.69.3';
+UPDATE civicrm_domain SET version = '5.69.4';
diff --git a/civicrm/sql/civicrm_generated.mysql b/civicrm/sql/civicrm_generated.mysql
index 0246c63aed..f1659b6aa0 100644
--- a/civicrm/sql/civicrm_generated.mysql
+++ b/civicrm/sql/civicrm_generated.mysql
@@ -2976,7 +2976,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.69.3',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
+ (1,'Default Domain Name',NULL,'5.69.4',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/ACL/Header.tpl b/civicrm/templates/CRM/ACL/Header.tpl
index fa94c0ae3a..c97015b726 100644
--- a/civicrm/templates/CRM/ACL/Header.tpl
+++ b/civicrm/templates/CRM/ACL/Header.tpl
@@ -13,40 +13,3 @@
 <div class="help">
   <p>{ts 1=$docLink}ACLs allow you to control access to CiviCRM data. An ACL consists of an <strong>Operation</strong> (e.g. 'View' or 'Edit'), a <strong>set of data</strong> that the operation can be performed on (e.g. a group of contacts), and a <strong>Role</strong> that has permission to do this operation. Refer to the %1 for more info.{/ts}</p>
 </div>
-
-{php}
-  $currentStep = $this->get_template_vars('step');
-  $wizard = [
-    'style' => ['barClass' => '', 'showTitle' => FALSE],
-    'currentStepNumber' => $currentStep,
-    'steps' => [
-      [
-        'title' => ts('Manage Roles'),
-        'link' => CRM_Utils_System::url('civicrm/admin/options/acl_role', 'reset=1'),
-        'collapsed' => FALSE,
-        'name' => '',
-      ],
-      [
-        'title' => ts('Assign Users'),
-        'link' => CRM_Utils_System::url('civicrm/acl/entityrole', 'reset=1'),
-        'collapsed' => FALSE,
-        'name' => '',
-      ],
-      [
-        'title' => ts('Manage ACLs'),
-        'link' => CRM_Utils_System::url('civicrm/acl', 'reset=1'),
-        'collapsed' => FALSE,
-        'name' => '',
-      ],
-    ],
-  ];
-  foreach ($wizard['steps'] as $num => &$step) {
-    $step['step'] = $step['valid'] = $step['stepNumber'] = $num + 1;
-    if ($step['stepNumber'] == $currentStep) {
-      $step['link'] = NULL;
-    }
-  }
-  $this->assign('wizard', $wizard);
-{/php}
-
-{include file="CRM/common/WizardHeader.tpl"}
diff --git a/civicrm/templates/CRM/Contact/Page/View/CustomDataView.tpl b/civicrm/templates/CRM/Contact/Page/View/CustomDataView.tpl
index e3b1752516..48608878c8 100644
--- a/civicrm/templates/CRM/Contact/Page/View/CustomDataView.tpl
+++ b/civicrm/templates/CRM/Contact/Page/View/CustomDataView.tpl
@@ -14,7 +14,7 @@
   {assign var="count" value=$customGroupCount%2}
   {if ($count eq $side) or $skipTitle}
     {foreach from=$customValues item=cd_edit key=cvID}
-      <details class="customFieldGroup crm-accordion-wrapper ui-corner-all {$cd_edit.name} crm-custom-set-block-{$customGroupId}" {if !empty($cd_edit.collapse_display)} open{/if}>
+      <details class="customFieldGroup crm-accordion-wrapper ui-corner-all {$cd_edit.name} crm-custom-set-block-{$customGroupId}" {if empty($cd_edit.collapse_display)} open{/if}>
         <summary class="crm-accordion-header crm-master-accordion-header">
           {$cd_edit.title}
         </summary>
diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl
index d44bd2b600..b516313aea 100644
--- a/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl
@@ -16,28 +16,29 @@
 {literal}
   <script type="text/javascript">
 
-    // Putting these functions directly in template so they are available for standalone forms
+    // Putting these functions directly in template for historical reasons.
     function useAmountOther(mainPriceFieldName) {
-     for( i=0; i < document.Main.elements.length; i++ ) {
-        element = document.Main.elements[i];
-        if ( element.type == 'radio' && element.name === mainPriceFieldName ) {
-          if (element.value == '0' ) {
-            element.checked = true;
-          }
-          else {
-            element.checked = false;
+      var currentFocus = CRM.$(':focus');
+      CRM.$('input[name=' + mainPriceFieldName + ']:radio:unchecked').each(
+        function () {
+          if (CRM.$(this).data('is-null-option') !== undefined) {
+            // Triggering this click here because over in Calculate.tpl
+            // a blur action is attached
+            CRM.$(this).prop('checked', true).trigger('click');
           }
         }
-      }
+      );
+      // Copied from `updatePriceSetHighlight()` below which isn't available here.
+      // @todo - consider adding this to the actions assigned in Calculate.tpl
+      CRM.$('#priceset .price-set-row span').removeClass('highlight');
+      CRM.$('#priceset .price-set-row input:checked').parent().addClass('highlight');
+      // Return the focus we blurred earlier.
+      currentFocus.trigger('focus');
+
     }
 
     function clearAmountOther(otherPriceFieldName) {
-      cj('#' + otherPriceFieldName).val('');
-      cj('#' + otherPriceFieldName).blur();
-      // @todo - remove the next 2 lines - they seems to relate to a field that is never present
-      // as amount_other will be (e.g) price_4
-      if (document.Main.amount_other == null) return; // other_amt field not present; do nothing
-      document.Main.amount_other.value = "";
+      cj('#' + otherPriceFieldName).val('').trigger('blur');
     }
 
   </script>
@@ -321,7 +322,7 @@
       var isRecur = cj('input[id="is_recur"]:checked');
 
       var quickConfig = {/literal}'{$quickConfig}'{literal};
-      if (cj("#auto_renew") && quickConfig) {
+      if (cj("#auto_renew").length && quickConfig) {
         showHideAutoRenew(null);
       }
 
diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution/MainMembershipBlock.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution/MainMembershipBlock.tpl
index c4aca65220..ecad3a4821 100644
--- a/civicrm/templates/CRM/Contribute/Form/Contribution/MainMembershipBlock.tpl
+++ b/civicrm/templates/CRM/Contribute/Form/Contribution/MainMembershipBlock.tpl
@@ -134,7 +134,7 @@
       var priceSetName = "price_"+{/literal}'{$membershipFieldID}'{literal};
       var considerUserInput = {/literal}'{$takeUserSubmittedAutoRenew}'{literal};
       if ( memTypeId ) considerUserInput = false;
-      if ( !memTypeId ) memTypeId = cj('input:radio[name='+priceSetName+']:checked').attr('membership-type');
+      if ( !memTypeId ) memTypeId = cj('input:radio[name='+priceSetName+']:checked').data('membership-type-id');
 
       //does this page has only one membership type.
       var renewOptions  = {/literal}{$autoRenewMembershipTypeOptions}{literal};
diff --git a/civicrm/vendor/autoload.php b/civicrm/vendor/autoload.php
index 4d99ded32f..28d164a6de 100644
--- a/civicrm/vendor/autoload.php
+++ b/civicrm/vendor/autoload.php
@@ -9,4 +9,4 @@ if (PHP_VERSION_ID < 50600) {
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInite36d44b412c906747bbbe03020d7d072::getLoader();
+return ComposerAutoloaderInite054b4f8fdcc4ecf2e44fd3dfbe06356::getLoader();
diff --git a/civicrm/vendor/composer/autoload_real.php b/civicrm/vendor/composer/autoload_real.php
index 9ba5ef25b5..c821f4ffbd 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 ComposerAutoloaderInite36d44b412c906747bbbe03020d7d072
+class ComposerAutoloaderInite054b4f8fdcc4ecf2e44fd3dfbe06356
 {
     private static $loader;
 
@@ -24,22 +24,22 @@ class ComposerAutoloaderInite36d44b412c906747bbbe03020d7d072
 
         require __DIR__ . '/platform_check.php';
 
-        spl_autoload_register(array('ComposerAutoloaderInite36d44b412c906747bbbe03020d7d072', 'loadClassLoader'), true, true);
+        spl_autoload_register(array('ComposerAutoloaderInite054b4f8fdcc4ecf2e44fd3dfbe06356', 'loadClassLoader'), true, true);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
-        spl_autoload_unregister(array('ComposerAutoloaderInite36d44b412c906747bbbe03020d7d072', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInite054b4f8fdcc4ecf2e44fd3dfbe06356', 'loadClassLoader'));
 
         $includePaths = require __DIR__ . '/include_paths.php';
         $includePaths[] = get_include_path();
         set_include_path(implode(PATH_SEPARATOR, $includePaths));
 
         require __DIR__ . '/autoload_static.php';
-        call_user_func(\Composer\Autoload\ComposerStaticInite36d44b412c906747bbbe03020d7d072::getInitializer($loader));
+        call_user_func(\Composer\Autoload\ComposerStaticInite054b4f8fdcc4ecf2e44fd3dfbe06356::getInitializer($loader));
 
         $loader->register(true);
 
-        $includeFiles = \Composer\Autoload\ComposerStaticInite36d44b412c906747bbbe03020d7d072::$files;
+        $includeFiles = \Composer\Autoload\ComposerStaticInite054b4f8fdcc4ecf2e44fd3dfbe06356::$files;
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequiree36d44b412c906747bbbe03020d7d072($fileIdentifier, $file);
+            composerRequiree054b4f8fdcc4ecf2e44fd3dfbe06356($fileIdentifier, $file);
         }
 
         return $loader;
@@ -51,7 +51,7 @@ class ComposerAutoloaderInite36d44b412c906747bbbe03020d7d072
  * @param string $file
  * @return void
  */
-function composerRequiree36d44b412c906747bbbe03020d7d072($fileIdentifier, $file)
+function composerRequiree054b4f8fdcc4ecf2e44fd3dfbe06356($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 e75a9e1d8e..476fdac974 100644
--- a/civicrm/vendor/composer/autoload_static.php
+++ b/civicrm/vendor/composer/autoload_static.php
@@ -4,7 +4,7 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInite36d44b412c906747bbbe03020d7d072
+class ComposerStaticInite054b4f8fdcc4ecf2e44fd3dfbe06356
 {
     public static $files = array (
         'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
@@ -713,11 +713,11 @@ class ComposerStaticInite36d44b412c906747bbbe03020d7d072
     public static function getInitializer(ClassLoader $loader)
     {
         return \Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInite36d44b412c906747bbbe03020d7d072::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInite36d44b412c906747bbbe03020d7d072::$prefixDirsPsr4;
-            $loader->prefixesPsr0 = ComposerStaticInite36d44b412c906747bbbe03020d7d072::$prefixesPsr0;
-            $loader->fallbackDirsPsr0 = ComposerStaticInite36d44b412c906747bbbe03020d7d072::$fallbackDirsPsr0;
-            $loader->classMap = ComposerStaticInite36d44b412c906747bbbe03020d7d072::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInite054b4f8fdcc4ecf2e44fd3dfbe06356::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInite054b4f8fdcc4ecf2e44fd3dfbe06356::$prefixDirsPsr4;
+            $loader->prefixesPsr0 = ComposerStaticInite054b4f8fdcc4ecf2e44fd3dfbe06356::$prefixesPsr0;
+            $loader->fallbackDirsPsr0 = ComposerStaticInite054b4f8fdcc4ecf2e44fd3dfbe06356::$fallbackDirsPsr0;
+            $loader->classMap = ComposerStaticInite054b4f8fdcc4ecf2e44fd3dfbe06356::$classMap;
 
         }, null, ClassLoader::class);
     }
diff --git a/civicrm/vendor/composer/installed.php b/civicrm/vendor/composer/installed.php
index 52232eb15b..b36a8b1362 100644
--- a/civicrm/vendor/composer/installed.php
+++ b/civicrm/vendor/composer/installed.php
@@ -3,7 +3,7 @@
         'name' => 'civicrm/civicrm-core',
         'pretty_version' => '5.69.x-dev',
         'version' => '5.69.9999999.9999999-dev',
-        'reference' => 'c3f742eff3a4248f26ed499e5ea61b2deee461e9',
+        'reference' => '4c4fcf6f73d8568b321f5ea40dfba2ce494055b9',
         'type' => 'library',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -40,7 +40,7 @@
         'civicrm/civicrm-core' => array(
             'pretty_version' => '5.69.x-dev',
             'version' => '5.69.9999999.9999999-dev',
-            'reference' => 'c3f742eff3a4248f26ed499e5ea61b2deee461e9',
+            'reference' => '4c4fcf6f73d8568b321f5ea40dfba2ce494055b9',
             'type' => 'library',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
diff --git a/civicrm/xml/version.xml b/civicrm/xml/version.xml
index 36efda8d5e..739101bfbb 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.69.3</version_no>
+  <version_no>5.69.4</version_no>
 </version>
-- 
GitLab