diff --git a/assets/src/js/frontend/give-stripe-elements.js b/assets/src/js/frontend/give-stripe-elements.js
index f8ddce85ba..a38a7db83d 100644
--- a/assets/src/js/frontend/give-stripe-elements.js
+++ b/assets/src/js/frontend/give-stripe-elements.js
@@ -308,67 +308,82 @@ class GiveStripeElements {
* @param setupStripeElement
* @param cardElements
*
+ * @unreleased Scrolls Stripe checkout modal into view for all screen sizes.
+ *
* @since 2.8.0
*/
triggerStripeModal( formElement, stripeElements, setupStripeElement, cardElements ) {
- const idPrefixElement = formElement.querySelector( 'input[name="give-form-id-prefix"]' );
- const stripeModalDonateBtn = formElement.querySelector( `#give-stripe-checkout-modal-donate-button-${ idPrefixElement.value }` );
- const cardholderName = formElement.querySelector( 'input[name="card_name"]' );
- const completeCardElements = {};
- let completeCardStatus = false;
-
- cardElements.forEach( ( cardElement ) => {
- completeCardElements.cardName = false;
-
- cardElement.addEventListener( 'ready', ( e ) => {
- completeCardElements[ e.elementType ] = false;
- completeCardElements.cardName = 'card' === e.elementType;
- } );
-
- cardElement.addEventListener( 'change', ( e ) => {
- completeCardElements[ e.elementType ] = e.complete;
- completeCardStatus = Object.values( completeCardElements ).every( ( string ) => {
- return true === string;
- } );
-
- completeCardStatus ? stripeModalDonateBtn.removeAttribute( 'disabled' ) : stripeModalDonateBtn.setAttribute( 'disabled', 'disabled' );
- } );
- } );
-
- if ( null !== cardholderName ) {
- cardholderName.addEventListener( 'keyup', ( e ) => {
- completeCardElements.cardName = '' !== e.target.value;
- completeCardStatus = Object.values( completeCardElements ).every( ( string ) => {
- return true === string;
- } );
- completeCardStatus ? stripeModalDonateBtn.removeAttribute( 'disabled' ) : stripeModalDonateBtn.setAttribute( 'disabled', 'disabled' );
- } );
- }
-
- if ( null !== stripeModalDonateBtn ) {
- // Process donation on the click of the modal donate button.
- stripeModalDonateBtn.addEventListener( 'click', ( e ) => {
- const currentModalDonateBtn = e.target;
- const loadingAnimationElement = currentModalDonateBtn.nextElementSibling;
- const isLegacyForm = stripeModalDonateBtn.getAttribute( 'data-is_legacy_form' );
-
- if ( isLegacyForm ) {
- currentModalDonateBtn.value = give_global_vars.purchase_loading;
- loadingAnimationElement.style.display = 'inline-block';
- } else {
- currentModalDonateBtn.value = '';
- loadingAnimationElement.classList.add( 'sequoia-loader' );
- loadingAnimationElement.classList.add( 'spinning' );
- loadingAnimationElement.classList.remove( 'give-loading-animation' );
- }
-
- // Create Payment Method.
- stripeElements.createPaymentMethod( formElement, setupStripeElement, cardElements );
-
- e.preventDefault();
- } );
- }
- }
+ const idPrefixElement = formElement.querySelector('input[name="give-form-id-prefix"]');
+ const stripeModalDonateBtn = formElement.querySelector(
+ `#give-stripe-checkout-modal-donate-button-${idPrefixElement.value}`
+ );
+ const cardholderName = formElement.querySelector('input[name="card_name"]');
+ const stripeModalContent = document.querySelector('.give-stripe-checkout-modal-container');
+ const purchaseButton = document.querySelector('#give-purchase-button');
+ const completeCardElements = {};
+ let completeCardStatus = false;
+
+ // Scroll checkout modal container into view.
+ purchaseButton.addEventListener('click', function () {
+ stripeModalContent.scrollIntoView({behavior: 'smooth'});
+ });
+
+ cardElements.forEach((cardElement) => {
+ completeCardElements.cardName = false;
+
+ cardElement.addEventListener('ready', (e) => {
+ completeCardElements[e.elementType] = false;
+ completeCardElements.cardName = 'card' === e.elementType;
+ });
+
+ cardElement.addEventListener('change', (e) => {
+ completeCardElements[e.elementType] = e.complete;
+ completeCardStatus = Object.values(completeCardElements).every((string) => {
+ return true === string;
+ });
+
+ completeCardStatus
+ ? stripeModalDonateBtn.removeAttribute('disabled')
+ : stripeModalDonateBtn.setAttribute('disabled', 'disabled');
+ });
+ });
+
+ if (null !== cardholderName) {
+ cardholderName.addEventListener('keyup', (e) => {
+ completeCardElements.cardName = '' !== e.target.value;
+ completeCardStatus = Object.values(completeCardElements).every((string) => {
+ return true === string;
+ });
+ completeCardStatus
+ ? stripeModalDonateBtn.removeAttribute('disabled')
+ : stripeModalDonateBtn.setAttribute('disabled', 'disabled');
+ });
+ }
+
+ if (null !== stripeModalDonateBtn) {
+ // Process donation on the click of the modal donate button.
+ stripeModalDonateBtn.addEventListener('click', (e) => {
+ const currentModalDonateBtn = e.target;
+ const loadingAnimationElement = currentModalDonateBtn.nextElementSibling;
+ const isLegacyForm = stripeModalDonateBtn.getAttribute('data-is_legacy_form');
+
+ if (isLegacyForm) {
+ currentModalDonateBtn.value = give_global_vars.purchase_loading;
+ loadingAnimationElement.style.display = 'inline-block';
+ } else {
+ currentModalDonateBtn.value = '';
+ loadingAnimationElement.classList.add('sequoia-loader');
+ loadingAnimationElement.classList.add('spinning');
+ loadingAnimationElement.classList.remove('give-loading-animation');
+ }
+
+ // Create Payment Method.
+ stripeElements.createPaymentMethod(formElement, setupStripeElement, cardElements);
+
+ e.preventDefault();
+ });
+ }
+ }
}
export { GiveStripeElements };
diff --git a/blocks/donation-form-grid/edit/inspector.js b/blocks/donation-form-grid/edit/inspector.js
index 0381ef0993..9e4bea7896 100644
--- a/blocks/donation-form-grid/edit/inspector.js
+++ b/blocks/donation-form-grid/edit/inspector.js
@@ -213,7 +213,7 @@ const Inspector = ({attributes, setAttributes}) => {
-
+
select {
- line-height: 1.2 !important;
- }
+ > select {
+ line-height: 1.2 !important;
}
}
+}
- .components-form-token-field{
+.give-donation-form-grid--grid-settings {
+ .components-form-token-field {
label { display: none !important;}
}
@@ -59,4 +58,5 @@
margin-top: 20px !important;
border: 1px solid transparent;
}
+}
diff --git a/blocks/donor-wall/edit/inspector.js b/blocks/donor-wall/edit/inspector.js
index 8d99ecb761..7567647afb 100644
--- a/blocks/donor-wall/edit/inspector.js
+++ b/blocks/donor-wall/edit/inspector.js
@@ -225,7 +225,7 @@ const { donorsPerPage,
-
+
select {
- line-height: 1.2 !important;
- }
+ > select {
+ line-height: 1.2 !important;
}
}
+}
-.components-form-token-field {
- margin-top: -18px !important;
- label { display: none !important;}
+.give-wall--wall-settings {
+ .components-form-token-field {
+ margin-top: -18px !important;
+ label { display: none !important;}
+ }
}
diff --git a/includes/class-notices.php b/includes/class-notices.php
index 9cf611167a..bf5979fb71 100644
--- a/includes/class-notices.php
+++ b/includes/class-notices.php
@@ -285,6 +285,7 @@ public function render_admin_notices() {
/**
* Render give frontend notices.
*
+ * @unreleased Display registered error on donation form.
* @since 1.8.9
* @access public
*
@@ -295,8 +296,13 @@ public function render_frontend_notices( $form_id = 0 ) {
$request_form_id = isset( $_REQUEST['form-id'] ) ? absint( $_REQUEST['form-id'] ) : 0;
- // Sanity checks first: Ensure that gateway returned errors display on the appropriate form.
- if ( ! isset( $_POST['give_ajax'] ) && $request_form_id !== $form_id ) {
+ // Sanity checks first:
+ // - Ensure that gateway returned errors display on the appropriate form.
+ // - Error should not display on AJAX request.
+ if (
+ isset( $_POST['give_ajax'] )
+ || ( $request_form_id && $request_form_id !== $form_id )
+ ) {
return;
}
diff --git a/src/Framework/FieldsAPI/Concerns/NameCollision.php b/src/Framework/FieldsAPI/Concerns/NameCollision.php
index dccb7bf66d..2fba1d7caa 100644
--- a/src/Framework/FieldsAPI/Concerns/NameCollision.php
+++ b/src/Framework/FieldsAPI/Concerns/NameCollision.php
@@ -26,14 +26,15 @@ public function checkNameCollisionDeep(Node $node)
}
/**
+ * @unreleased add existing and incoming nodes to exception
* @since 2.10.2
*
* @throws NameCollisionException
*/
public function checkNameCollision(Node $node)
{
- if ($this->getNodeByName($node->getName())) {
- throw new NameCollisionException($node->getName());
+ if ($existingNode = $this->getNodeByName($node->getName())) {
+ throw new NameCollisionException($node->getName(), $existingNode, $node);
}
}
}
diff --git a/src/Framework/FieldsAPI/Exceptions/NameCollisionException.php b/src/Framework/FieldsAPI/Exceptions/NameCollisionException.php
index 54d5645518..579b60608d 100644
--- a/src/Framework/FieldsAPI/Exceptions/NameCollisionException.php
+++ b/src/Framework/FieldsAPI/Exceptions/NameCollisionException.php
@@ -3,15 +3,64 @@
namespace Give\Framework\FieldsAPI\Exceptions;
use Give\Framework\Exceptions\Primitives\Exception;
+use Give\Framework\FieldsAPI\Contracts\Node;
/**
+ * @unreleased add existing and incoming nodes to exception
* @since 2.10.2
*/
class NameCollisionException extends Exception
{
- public function __construct($name, $code = 0, Exception $previous = null)
+ /**
+ * @var string
+ */
+ protected $nodeNameCollision;
+ /**
+ * @var Node
+ */
+ protected $existingNode;
+ /**
+ * @var Node
+ */
+ protected $incomingNode;
+
+ public function __construct(
+ string $name,
+ Node $existingNode,
+ Node $incomingNode,
+ int $code = 0,
+ Exception $previous = null
+ )
{
+ $this->nodeNameCollision = $name;
+ $this->existingNode = $existingNode;
+ $this->incomingNode = $incomingNode;
+
$message = "Node name collision for $name";
parent::__construct($message, $code, $previous);
}
+
+ /**
+ * @unreleased
+ */
+ public function getNodeNameCollision(): string
+ {
+ return $this->nodeNameCollision;
+ }
+
+ /**
+ * @unreleased
+ */
+ public function getIncomingNode(): Node
+ {
+ return $this->incomingNode;
+ }
+
+ /**
+ * @unreleased
+ */
+ public function getExistingNode(): Node
+ {
+ return $this->existingNode;
+ }
}
diff --git a/src/Framework/PaymentGateways/Traits/HandleHttpResponses.php b/src/Framework/PaymentGateways/Traits/HandleHttpResponses.php
index 7d061aa416..36d0846a97 100644
--- a/src/Framework/PaymentGateways/Traits/HandleHttpResponses.php
+++ b/src/Framework/PaymentGateways/Traits/HandleHttpResponses.php
@@ -11,6 +11,7 @@ trait HandleHttpResponses
/**
* Handle Response
*
+ * @unreleased added check for responding with json
* @since 2.27.0 add support for json content-type
* @since 2.18.0
*
@@ -19,7 +20,7 @@ trait HandleHttpResponses
public function handleResponse($type)
{
if ($type instanceof RedirectResponse) {
- if (isset($_SERVER['CONTENT_TYPE']) && str_contains($_SERVER['CONTENT_TYPE'], "application/json")) {
+ if ($this->wantsJson()) {
wp_send_json([
'type' => 'redirect',
'data' => [
@@ -60,4 +61,20 @@ public function handleExceptionResponse(\Exception $exception, string $message)
give_set_error('PaymentGatewayException', $message);
give_send_back_to_checkout();
}
+
+ /**
+ * This checks the server headers for 'application/json' to determine if it should respond with json.
+ *
+ * @unreleased
+ *
+ * @return bool
+ */
+ protected function wantsJson(): bool
+ {
+ if (isset($_SERVER['HTTP_ACCEPT']) && str_contains($_SERVER['HTTP_ACCEPT'], 'application/json')) {
+ return true;
+ }
+
+ return isset($_SERVER['CONTENT_TYPE']) && str_contains($_SERVER['CONTENT_TYPE'], 'application/json');
+ }
}
diff --git a/src/PaymentGateways/PayPalCommerce/ScriptLoader.php b/src/PaymentGateways/PayPalCommerce/ScriptLoader.php
index 72f05332ee..0430861564 100644
--- a/src/PaymentGateways/PayPalCommerce/ScriptLoader.php
+++ b/src/PaymentGateways/PayPalCommerce/ScriptLoader.php
@@ -133,6 +133,7 @@ function givePayPalOnBoardedCallback(mode, authCode, sharedId) {
/**
* Load public assets.
*
+ * @unreleased Handle exception if client token is not generated.
* @since 2.9.0
*/
public function loadPublicAssets()
@@ -144,6 +145,22 @@ public function loadPublicAssets()
/* @var MerchantDetail $merchant */
$merchant = give(MerchantDetail::class);
$scriptId = 'give-paypal-commerce-js';
+ $clientToken = '';
+
+ try{
+ $clientToken = $this->merchantRepository->getClientToken();
+ } catch ( \Exception $exception ) {
+ give_set_error(
+ 'give-paypal-commerce-client-token-error',
+ sprintf(
+ esc_html__(
+ 'Unable to load PayPal Commerce client token. Please try again later. Error: %1$s',
+ 'give'
+ ),
+ $exception->getMessage()
+ )
+ );
+ }
/**
* List of PayPal query parameters: https://developer.paypal.com/docs/checkout/reference/customize-sdk/#query-parameters
@@ -156,7 +173,7 @@ public function loadPublicAssets()
'disable-funding' => 'credit',
'vault' => true,
'data-partner-attribution-id' => give('PAYPAL_COMMERCE_ATTRIBUTION_ID'),
- 'data-client-token' => $this->merchantRepository->getClientToken(),
+ 'data-client-token' => $clientToken,
];
if (give_is_setting_enabled(give_get_option('paypal_commerce_accept_venmo', 'disabled'))) {