diff --git a/CRM/Contact/BAO/Contact/Location.php b/CRM/Contact/BAO/Contact/Location.php
index 8e7a12f373c6887e4269a3cd0c7aca4fc8a56195..4d7c24eaa2330718a47e11253a81a9d96d7c659d 100644
--- a/CRM/Contact/BAO/Contact/Location.php
+++ b/CRM/Contact/BAO/Contact/Location.php
@@ -45,21 +45,34 @@ class CRM_Contact_BAO_Contact_Location {
    *   Array of display_name, email, location type and location id if found, or (null,null,null, null)
    */
   public static function getEmailDetails($id, $isPrimary = TRUE, $locationTypeID = NULL) {
-    $params = [
-      'location_type_id' => $locationTypeID,
+    $params = array(
       'contact_id' => $id,
-      'return' => ['contact_id.display_name', 'email', 'location_type_id', 'id'],
-    ];
+      'return' => array('display_name', 'email.email'),
+      'api.Email.get' => array(
+        'location_type_id' => $locationTypeID,
+        'sequential' => 0,
+        'return' => array('email', 'location_type_id', 'id'),
+      ),
+    );
     if ($isPrimary) {
-      $params['is_primary'] = 1;
+      $params['api.Email.get']['is_primary'] = 1;
     }
-    $emails = civicrm_api3('Email', 'get', $params);
 
-    if ($emails['count'] > 0) {
-      $email = reset($emails['values']);
-      return [$email['contact_id.display_name'], $email['email'], $email['location_type_id'], $email['id']];
+    $contacts = civicrm_api3('Contact', 'get', $params);
+    if ($contacts['count'] > 0) {
+      $contact = reset($contacts['values']);
+      if ($contact['api.Email.get']['count'] > 0) {
+        $email = reset($contact['api.Email.get']['values']);
+      }
     }
-    return [NULL, NULL, NULL, NULL];
+    $returnParams = array(
+      (isset($contact['display_name'])) ? $contact['display_name'] : NULL,
+      (isset($email['email'])) ? $email['email'] : NULL,
+      (isset($email['location_type_id'])) ? $email['location_type_id'] : NULL,
+      (isset($email['id'])) ? $email['id'] : NULL,
+    );
+
+    return $returnParams;
   }
 
   /**
diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php
index f2588a8b9ba159cdb7e5ea48c9b8c05bfe411a23..adf30b59070d891bb64808110f9b2cad35d9291a 100644
--- a/CRM/Contact/BAO/Query.php
+++ b/CRM/Contact/BAO/Query.php
@@ -2710,7 +2710,7 @@ class CRM_Contact_BAO_Query {
       case 'civicrm_worldregion':
         // We can be sure from the calling function that country will already be joined in.
         // we really don't need world_region - we could use a pseudoconstant for it.
-        return "$side JOIN civicrm_worldregion ON civicrm_country.region_id = civicrm_worldregion.id ";
+        return " $side JOIN civicrm_worldregion ON civicrm_country.region_id = civicrm_worldregion.id ";
 
       case 'civicrm_location_type':
         return " $side JOIN civicrm_location_type ON civicrm_address.location_type_id = civicrm_location_type.id ";
diff --git a/CRM/Contribute/Form/CancelSubscription.php b/CRM/Contribute/Form/CancelSubscription.php
index bd84cdca3c953acbb1dbbb485f2d6ab416809be7..1f32d308bae380b59ebf3193a0ea9a139f963911 100644
--- a/CRM/Contribute/Form/CancelSubscription.php
+++ b/CRM/Contribute/Form/CancelSubscription.php
@@ -62,11 +62,12 @@ class CRM_Contribute_Form_CancelSubscription extends CRM_Contribute_Form_Contrib
 
   /**
    * Set variables up before form is built.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function preProcess() {
     parent::preProcess();
     if ($this->_crid) {
-      $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_crid);
       $this->assign('frequency_unit', $this->_subscriptionDetails->frequency_unit);
       $this->assign('frequency_interval', $this->_subscriptionDetails->frequency_interval);
       $this->assign('amount', $this->_subscriptionDetails->amount);
@@ -97,7 +98,6 @@ class CRM_Contribute_Form_CancelSubscription extends CRM_Contribute_Form_Contrib
         CRM_Core_Error::fatal(ts('The recurring contribution looks to have been cancelled already.'));
       }
       $this->_paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'obj');
-      $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_coid, 'contribution');
 
       $this->assign('frequency_unit', $this->_subscriptionDetails->frequency_unit);
       $this->assign('frequency_interval', $this->_subscriptionDetails->frequency_interval);
diff --git a/CRM/Contribute/Form/ContributionRecur.php b/CRM/Contribute/Form/ContributionRecur.php
index 20aa64f1725f31f199ec74d1de6e668e420a5c0f..f5b26ce4e75a80700cc9fa56d4a8610266fbf2de 100644
--- a/CRM/Contribute/Form/ContributionRecur.php
+++ b/CRM/Contribute/Form/ContributionRecur.php
@@ -100,6 +100,13 @@ class CRM_Contribute_Form_ContributionRecur extends CRM_Core_Form {
    */
   protected $entityFields = [];
 
+  /**
+   * Details of the subscription (recurring contribution) to be altered.
+   *
+   * @var array
+   */
+  protected $subscriptionDetails = [];
+
   /**
    * Explicitly declare the entity api name.
    */
@@ -125,6 +132,8 @@ class CRM_Contribute_Form_ContributionRecur extends CRM_Core_Form {
 
   /**
    * Set variables up before form is built.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function preProcess() {
     $this->setAction(CRM_Core_Action::UPDATE);
@@ -132,7 +141,11 @@ class CRM_Contribute_Form_ContributionRecur extends CRM_Core_Form {
     $this->_crid = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE);
     $this->contributionRecurID = $this->_crid;
     $this->_coid = CRM_Utils_Request::retrieve('coid', 'Integer', $this, FALSE);
+    $this->setSubscriptionDetails();
     $this->setPaymentProcessor();
+    if ($this->getSubscriptionContactID()) {
+      $this->set('cid', $this->getSubscriptionContactID());
+    }
   }
 
   /**
@@ -151,4 +164,35 @@ class CRM_Contribute_Form_ContributionRecur extends CRM_Core_Form {
     }
   }
 
+  /**
+   * Set the subscription details on the form.
+   */
+  protected function setSubscriptionDetails() {
+    if ($this->contributionRecurID) {
+      $this->subscriptionDetails = $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_crid);
+    }
+    elseif ($this->_coid) {
+      $this->subscriptionDetails = $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_coid, 'contribution');
+    }
+  }
+
+  /**
+   * Get details for the recurring contribution being altered.
+   *
+   * @return array
+   */
+  public function getSubscriptionDetails() {
+    return $this->subscriptionDetails;
+  }
+
+  /**
+   * Get the contact ID for the subscription.
+   *
+   * @return int|false
+   */
+  protected function getSubscriptionContactID() {
+    $sub = $this->getSubscriptionDetails();
+    return isset($sub->contact_id) ? $sub->contact_id : FALSE;
+  }
+
 }
diff --git a/CRM/Contribute/Form/UpdateBilling.php b/CRM/Contribute/Form/UpdateBilling.php
index 5a20bd0ef33fc0603ab4e5343586b046f35a5f38..2854e77424cd800c226079a3d590dc12319ce075 100644
--- a/CRM/Contribute/Form/UpdateBilling.php
+++ b/CRM/Contribute/Form/UpdateBilling.php
@@ -45,12 +45,12 @@ class CRM_Contribute_Form_UpdateBilling extends CRM_Contribute_Form_Contribution
 
   /**
    * Set variables up before form is built.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function preProcess() {
     parent::preProcess();
     if ($this->_crid) {
-      $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_crid);
-
       // Are we cancelling a recurring contribution that is linked to an auto-renew membership?
       if ($this->_subscriptionDetails->membership_id) {
         $this->_mid = $this->_subscriptionDetails->membership_id;
@@ -60,7 +60,6 @@ class CRM_Contribute_Form_UpdateBilling extends CRM_Contribute_Form_Contribution
     if ($this->_coid) {
       $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'info');
       $this->_paymentProcessor['object'] = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'obj');
-      $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_coid, 'contribution');
     }
 
     if ($this->_mid) {
diff --git a/CRM/Contribute/Form/UpdateSubscription.php b/CRM/Contribute/Form/UpdateSubscription.php
index 162748d9cf8b34003293d817e1de6a45e46d0c96..0e50b0383cb9b7474a1568df9ec36fd64faff5bb 100644
--- a/CRM/Contribute/Form/UpdateSubscription.php
+++ b/CRM/Contribute/Form/UpdateSubscription.php
@@ -71,15 +71,10 @@ class CRM_Contribute_Form_UpdateSubscription extends CRM_Contribute_Form_Contrib
     parent::preProcess();
     $this->setAction(CRM_Core_Action::UPDATE);
 
-    if ($this->contributionRecurID) {
-      $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->contributionRecurID);
-    }
-
     if ($this->_coid) {
       $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'info');
       // @todo test & replace with $this->_paymentProcessorObj =  Civi\Payment\System::singleton()->getById($this->_paymentProcessor['id']);
       $this->_paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'obj');
-      $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_coid, 'contribution');
       $this->contributionRecurID = $this->_subscriptionDetails->recur_id;
     }
     elseif ($this->contributionRecurID) {
diff --git a/CRM/Event/Form/Registration/Confirm.php b/CRM/Event/Form/Registration/Confirm.php
index 8bebdb1e6ab47a7e09538c6543aa4c8cc3845872..1b35d29fe31ad87732edba464161e1a1d5643ecb 100644
--- a/CRM/Event/Form/Registration/Confirm.php
+++ b/CRM/Event/Form/Registration/Confirm.php
@@ -559,6 +559,7 @@ class CRM_Event_Form_Registration_Confirm extends CRM_Event_Form_Registration {
           $preApprovalParams = $this->_paymentProcessor['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
           $value = array_merge($value, $preApprovalParams);
         }
+        $result = NULL;
 
         if (!empty($value['is_pay_later']) ||
           $value['amount'] == 0 ||
@@ -581,6 +582,16 @@ class CRM_Event_Form_Registration_Confirm extends CRM_Event_Form_Registration {
           if (empty($value['email'])) {
             $value['email'] = CRM_Utils_Array::valueByRegexKey('/^email-/', $value);
           }
+
+          if (is_object($payment)) {
+            // Not quite sure why we don't just user $value since it contains the data
+            // from result
+            // @todo ditch $result & retest.
+            list($result, $value) = $this->processPayment($payment, $value);
+          }
+          else {
+            CRM_Core_Error::fatal($paymentObjError);
+          }
         }
 
         $value['receive_date'] = $now;
@@ -607,44 +618,8 @@ class CRM_Event_Form_Registration_Confirm extends CRM_Event_Form_Registration {
             $isAdditionalAmount = TRUE;
           }
 
-          CRM_Contribute_Form_AbstractEditPayment::formatCreditCardDetails($params[0]);
           //passing contribution id is already registered.
-          $contribution = self::processContribution($this, $value, NULL, $contactID, TRUE, $isAdditionalAmount, $this->_paymentProcessor);
-
-          try {
-            // @todo this should really be if $amount > 0, for pay later we should just load the
-            // manual pseudo processor (0) so there *should* always be a defined processor
-            if (!empty($payment)) {
-              $result = $payment->doPayment($value, 'event');
-              if ($result['payment_status_id'] == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed')) {
-                try {
-                  civicrm_api3('contribution', 'completetransaction', [
-                    'id' => $contribution->id,
-                    'trxn_id' => $result['trxn_id'],
-                    'payment_processor_id' => $this->_paymentProcessor['id'],
-                    'is_transactional' => FALSE,
-                    'fee_amount' => CRM_Utils_Array::value('fee_amount', $result),
-                    'is_email_receipt' => FALSE,
-                    'card_type_id' => CRM_Utils_Array::value('card_type_id', $params[0]),
-                    'pan_truncation' => CRM_Utils_Array::value('pan_truncation', $params[0]),
-                  ]);
-                  // This has now been set to 1 in the DB - declare it here also
-                  $contribution->contribution_status_id = 1;
-                }
-                catch (CiviCRM_API3_Exception $e) {
-                  if ($e->getErrorCode() != 'contribution_completed') {
-                    throw new CRM_Core_Exception('Failed to update contribution in database');
-                  }
-                }
-              }
-            }
-          }
-          catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
-            Civi::log()->error('Payment processor exception: ' . $e->getMessage());
-            CRM_Core_Session::singleton()->setStatus($e->getMessage());
-            CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/event/register', "id={$this->_eventId}"));
-          }
-
+          $contribution = self::processContribution($this, $value, $result, $contactID, $pending, $isAdditionalAmount, $this->_paymentProcessor);
           $value['contributionID'] = $contribution->id;
           $value['contributionTypeID'] = $contribution->financial_type_id;
           $value['receive_date'] = $contribution->receive_date;
@@ -998,9 +973,6 @@ class CRM_Event_Form_Registration_Confirm extends CRM_Event_Form_Registration {
     }
 
     // CRM-20264: fetch CC type ID and number (last 4 digit) and assign it back to $params
-    // @todo remove this once backoffice form is doing it - front end is doing it twice.
-    // (in general they are sharing much more code than they should - anything that truly should be shared
-    // should not be on this class).
     CRM_Contribute_Form_AbstractEditPayment::formatCreditCardDetails($params);
 
     $contribParams = [
diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php
index a8f62440b39b221dfd7077074518326d6a06e1ad..47f71da3eacf855824472ac6665ef962483fa857 100644
--- a/CRM/Upgrade/Form.php
+++ b/CRM/Upgrade/Form.php
@@ -49,6 +49,9 @@ class CRM_Upgrade_Form extends CRM_Core_Form {
 
   /**
    * Minimum php version required to run (equal to or lower than the minimum install version)
+   *
+   * Even though 5.6 is no longer supported, this value is left here for a while
+   * so as not to block stragglers from upgrading.
    */
   const MINIMUM_PHP_VERSION = '5.6';
 
diff --git a/CRM/Upgrade/Incremental/General.php b/CRM/Upgrade/Incremental/General.php
index b9179301f13096ceb10126d269461d132891c66d..67763939fffee382306d4b3411cbb8e60ae737cb 100644
--- a/CRM/Upgrade/Incremental/General.php
+++ b/CRM/Upgrade/Incremental/General.php
@@ -53,7 +53,7 @@ class CRM_Upgrade_Incremental_General {
    *
    * @see install/index.php
    */
-  const MIN_INSTALL_PHP_VER = '5.6';
+  const MIN_INSTALL_PHP_VER = '7.0';
 
   /**
    * Compute any messages which should be displayed before upgrade.
diff --git a/contributor-key.yml b/contributor-key.yml
index 800b607dc76c1362745d2cb62fbecf6aff2a4727..6b5355f3d3943f0dbfc27cd9c4ee33e9c8240785 100644
--- a/contributor-key.yml
+++ b/contributor-key.yml
@@ -516,6 +516,9 @@
   organization: Edinburgh College
   jira        : grahamsmith
 
+- name        : Guillaume Rischard
+  github      : grischard
+
 - github      : guanhuan
   name        : Guanhuan Chen
   organization: CompuCorp
@@ -1224,6 +1227,10 @@
   name        : Rubén Pineda
   organization: iXiam
 
+- github      : scoobird
+  name        : Morgan Robinson
+  organization: Palante
+
 - name        : Steve Binkowski
   jira        : s.bink
 
diff --git a/release-notes.md b/release-notes.md
index b358f699c7443642d6a33b8f8a6ed0626ad256d2..ca34d56942f80085da60b8a43e68331254a11caa 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -15,6 +15,17 @@ Other resources for identifying changes are:
     * https://github.com/civicrm/civicrm-joomla
     * https://github.com/civicrm/civicrm-wordpress
 
+## CiviCRM 5.14.0
+
+Released June 5, 2019
+
+- **[Synopsis](release-notes/5.14.0.md#synopsis)**
+- **[Features](release-notes/5.14.0.md#features)**
+- **[Bugs resolved](release-notes/5.14.0.md#bugs)**
+- **[Miscellany](release-notes/5.14.0.md#misc)**
+- **[Credits](release-notes/5.14.0.md#credits)**
+- **[Feedback](release-notes/5.14.0.md#feedback)**
+
 ## CiviCRM 5.13.5
 
 Released May 29, 2019
diff --git a/release-notes/5.14.0.md b/release-notes/5.14.0.md
new file mode 100644
index 0000000000000000000000000000000000000000..54c293dfe97795ab373ce2c333b2277e144407a5
--- /dev/null
+++ b/release-notes/5.14.0.md
@@ -0,0 +1,539 @@
+# CiviCRM 5.14.0
+
+Released June 5, 2019
+
+- **[Synopsis](#synopsis)**
+- **[Features](#features)**
+- **[Bugs resolved](#bugs)**
+- **[Miscellany](#misc)**
+- **[Credits](#credits)**
+- **[Feedback](#feedback)**
+
+## <a name="synopsis"></a>Synopsis
+
+| *Does this version...?*                                         |         |
+|:--------------------------------------------------------------- |:-------:|
+| Fix security vulnerabilities?                                   |   no    |
+| Change the database schema?                                     | **yes** |
+| Alter the API?                                                  | **yes** |
+| Require attention to configuration options?                     |   no    |
+| Fix problems installing or upgrading to a previous version?     | **yes** |
+| Introduce features?                                             | **yes** |
+| Fix bugs?                                                       | **yes** |
+
+## <a name="features"></a>Features
+
+### Core CiviCRM
+
+- **Minimum supported PHP version is 7.0
+  ([14437](https://github.com/civicrm/civicrm-core/pull/14437))**
+
+  CiviCRM now requires PHP 7.0 or higher.  While sites running PHP 5.6 will be
+  able to upgrade to CiviCRM 5.14.0, they will see an error message saying it is
+  no longer supported.  Upcoming changes to CiviCRM will not be evaluated with
+  regard to PHP 5.6 compatibility, and new sites installing CiviCRM must have
+  PHP 7.0 or higher.
+
+- **civi.api.prepare - Allow dynamic wrappers (preliminary work for
+  [dev/core#873](https://lab.civicrm.org/dev/core/issues/873):
+  [14047](https://github.com/civicrm/civicrm-core/pull/14047))**
+
+  This furthers the goal of allowing users to A/B test subject lines in Mosaico
+  by allowing extension developers to dynamically wrap an API using a listener
+  for the new civi.api.prepare event.
+
+- **Make DAO fields more consistent
+  ([14072](https://github.com/civicrm/civicrm-core/pull/14072))**
+
+  This PR makes it so the 'where' field is populated regardless of whether
+  import or export is set. The goal of this pull request is to simplify some of
+  the metadata based magic by trying to get the meaning of import & export
+  closer to the original intent.
+
+- **Hook to alter menubar css variables & fix breakpoint in WP
+  ([14135](https://github.com/civicrm/civicrm-core/pull/14135) and
+  [14420](https://github.com/civicrm/civicrm-core/pull/14420))**
+
+  A new hook, `hook_civicrm_getAssetUrl()`, allows modifying parameters for a
+  semi-static asset like a CSS file that takes certain parameters.  The result
+  is that the menu bar is more flexible and configurable, and this fixes a
+  couple of issues with it in WordPress at certain widths.
+
+- **Replace jcalendar instances with datepicker (continues
+  [dev/core#561](https://lab.civicrm.org/dev/core/issues/561):
+  [14150](https://github.com/civicrm/civicrm-core/pull/14150) and
+  [14099](https://github.com/civicrm/civicrm-core/pull/14099))**
+
+  The campaign search form and pledge forms now use datepicker instead of the
+  deprecated jcalendar for choosing dates.
+
+- **Result filter criteria doesn't show IS NULL/IS NOT NULL for operations
+  ([dev/core#865](https://lab.civicrm.org/dev/core/issues/865):
+  [14028](https://github.com/civicrm/civicrm-core/pull/14028))**
+
+  Allow users to access to the operations `IS NULL` and `IS NOT NULL` when
+  choosing report filters for multiselect fields.
+
+- **Allow url to set IS NULL/ NOT NULL in report for operations
+  ([dev/core#876](https://lab.civicrm.org/dev/core/issues/876):
+  [14052](https://github.com/civicrm/civicrm-core/pull/14052))**
+
+  Makes it so that the newly added filter options IS NULL/ NOT NULL (added
+  above) can be passed via URL.
+
+- **Convert dedupe select to select 2 and remove not-used var
+  ([14161](https://github.com/civicrm/civicrm-core/pull/14161))**
+
+  On the Dedupe rules form, the "Field" drop-downs are converted to Select2
+  fields.
+
+- **Add function filterLinks to return an array of links for an entity that can
+  be used by the API / form layer to generate a link
+  ([14112](https://github.com/civicrm/civicrm-core/pull/14112))**
+
+  Adds the function filterLinks (CRM_Core_Action::filterLinks()) which can be
+  used to generate link html client-side. This function is currently unused,
+  added in preparation for CiviCase link refactoring.
+
+- **Add serialize metadata to tag used_for field
+  ([14096](https://github.com/civicrm/civicrm-core/pull/14096))**
+
+  Add schema metadata to the Tag `used_for` field, which is a comma separated
+  list.
+
+- **Cron Status Check - Better hyperlinks
+  ([14085](https://github.com/civicrm/civicrm-core/pull/14085))**
+
+  Improves the System Status check "Cron Not Running" by including a link to
+  most up to date documentation.
+
+- **Add pseudoconstant to UFField dao
+  ([14041](https://github.com/civicrm/civicrm-core/pull/14041))**
+
+  Improves the UFField API by exposing the option list for the field
+  `fieldname`, before this change it was displayed as a text field instead of a
+  select.
+
+- **CQ: Switch core forms to be Entity Forms (continues
+  [dev/core#818](https://lab.civicrm.org/dev/core/issues/818):
+  [13887](https://github.com/civicrm/civicrm-core/pull/13887)))**
+
+  The group settings form now relies upon a standard entity form for the most
+  part, with most fields defined by metadata.  This removes boilerplate code
+  from the specific form function, standardizing the form and facilitating later
+  form improvements that can be made across the board.
+
+### CiviContribute
+
+- **CQ: Refactor Recurring Contribution Forms (continues
+  [dev/core#846](https://lab.civicrm.org/dev/core/issues/846):
+  [13994](https://github.com/civicrm/civicrm-core/pull/13994) and
+  [13993](https://github.com/civicrm/civicrm-core/pull/13993)))**
+
+  This begins the process of converting recurring contribution forms to use
+  entity forms.  It standardizes the way in which the payment processor is
+  loaded in the 3 forms that update subscriptions.
+
+- **Call Payment.create from payment.cancel
+  ([13689](https://github.com/civicrm/civicrm-core/pull/13689))**
+
+  This change improves the Payment.create API by adding handling for negative
+  amounts and moves the Payment.cancel API to use the Payment.create API instead
+  of a bespoke function.
+
+- **Switch to using ContributionRecur.cancel API from CancelSubscription form
+  ([14033](https://github.com/civicrm/civicrm-core/pull/14033))**
+
+  This change makes it so the CancelSubscription Form uses the
+  ContributionRecur.cancel API instead of a bespoke function.
+
+- **Update MasterCard icon to match branding guidelines
+  ([14078](https://github.com/civicrm/civicrm-core/pull/14078))**
+
+  Updates the MasterCard icon to match the MasterCard branding guidelines:
+  https://brand.mastercard.com/brandcenter/mastercard-brand-mark/downloads.html
+
+### CiviEvent
+
+- **Event Cart: add support for the Credit Card type icons
+  ([14175](https://github.com/civicrm/civicrm-core/pull/14175))**
+
+  Adds support for the Credit Card type icons (Amex, visa etc.) to Event Cart
+  registration forms.
+
+### Drupal Integration
+
+- **Drupal Views : relationships for standard groups
+  ([CRM-20006](https://issues.civicrm.org/jira/browse/CRM-20006):
+  [507](https://github.com/civicrm/civicrm-drupal/pull/507))**
+
+  Adds relationships for standard groups to the CiviCRM/Drupal Views
+  integration.
+
+- **Enable contribution views to filter on empty Receipt and Thank You dates
+  ([573](https://github.com/civicrm/civicrm-drupal/pull/573))**
+
+  Adds a view filter on "Thank You date" and "Receipt Date" for empty/not empty
+  which can be used to create a view showing contributions that
+  have/have not been thanked.
+
+## <a name="bugs"></a>Bugs resolved
+
+### Core CiviCRM
+
+- **Email processor filing all emails as .unknown attachments
+  ([dev/core#940](https://lab.civicrm.org/dev/core/issues/940):
+  [14208](https://github.com/civicrm/civicrm-core/pull/14208))**
+
+  Fixes a regression where emails being sent to the email-to-activity mailbox
+  were being saved with a `.unknown` extension and no content.
+
+- **.ics files should be whitelisted as file types for upload
+  ([dev/core#974](https://lab.civicrm.org/dev/core/issues/974):
+  [14531](https://github.com/civicrm/civicrm-core/pull/14351))**
+
+- **Regression - faster activity tab in 5.13 won't sort by source_name now
+  ([dev/core#934](https://lab.civicrm.org/dev/core/issues/934):
+  [14194](https://github.com/civicrm/civicrm-core/pull/14194) and
+  [14204](https://github.com/civicrm/civicrm-core/pull/14204))**
+
+- **Search displaying Related Contacts produces fatal error
+  ([dev/core#1002](https://lab.civicrm.org/dev/core/issues/1002):
+  [14409](https://github.com/civicrm/civicrm-core/pull/14409))**
+
+- **Error randomly appearing when Financial type ACL is enabled
+  ([dev/core#918](https://lab.civicrm.org/dev/core/issues/918):
+  [14165](https://github.com/civicrm/civicrm-core/pull/14165) and
+  [14168](https://github.com/civicrm/civicrm-core/pull/14168))**
+
+  Fixes an undefined index error `Undefined index: status in
+  CRM_Utils_Check_Component_FinancialTypeAcls::checkFinancialAclReport()` when a
+  site has a Financial Type ACL enabled.
+
+- **utf8mb4 warning keeps coming back up
+  ([dev/core#880](https://lab.civicrm.org/dev/core/issues/880):
+  [14123](https://github.com/civicrm/civicrm-core/pull/14123))**
+
+  This change makes it so users only receive the Mysql utf8mb4 status check
+  once, before this change the status check would show up whenever apache was
+  restarted.
+
+- **Fix import primary phone, primary email, add tests
+  ([14043](https://github.com/civicrm/civicrm-core/pull/14043))**
+
+  Fixes a bug where importing a phone (or email) & choosing 'Primary' as the
+  location type resulted in the creation of a duplicate, sometimes with no
+  location type.
+
+- **Fix upgrade message db setting location
+  ([14083](https://github.com/civicrm/civicrm-core/pull/14083))**
+
+  Updates the upgrade message "This database uses InnoDB Full Text Search for
+  optimized searching. The upgrade procedure has not been tested with this
+  feature. You should disable (and later re-enable) the feature by navigating
+  to..." to point to the right settings form "Administer => Customize Data and
+  Screens => Search Preferences" instead of "Administer => System Settings =>
+  Miscellaneous".
+
+- **fix getAddressColumns() to respect tableAlias
+  ([14014](https://github.com/civicrm/civicrm-core/pull/14014))**
+
+  Fixes `CRM_Report_Form::getAddressColumns()` function so that it no longer
+  returns an error when used twice.
+
+- **Ensure edit tooltip is always initially hidden in contact summary block
+  ([14065](https://github.com/civicrm/civicrm-core/pull/14065))**
+
+  Ensures when a user does not have permission to edit a contact summary block,
+  the little "Edit" tooltip is hidden.
+
+- **Skip adding core resources when building assets
+  ([14010](https://github.com/civicrm/civicrm-core/pull/14010))**
+
+- **Advanced Search - Mailing column headers out of alignment
+  ([dev/core#248](https://lab.civicrm.org/dev/core/issues/248):
+  [14134](https://github.com/civicrm/civicrm-core/pull/14134))**
+
+- **Validate queue_id is a positive integer before passing to the BAO
+  ([14355](https://github.com/civicrm/civicrm-core/pull/14355))**
+
+### CiviCase
+
+- **Case activity query performs poorly on large databases
+  ([dev/core#832](https://lab.civicrm.org/dev/core/issues/832):
+  [14086](https://github.com/civicrm/civicrm-core/pull/14086) and
+  [14139](https://github.com/civicrm/civicrm-core/pull/14139))**
+
+  This change enhances performance of CiviCase by streamlining the Case Activity
+  Query which was crashing the server for some sites with large databases by
+  producing too many temporary files.
+
+- **Make `hook_civicrm_pre()` and `hook_civicrm_post()` fire on CaseContacts
+  ([14030](https://github.com/civicrm/civicrm-core/pull/14030))**
+
+### CiviContribute
+
+- **Always bootstrap CRM_Utils_System on legacy IPN endpoint
+  ([dev/drupal#66](https://lab.civicrm.org/dev/drupal/issues/66),
+  [dev/core#973](https://lab.civicrm.org/dev/core/issues/973), and
+  [dev/core#1017](https://lab.civicrm.org/dev/core/issues/1017):
+  [14406](https://github.com/civicrm/civicrm-core/pull/14406) and
+  [14430](https://github.com/civicrm/civicrm-core/pull/14430))**
+
+  This fixes a recent regression where recurring payments would fail to be
+  recorded in CiviCRM if the contribution was set to notify the legacy IPN URL.
+
+- **Create contribution before taking payment, per contribution page workflow
+  ([13837](https://github.com/civicrm/civicrm-core/pull/13837))**
+
+  Makes it so the Contribution is created before the payment processor is
+  called, so there is always a contribution record even if payment fails.
+
+### CiviEvent
+
+- **Event Cart: pass the contactID to fix payment on Stripe
+  ([14173](https://github.com/civicrm/civicrm-core/pull/14173))**
+
+  Fixes a bug where a user could not complete an Event Registration using Event
+  Cart and the Stripe payment processor.
+
+- **Event Cart: fix sending of email receipts
+  ([14170](https://github.com/civicrm/civicrm-core/pull/14170))**
+
+  Fixes fatal error being thrown when an email receipt was sent from an Event
+  Cart registration.
+
+- **Event Cart: honor the allow_same_participant_emails setting
+  ([14174](https://github.com/civicrm/civicrm-core/pull/14174))**
+
+  Ensures the Event Cart Checkout process honors the "allow participants with
+  the same email address".
+
+- **Event Cart: fix start_date formatting in line items during checkout
+  ([14169](https://github.com/civicrm/civicrm-core/pull/14169))**
+
+  Ensures start dates are formatted according to the site's localization
+  preference during the Event Cart checkout process.
+
+- **CRM-18570 When creating a new event using a template the new event screen is
+  taking the default values directly from the custom fields, and not from what's
+  saved in the event template.
+  ([dev/core#553](https://lab.civicrm.org/dev/core/issues/553):
+  [14063](https://github.com/civicrm/civicrm-core/pull/14063))**
+
+### CiviMail
+
+- **Fixed double protocol being added in link by CKEditor.
+  ([14128](https://github.com/civicrm/civicrm-core/pull/14128))**
+
+  Fixes a bug where adding action tokens using the CKEditor link interface, and
+  then editing them could cause double protocols (ex: http://https:// or
+  http://http://).
+
+- **Fix ts() usage in CRM_SMS_Form_Schedule
+  ([14111](https://github.com/civicrm/civicrm-core/pull/14111))**
+
+  Allows translation of the "Send Mass SMS" form.
+
+### CiviMember
+
+- **Batch update membership type
+  ([dev/core#338](https://lab.civicrm.org/dev/core/issues/338):
+  [14064](https://github.com/civicrm/civicrm-core/pull/14064))**
+
+  Fixes a regression when batch updating membership types and copying values
+  down the column.
+
+- **Membership status processor ignoring records where is_override=0
+  ([dev/core#881](https://lab.civicrm.org/dev/core/issues/881):
+  [14059](https://github.com/civicrm/civicrm-core/pull/14059) and
+  [14073](https://github.com/civicrm/civicrm-core/pull/14073))**
+
+- **Back-office membership renewals don't display an on-screen notification
+  ([dev/membership#9](https://lab.civicrm.org/dev/membership/issues/9):
+  [14088](https://github.com/civicrm/civicrm-core/pull/14088))**
+
+- **Fix test / possible live error on submitting credit card renewals
+  ([14412](https://github.com/civicrm/civicrm-core/pull/14412))**
+
+  This resolves intermittent errors in unit tests when renewing memberships due
+  to the `receive_date` not being set.
+
+- **Fixed auto populate of contact reference field on membership signup form
+  ([14077](https://github.com/civicrm/civicrm-core/pull/14077))**
+
+### Drupal Integration
+
+- **Civi 5.12.1 renders Quicksearch unreadable on Drupal 8
+  ([dev/drupal#56](https://lab.civicrm.org/dev/drupal/issues/56):
+  [14080](https://github.com/civicrm/civicrm-core/pull/14080))**
+
+  Fixes a regression where the text in the Quicksearch box was too light to read
+  so that it is darker and easier to read.
+
+- **Don't render an empty mailto link for empty email addresses
+  ([540](https://github.com/civicrm/civicrm-drupal/pull/540))**
+
+  Fixes a bug in Views where an "email address" field set to output as a
+  `mailto:` link would still make a link if no email exists.
+
+- **Column next_sched_contribution should be next_sched_contribution_date for
+  Views integration
+  ([dev/drupal#48](https://lab.civicrm.org/dev/drupal/issues/48):
+  [578](https://github.com/civicrm/civicrm-drupal/pull/578))**
+
+### Joomla Integration
+
+- **Fix bug preventing affecting `cv --user` on Joomla
+  ([13890](https://github.com/civicrm/civicrm-core/pull/13890))**
+
+## <a name="misc"></a>Miscellany
+
+- **Bump php version to match core requirements
+  ([567](https://github.com/civicrm/civicrm-drupal/pull/567))**
+
+- **CiviUnitTestCase - Extract traits to facilitate extension testing
+  (preliminary work for
+  [dev/core#873](https://lab.civicrm.org/dev/core/issues/873):
+  [14044](https://github.com/civicrm/civicrm-core/pull/14044))**
+
+- **Refactor dedupe merger (preliminary work for
+  [dev/core#723](https://lab.civicrm.org/dev/core/issues/723):
+  [14144](https://github.com/civicrm/civicrm-core/pull/14144) and
+  [14146](https://github.com/civicrm/civicrm-core/pull/14146))**
+
+- **Replace all instances of CRM_Core_Fatal with throw new CRM_Core_Exception
+  (continues [dev/core#560](https://lab.civicrm.org/dev/core/issues/560):
+  [14143](https://github.com/civicrm/civicrm-core/pull/14143))**
+
+- **[REF] Add unit tests on contribution recur trxn_id, contribution recur
+  processor id (preparation for
+  [dev/core#830](https://lab.civicrm.org/dev/core/issues/830):
+  [14119](https://github.com/civicrm/civicrm-core/pull/14119))**
+
+- **Remove instances of $dao->free (continues
+  [dev/core#562](https://lab.civicrm.org/dev/core/issues/562):
+  [14070](https://github.com/civicrm/civicrm-core/pull/14070),
+  [14071](https://github.com/civicrm/civicrm-core/pull/14071) and
+  [14037](https://github.com/civicrm/civicrm-core/pull/14037))**
+
+- **(NFC) Add tests of retreiving notes for display on a contact record a…
+  ([14094](https://github.com/civicrm/civicrm-core/pull/14094))**
+
+- **(NFC) Update CRM/Contact to match new coder style
+  ([14022](https://github.com/civicrm/civicrm-core/pull/14022))**
+
+- **(NFC) Minor code style fixes
+  ([14015](https://github.com/civicrm/civicrm-core/pull/14015))**
+
+- **(NFC) Add in unit test checking that E2E test returns sensible contac…
+  ([14076](https://github.com/civicrm/civicrm-core/pull/14076))**
+
+- **NFC Whitespace formatting for formButtons.tpl
+  ([14107](https://github.com/civicrm/civicrm-core/pull/14107))**
+
+- **(NFC) Expand unit test to show off double protocol error
+  ([14141](https://github.com/civicrm/civicrm-core/pull/14141))**
+
+- **[REF] move gathering of location info to relevant function (towards
+  dev/core#723) ([14142](https://github.com/civicrm/civicrm-core/pull/14142))**
+
+- **[REF] Move the qfbug handling to it's own function (towards dev/core#723)
+  ([14140](https://github.com/civicrm/civicrm-core/pull/14140))**
+
+- **[REF] Use events for CMS resource loading
+  ([14131](https://github.com/civicrm/civicrm-core/pull/14131))**
+
+- **[REF] extract getConflicts function
+  ([14148](https://github.com/civicrm/civicrm-core/pull/14148))**
+
+- **[REF] Extract get cfields function
+  ([14151](https://github.com/civicrm/civicrm-core/pull/14151))**
+
+- **[REF] Use variable for menubar height
+  ([14122](https://github.com/civicrm/civicrm-core/pull/14122))**
+
+- **[REF] Clean up CRM_Contribute_Form_SearchTest
+  ([14068](https://github.com/civicrm/civicrm-core/pull/14068))**
+
+- **[REF] Extract formatLocationBlock from import parsing
+  ([14040](https://github.com/civicrm/civicrm-core/pull/14040))**
+
+- **[REF] function extraction in dedupe code
+  ([14157](https://github.com/civicrm/civicrm-core/pull/14157))**
+
+- **[REF] cleanup input parameters on extracted function
+  ([14050](https://github.com/civicrm/civicrm-core/pull/14050))**
+
+- **(REF) Fix accepted params in CustomValueTable::setValues
+  ([14079](https://github.com/civicrm/civicrm-core/pull/14079))**
+
+- **(REF) Fix unnecessary required param in contact BAO update
+  ([14075](https://github.com/civicrm/civicrm-core/pull/14075))**
+
+- **(REF) Reduce redundant code in CRM_Utils_Type::validate
+  ([14011](https://github.com/civicrm/civicrm-core/pull/14011))**
+
+- **REF Replace hardcoded IDs with pseudoconstants in activity form
+  ([14108](https://github.com/civicrm/civicrm-core/pull/14108))**
+
+- **(REF) Standardize UFField create function
+  ([14084](https://github.com/civicrm/civicrm-core/pull/14084))**
+
+- **[REF] calll formatLocationBlock from parent function.
+  ([14105](https://github.com/civicrm/civicrm-core/pull/14105))**
+
+- **(REF; dev/core#873) MailingAB - Migrate "copy winner" logic from JS to PHP
+  ([14045](https://github.com/civicrm/civicrm-core/pull/14045))**
+
+- **REF Don't pass activity object to addCaseActivityLinks
+  ([14110](https://github.com/civicrm/civicrm-core/pull/14110))**
+
+- **REF Use shared function to check inbound email permissions
+  ([14109](https://github.com/civicrm/civicrm-core/pull/14109))**
+
+- **[REF] Follow up tidy up on import fixes
+  ([14117](https://github.com/civicrm/civicrm-core/pull/14117))**
+
+- **Readability cleanup on EventIncome report (towards bringing it under
+  testing) ([13963](https://github.com/civicrm/civicrm-core/pull/13963))**
+
+- **Remove deprecated function
+  ([14039](https://github.com/civicrm/civicrm-core/pull/14039))**
+
+- **Revert "dev/core#553: Creating new event takes value from default value not
+  from saved template for custom fields"
+  ([14121](https://github.com/civicrm/civicrm-core/pull/14121))**
+
+- **Update docblock for doPayment function
+  ([13844](https://github.com/civicrm/civicrm-core/pull/13844))**
+
+- **[cleanup] remove CRM_Report_Form_Extended
+  ([14149](https://github.com/civicrm/civicrm-core/pull/14149))**
+
+## <a name="credits"></a>Credits
+
+This release was developed by the following code authors:
+
+AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Alok Patel; Australian
+Greens - Seamus Lee; Christian Wach; CiviCRM - Coleman Watts, Tim Otten;
+CiviDesk - Yashodha Chaku; Coop SymbioTIC - Mathieu Lutfy; Dave D; Electronic
+Frontier Foundation - Mark Burdett; Freeform Solutions - Herb van den Dool;
+Fuzion - Luke Stewart; Guillaume Rischard; JMA Consulting - Monish Deb; John
+Kingsnorth; Korlon - Stuart Gaston; Lighthouse Design and Consulting - Brian
+Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MJW Consulting -
+Matthew Wire; Pradeep Nayak; Squiffle Consulting - Aidan Saunders; Tadpole
+Collective - Kevin Cristiano; Wikimedia Foundation - Eileen McNaughton
+
+Most authors also reviewed code for this release; in addition, the following
+reviewers contributed their comments:
+
+AGH Strategies - Tommy Bobo; DevApp - Adam Kwiatkowski; Fuzion - Jitendra
+Purohit; JMA Consulting - Joe Murray; Nicol Wistreich; Palante - Morgan
+Robinson; Skvare - Mark Hanna; Tapash Datta;
+
+## <a name="feedback"></a>Feedback
+
+These release notes are edited by Alice Frumin and Andrew Hunt.  If you'd like
+to provide feedback on them, please log in to https://chat.civicrm.org/civicrm
+and contact `@agh1`.
diff --git a/tests/phpunit/CRM/Contact/BAO/ContactTest.php b/tests/phpunit/CRM/Contact/BAO/ContactTest.php
index cc168dfeac4b4d2a1f69ad9d7fc9b291a383651c..2556b203e2fe2d72ac725977e02fea91cbca3cd4 100644
--- a/tests/phpunit/CRM/Contact/BAO/ContactTest.php
+++ b/tests/phpunit/CRM/Contact/BAO/ContactTest.php
@@ -1623,4 +1623,18 @@ class CRM_Contact_BAO_ContactTest extends CiviUnitTestCase {
     $this->contactDelete($contactId);
   }
 
+  /**
+   * Test that contact details are still displayed if no email is present.
+   *
+   * @throws \Exception
+   */
+  public function testContactEmailDetailsWithNoPrimaryEmail() {
+    $params = $this->contactParams();
+    unset($params['email']);
+    $contact = CRM_Contact_BAO_Contact::create($params);
+    $contactId = $contact->id;
+    $result = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactId);
+    $this->assertEquals([$contact->display_name, NULL, NULL, NULL], $result);
+  }
+
 }