@automattic/newspack-blocks 4.26.8 → 4.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # @automattic/newspack-blocks [4.27.0](https://github.com/Automattic/newspack-workspace/compare/newspack-blocks@4.26.8...newspack-blocks@4.27.0) (2026-07-08)
2
+
3
+
4
+ ### Features
5
+
6
+ * **gates:** capture gate id on landing-page conversions (NPPD-1887) ([#575](https://github.com/Automattic/newspack-workspace/issues/575)) ([20e84f8](https://github.com/Automattic/newspack-workspace/commit/20e84f87dc1be96b5575548593fd0f7e77da52e6))
7
+
1
8
  ## @automattic/newspack-blocks [4.26.8](https://github.com/Automattic/newspack-workspace/compare/newspack-blocks@4.26.7...newspack-blocks@4.26.8) (2026-07-02)
2
9
 
3
10
 
@@ -1 +1 @@
1
- <?php return array('dependencies' => array(), 'version' => '11e14e1094388baf7705');
1
+ <?php return array('dependencies' => array(), 'version' => '9ede98c6b50c49ec1720');
package/dist/modal.js CHANGED
@@ -1 +1 @@
1
- (()=>{"use strict";function e(e,t=!1){const n=e.querySelectorAll('button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])');if(0===n.length)return!1;const o=n?.[0];let c;o.focus(),t?(document.addEventListener("keydown",function(e){if(("Tab"===e.key||9===e.keyCode)&&e.shiftKey&&document.activeElement===o){const n=t.contentWindow.document,o=n.getElementById("checkout-after-success");c=null!==o?o:n.getElementById("checkout_cancel"),c.focus(),e.preventDefault()}}),document.getElementById("newspack-a11y-last-element").addEventListener("focus",()=>{o.focus()})):(c=n[n.length-1],document.addEventListener("keydown",function(e){("Tab"===e.key||9===e.keyCode)&&(e.shiftKey?document.activeElement===o&&(c.focus(),e.preventDefault()):document.activeElement===c&&(o.focus(),e.preventDefault()))}))}const t=(e,t={})=>({...t,action:e}),n=["action","action_type","amount","currency","product_id","product_type","variation_id","variation_ids","is_variable","is_grouped","child_ids","price_summary","newspack_popup_id","prompt_title","gate_post_id","recurrence","referrer"],o=(e,t="np_modal_checkout_interaction")=>{if("function"==typeof window.gtag&&e){const o={};for(const t of n)e[t]&&("boolean"==typeof e[t]?e[t]=e[t]?"yes":"no":"true"===e[t]?e[t]="yes":"false"===e[t]&&(e[t]="no"),o[t]=e[t].toString());window.gtag("event",t,o)}},c=e=>{if("function"!=typeof window.gtag)return;let n="opened";const{amount:c="",is_variable:a="",is_grouped:r="",price:i="",variation_id:s=""}=e,l={...e};(c||i)&&(l.amount=c||i),a&&!s&&(n="opened_variations"),r&&(n="opened_grouped");const u=t(n,l);o(u)};function a(e,t=null){const n=document.createElement("input");return n.type="hidden",n.name=e,t&&(n.value=t),n}function r(e){e.requestSubmit(e.querySelector('button[type="submit"]'))}function i(e){const t="string"==typeof e?document.getElementById(e):e;if(!t)return console.warn("No container found for checkout data"),{};let n={};if("FORM"===t.tagName){const e=new FormData(t);n=Object.fromEntries(e)}const o=t.dataset.checkout;if(!o)return console.warn("No checkout data found"),n;try{n={...n,...JSON.parse(o)}}catch(e){console.warn("Error parsing checkout data")}const c=new URL(t.ownerDocument.defaultView.location.href);return c.searchParams.get("action_type")&&(n.action_type=c.searchParams.get("action_type")),n}const s=e=>{if("function"!=typeof window.gtag)return;e=e||i("newspack_modal_checkout");const n=t("dismissed",e);o(n)};function l(e){const t=e&&e.dataset?e.dataset.checkout:null;if(!t)return null;try{return JSON.parse(t)}catch(e){return null}}function u(e,t,n=null){const o=e.querySelectorAll(".wp-block-newspack-blocks-checkout-button"),c=null!=n&&""!==String(n);let a=null;return o.forEach(e=>{if(a)return;const o=e.querySelector("form"),r=l(o);r&&String(r.product_id)===String(t)&&(c&&String(r.variation_id)!==String(n)||(a=o))}),a}const d=["after_success_behavior","after_success_url","after_success_button_label","gate_post_id","newspack_popup_id","prompt_title"];const p=newspackBlocksModal.newspack_class_prefix,m="newspack_modal_checkout_iframe",_="newspack_modal_checkout_container",h=`${p}__modal`,k="newspack-blocks__modal-variation",y=`${p}__processing-payment-text`,w=newspackBlocksModal.processing_payment_messages;let f=!1,v=newspackBlocksModal.labels.checkout_modal_title,g=null;const b=e=>{e.overlayId&&window.newspackReaderActivation?.overlays&&window.newspackReaderActivation?.overlays.remove(e.overlayId),e.setAttribute("data-state","closed"),document.body.style.overflow="auto"};window.onpageshow=e=>{e.persisted&&(document.querySelectorAll(".modal-processing").forEach(e=>e.classList.remove("modal-processing")),document.querySelectorAll(".non-modal-checkout-loading").forEach(e=>e.classList.remove("non-modal-checkout-loading")),document.querySelectorAll(`.${h}-container`).forEach(e=>b(e)))};const S=new CustomEvent("checkout-closed");var E;window.newspackRAS=window.newspackRAS||[],E=()=>{const t=document.querySelector("#newspack_modal_checkout");if(!t)return;const n=t.querySelector(`.${h}__content`),o=a("modal_checkout","1"),E=n.querySelector(`.${p}__spinner`);let A=document.querySelector(".newspack-reader__account-link")?.[0];const q="600px",L=document.createElement("iframe");L.name=m,L.style.height=q,L.style.visibility="hidden";const $=e=>{B.observe(e),"none"!==E.style.display&&(E.style.display="none"),"visible"!==L.style.visibility&&(L.style.visibility="visible"),L._ready=!0};!function(e,t,n){e._ready=!1,e._readyTimer&&clearTimeout(e._readyTimer);let o=!1,c="";function a(){o||(o=!0,clearTimeout(e._readyTimer),t.call(this))}function r(){"complete"===this.readyState&&a.call(this)}function i(){e._ready=!1,n&&n(),e._readyTimer&&clearTimeout(e._readyTimer);const t=e.contentDocument||e.contentWindow?.document;t&&(t.removeEventListener("DOMContentLoaded",a),t.removeEventListener("readystatechange",r))}function s(){if(e._ready)return void i();const t=e.contentDocument||e.contentWindow?.document;t&&0!==t.URL.indexOf("about:")?"complete"===t?.readyState?a.call(t):(t.addEventListener("DOMContentLoaded",a),t.addEventListener("readystatechange",r)):e._readyTimer=setTimeout(s,10)}e._observer||(e._observer=new MutationObserver(e=>{e.forEach(e=>{"attributes"===e.type&&"src"===e.attributeName&&(o=!1,i(),s())})}),e._observer.observe(e,{attributes:!0})),e._locationObserver||(e._locationObserver=setInterval(function(){const t=e.contentDocument||e.contentWindow?.document;t&&t.URL!==c&&(c=t.URL,o=!1,i(),s())},50)),s()}(L,function(){const n=L.contentWindow?.location;if(window.newspackReaderActivation&&n?.href?.includes("order-received")){const e=window.newspackReaderActivation,t=new Proxy(new URLSearchParams(n.search),{get:(e,t)=>e.get(t)});t.email&&(e.setReaderEmail(t.email),e.setAuthenticated(!0))}const o=L?.contentDocument?.querySelector(`#${_}`);if(!o)return;const c=o.querySelector("#modal-checkout-product-details"),a=i(c),r=document.createElement("p");r.classList.add(y);let s=[];const l=()=>{s.forEach(e=>clearTimeout(e)),s=[]},u=()=>{E.style.display="none",l(),E.querySelectorAll(`.${y}`).forEach(e=>e.remove())};!function(e,t){e.checkoutCancel?t():e.addEventListener("checkout-cancel",t)}(o,()=>{P()}),function(e,t){e.checkoutPlaceOrderStart?t():e.addEventListener("checkout-place-order-start",t)}(o,e=>{E.querySelectorAll(`.${y}`).forEach(e=>e.remove()),E.style.display="flex",l(),r.textContent=w[0]?.text??"",w.slice(1).forEach(({text:t,delay:n})=>{const o=setTimeout(()=>{e.target.dispatchEvent(new CustomEvent("checkout-place-order-processing")),r.textContent=t},n);s.push(o)}),E.appendChild(r)}),function(e,t){e.checkoutPlaceOrderError?t():e.addEventListener("checkout-place-order-error",t)}(o,u),function(e,t){e.checkoutReady?t():e.addEventListener("checkout-ready",t)}(o,()=>{const e=c?.querySelector("strong");if(e&&(e.textContent=a.price_summary),t.initialErrors){const e=document.createElement("div");e.classList.add("woocommerce-error"),e.textContent=t.initialErrors,o.prepend(e),delete t.initialErrors}N(),T(v),L.contentWindow?.newspackBlocksModalCheckout?.checkout_nonce&&(t.checkout_nonce=L.contentWindow.newspackBlocksModalCheckout.checkout_nonce),$(o)}),function(e,t){e.checkoutComplete?t():e.addEventListener("checkout-complete",t)}(o,()=>{window.newspackRAS.push(["checkout_completed",a]),window?.newspackReaderActivation?.refreshNewslettersSignupModal&&window?.newspackReaderActivation?.getReader()?.email&&window.newspackReaderActivation.refreshNewslettersSignupModal(window.newspackReaderActivation.getReader().email),N("small"),T(newspackBlocksModal.labels.thankyou_modal_title),$(o),e(t.querySelector(`.${h}`)),u()}),function(e,t){e.checkoutPlaceOrderCriticalError?t():e.addEventListener("checkout-place-order-critical-error",t)}(o,()=>{var e;(e=g)&&(P(),E.style.display="none",t.initialErrors=newspackBlocksModal.labels.critical_error,e.requestSubmit(e.querySelector('button[type="submit"]')),u())})},()=>{E.style.display="flex"});const R=e=>new Promise((t,n)=>{const o=new URLSearchParams(e);o.append("action","modal_checkout_request"),fetch(newspackBlocksModal.ajax_url+"?"+o.toString()).then(e=>{e.ok||n(e),e.json().then(e=>{t(e.url)}).catch(n)}).catch(n)}),M=()=>"undefined"!=typeof newspack_ras_config&&!newspack_ras_config?.is_logged_in&&!window?.newspackReaderActivation?.getReader?.()?.authenticated&&newspackBlocksModal?.is_registration_required&&window?.newspackReaderActivation?.openAuthModal,C=t=>{const n=!newspackBlocksModal.has_unsupported_payment_gateway;n||t.preventDefault();const o=t.target;o.classList.add("modal-processing");const u=i(o);if(u.newspack_donate){const e=u.donation_frequency,t=[...o.querySelectorAll(`.donation-tier__${e}, .donation-frequency__${e}`)],n=u.donation_tier_index;let c,a;n?(c=t[n],a=u[`donation_value_${e}`]):(c=t[0],u[`donation_value_${e}_untiered`]?a=u[`donation_value_${e}_untiered`]:(a=u[`donation_value_${e}`],"other"===a&&(a=u[`donation_value_${e}_other`])));const r=i(c);for(const e in r)u[e]=r[e];u.amount=a,u.price_summary=u.summary_template.replace("{{PRICE}}",function(e,t="USD"){return parseFloat(e).toLocaleString(document.documentElement.lang,{style:"currency",currency:t,currencyDisplay:"narrowSymbol"})}(u.amount,u.currency))}u&&Object.keys(u).forEach(e=>{0===o.querySelectorAll('input[name="'+e+'"]').length&&o.prepend(a(e,u[e]))}),u.variation_id||(A=t.submitter);const d=document.querySelectorAll(`.${k}`);if(d.forEach(e=>{(M()||n)&&b(e)}),u.is_grouped||u.is_variable&&!u.variation_id){const n=[...d].find(e=>e.dataset.productId===u.product_id);if(n)return n.querySelectorAll(`form[target="${m}"]`).forEach(e=>{["after_success_behavior","after_success_url","after_success_button_label","gate_post_id","newspack_popup_id","prompt_title"].forEach(t=>{0===e.querySelectorAll('input[name="'+t+'"]').length&&e.prepend(a(t,u[t]))});const t=l(e);t&&Object.keys(t).forEach(n=>{0===e.querySelectorAll('input[name="'+n+'"]').length&&e.prepend(a(n,t[n]))})}),t.preventDefault(),o.classList.remove("modal-processing"),O(n),e(n,!1),f||c(u),void document.getElementById("newspack_modal_checkout").setAttribute("data-checkout",JSON.stringify(u))}if(n||M()){if(o.classList.remove("modal-processing"),f||c(u),f=!0,M()){t.preventDefault();const e=u.price_summary,o=e?`<div class="order-details-summary ${p}__box ${p}__box--text-center"><p><strong>${e}</strong></p></div>`:"",c=R(u);c.then(e=>{window.newspackReaderActivation?.setPendingCheckout?.(e)}),window.newspackReaderActivation.openAuthModal({title:newspackBlocksModal.labels.auth_modal_title,onSuccess:(e,t)=>{c.then(e=>{t?.registered&&n&&(e+=`&${newspackBlocksModal.checkout_registration_flag}=1`),n?r(x(e)):R(u).then(window.location.href=e)}).catch(e=>{console.warn("Unable to generate cart:",e),P()})},onError:()=>{P()},onDismiss:()=>{s(u),f=!1,document.getElementById("newspack_modal_checkout").removeAttribute("data-checkout")},skipSuccess:!0,skipNewslettersSignup:!0,labels:{signin:{title:newspackBlocksModal.labels.signin_modal_title},register:{title:newspackBlocksModal.labels.register_modal_title}},content:o,trigger:t.submitter,closeOnSuccess:n})}else D(),document.getElementById("newspack_modal_checkout").setAttribute("data-checkout",JSON.stringify(u));g=o}else R(u).then(e=>{window.location.href=e}),(!u.is_variable||u.variation_id)&&o.querySelectorAll("button[type=submit]:focus").forEach(e=>{e.classList.add("non-modal-checkout-loading");const t=e.innerHTML;e.innerHTML="<span>"+t+"</span>"})},x=e=>{const t=document.createElement("form");t.method="POST",t.action=e,t.target=m,t.style.display="none";const n=document.createElement("button");return n.setAttribute("type","submit"),t.appendChild(n),document.body.appendChild(t),t.addEventListener("submit",C),t},B=new ResizeObserver(e=>{if(!e||!e.length)return;if(!L.contentDocument)return;const o=e[0].contentRect;if(o){const e=.01*Math.max(document.documentElement.clientHeight,window.innerHeight||0)*90-(t.querySelector(`.${h}__header`)?.offsetHeight||0),c=o.top+o.bottom,a=Math.min(c,e);if(0===a)return void(L.style.visibility="hidden");n.style.height=a+"px",L.style.height=a+"px"}}),P=()=>{const e=L?.contentDocument?.querySelector(`#${_}`),o=e?.querySelector('input[name="after_success_url"]'),c=e?.querySelector('input[name="after_success_behavior"]'),a=document?.querySelector(".newspack-newsletters-signup-modal"),r=i(e?.querySelector("#modal-checkout-product-details"));e?.checkoutComplete||(async()=>{const e=new FormData;newspackBlocksModal.has_unsupported_payment_gateway||e.append("modal_checkout","1"),e.append("action","abandon_modal_checkout"),e.append("_wpnonce",t.checkout_nonce),t.checkout_nonce=null;try{await fetch(newspackBlocksModal.ajax_url,{method:"POST",body:e})}catch(e){console.warn("Unable to empty cart:",e)}})();const l=!(L.contentDocument&&o&&c&&e?.checkoutComplete);if((l||a)&&(E.style.display="flex",L&&n.contains(L)&&(L._ready=!1,L.src="about:blank",L.style.height=q,L.style.visibility="hidden",n.style.height=q,n.removeChild(L)),B&&B.disconnect(),document.querySelectorAll(`.${h}-container`).forEach(e=>b(e)),A&&A.focus(),document.dispatchEvent(S)),e?.checkoutComplete){const e=()=>{if(o&&c){const e=o.getAttribute("value"),t=c.getAttribute("value");"custom"===t?window.location.href=e:"referrer"===t&&window.history.back()}window?.newspackReaderActivation?.setPendingCheckout?.(),f=!1};"subscription_switch"!==r.action_type&&window?.newspackReaderActivation?.openNewslettersSignupModal?window.newspackReaderActivation.openNewslettersSignupModal({onSuccess:e,onError:e,closeOnSuccess:l}):e(),l&&(v=newspackBlocksModal.labels.checkout_modal_title,N(),T(v))}else window?.newspackReaderActivation?.setPendingCheckout?.(),s(),f=!1,document.getElementById("newspack_modal_checkout").removeAttribute("data-checkout");document.removeEventListener("keydown",I)},D=o=>{o&&(L.src=o),E.style.display="flex",O(t),n.appendChild(L),t.addEventListener("click",e=>{e.target===t&&P()}),e(t,L),document.addEventListener("keydown",I)},O=e=>{window.newspackReaderActivation?.overlays&&(e.overlayId=window.newspackReaderActivation?.overlays.add()),e.setAttribute("data-state","open"),document.body.style.overflow="hidden"},T=e=>{const n=t.querySelector(`.${h}__header h2`);n&&(n.innerText=e)},N=(e="default")=>{const n=t.querySelector(`.${h}`);n&&("small"===e?n.classList.add(`${h}--small`):n.classList.remove(`${h}--small`))};t.querySelectorAll(`.${h}__close`).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),P()})}),document.querySelectorAll(".newspack-blocks__modal-variation").forEach(e=>{e.addEventListener("click",t=>{t.target===e&&P()}),e.querySelectorAll(`.${h}__close`).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),P()})})});const I=e=>{"Escape"===e.key&&P()};document.querySelectorAll(".wpbnbd.wpbnbd--platform-wc, .wp-block-newspack-blocks-checkout-button, .newspack-blocks__modal-variation").forEach(e=>{e.querySelectorAll("form").forEach(e=>{newspackBlocksModal.has_unsupported_payment_gateway||e.prepend(o.cloneNode()),e.target=m,e.addEventListener("submit",C)})}),(()=>{const e=new URLSearchParams(window.location.search);if(!e.has("checkout"))return;let t=!0;const n=e.get("type");if("donate"===n){const t=e.get("layout"),n=e.get("frequency"),o=e.get("amount"),c=e.get("other");t&&n&&o&&((e,t,n,o=null)=>{let c;document.querySelectorAll(".wpbnbd.wpbnbd--platform-wc form").forEach(a=>{const r=a.querySelector(`input[name="donation_frequency"][value="${t}"]`);if(r)if("tiered"===e){const e=document.querySelector(`button[data-frequency-slug="${t}"]`);if(!e)return;e.click();const o=a.querySelector(`button[type="submit"][name="donation_value_${t}"][value="${n}"]`);if(!o)return;o.click()}else{const i="untiered"===e?a.querySelector(`input[name="donation_value_${t}_untiered"]`):a.querySelector(`input[name="donation_value_${t}"][value="${n}"]`);if(r&&i){if(r.checked=!0,"untiered"===e)i.value=n;else if("other"===n){i.click();const e=a.querySelector(`input[name="donation_value_${t}_other"]`);e&&o&&(e.value=o)}else i.checked=!0;c=a}}}),c&&r(c)})(t,n,o,c)}else if("checkout_button"===n){const n=e.get("product_id"),o=e.get("variation_id");n?t=((e,t=null)=>{const n=function(e,t,n,o={}){const{allowProductOnlyFallback:c=!1}=o;if(null==n||""===String(n))return u(e,t,null);const a=u(e,t,n);if(a)return a;const r=function(e,t,n,o={}){const{variationModalClassPrefix:c,iframeName:a}=o,r=[...e.querySelectorAll(`.${c}`)].find(e=>String(e.dataset.productId)===String(t));if(!r)return null;const i=r.querySelectorAll("form"),s=a?[...i].find(e=>e.getAttribute("target")===a):i[0];if(!s)return null;const l=[...s.querySelectorAll('input[type="radio"][name="product_id"]')].find(e=>String(e.value)===String(n));return l?(l.checked=!0,s):null}(e,t,n,o);return r?(function(e,t,n=d){if(!e||!t)return;const o=t.ownerDocument,c=new FormData(e);n.forEach(e=>{if(t.querySelector(`input[name="${e}"]`))return;const n=c.getAll(e).filter(e=>"string"==typeof e&&e);if(!n.length)return;const a=o.createElement("input");a.type="hidden",a.name=e,a.value=n[n.length-1],t.prepend(a)})}(u(e,t,null),r),r):c?u(e,t,null):null}(document,e,t,{variationModalClassPrefix:k,iframeName:m});if(n)return r(n),!0;const o=`Newspack modal checkout: no checkout form found for product_id "${e}"`+(t?` and variation_id "${t}"`:"")+". The checkout was not triggered.";return console.warn(o),!1})(n,o):(t=!1,console.warn("Newspack modal checkout: checkout_button trigger is missing product_id. The checkout was not triggered."))}else{const e=window.newspackReaderActivation?.getPendingCheckout?.();e&&r(x(e))}t&&window.history.replaceState(null,null,window.location.pathname)})(),window.newspackOpenModalCheckout=({url:e=null,title:t=null,actionType:n=null,afterSuccess:o={},onCheckoutComplete:c=null,onClose:a=null})=>{if(v=t||newspackBlocksModal.labels.checkout_modal_title,T(v),(e=new URL(e||newspackBlocksModal.checkout_url)).searchParams.has("my_account_checkout")&&e.searchParams.delete("my_account_checkout"),e.searchParams.has("modal_checkout")||e.searchParams.set("modal_checkout","1"),n&&e.searchParams.set("action_type",n),o?.url&&e.searchParams.set("after_success_url",o.url),(o?.behavior||o?.url)&&e.searchParams.set("after_success_behavior",o.behavior||"custom"),o?.buttonLabel&&e.searchParams.set("after_success_button_label",o.buttonLabel),c){const e=({detail:{action:e,data:t}})=>{"checkout_completed"===e&&c(t)};window.newspackRAS.push(t=>{t.on("activity",e);const n=()=>{t.off("activity",e),document.removeEventListener("checkout-closed",n)};document.addEventListener("checkout-closed",n)})}if(a){const e=()=>{a(),document.removeEventListener("checkout-closed",e)};document.addEventListener("checkout-closed",e)}D(e.toString())},window.newspackCloseModalCheckout=P},"undefined"!=typeof document&&("complete"!==document.readyState&&"interactive"!==document.readyState?document.addEventListener("DOMContentLoaded",E):E())})();
1
+ (()=>{"use strict";function e(e,t=!1){const n=e.querySelectorAll('button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])');if(0===n.length)return!1;const o=n?.[0];let c;o.focus(),t?(document.addEventListener("keydown",function(e){if(("Tab"===e.key||9===e.keyCode)&&e.shiftKey&&document.activeElement===o){const n=t.contentWindow.document,o=n.getElementById("checkout-after-success");c=null!==o?o:n.getElementById("checkout_cancel"),c.focus(),e.preventDefault()}}),document.getElementById("newspack-a11y-last-element").addEventListener("focus",()=>{o.focus()})):(c=n[n.length-1],document.addEventListener("keydown",function(e){("Tab"===e.key||9===e.keyCode)&&(e.shiftKey?document.activeElement===o&&(c.focus(),e.preventDefault()):document.activeElement===c&&(o.focus(),e.preventDefault()))}))}const t=(e,t={})=>({...t,action:e}),n=["action","action_type","amount","currency","product_id","product_type","variation_id","variation_ids","is_variable","is_grouped","child_ids","price_summary","newspack_popup_id","prompt_title","gate_post_id","recurrence","referrer"],o=(e,t="np_modal_checkout_interaction")=>{if("function"==typeof window.gtag&&e){const o={};for(const t of n)e[t]&&("boolean"==typeof e[t]?e[t]=e[t]?"yes":"no":"true"===e[t]?e[t]="yes":"false"===e[t]&&(e[t]="no"),o[t]=e[t].toString());window.gtag("event",t,o)}},c=e=>{if("function"!=typeof window.gtag)return;let n="opened";const{amount:c="",is_variable:a="",is_grouped:r="",price:i="",variation_id:s=""}=e,l={...e};(c||i)&&(l.amount=c||i),a&&!s&&(n="opened_variations"),r&&(n="opened_grouped");const u=t(n,l);o(u)};function a(e,t=null){const n=document.createElement("input");return n.type="hidden",n.name=e,t&&(n.value=t),n}function r(e){e.requestSubmit(e.querySelector('button[type="submit"]'))}function i(e){const t="string"==typeof e?document.getElementById(e):e;if(!t)return console.warn("No container found for checkout data"),{};let n={};if("FORM"===t.tagName){const e=new FormData(t);n=Object.fromEntries(e)}const o=t.dataset.checkout;if(!o)return console.warn("No checkout data found"),n;try{n={...n,...JSON.parse(o)}}catch(e){console.warn("Error parsing checkout data")}const c=new URL(t.ownerDocument.defaultView.location.href);return c.searchParams.get("action_type")&&(n.action_type=c.searchParams.get("action_type")),n}const s=e=>{if("function"!=typeof window.gtag)return;e=e||i("newspack_modal_checkout");const n=t("dismissed",e);o(n)};function l(e){const t=e&&e.dataset?e.dataset.checkout:null;if(!t)return null;try{return JSON.parse(t)}catch(e){return null}}function u(e,t,n=null){const o=e.querySelectorAll(".wp-block-newspack-blocks-checkout-button"),c=null!=n&&""!==String(n);let a=null;return o.forEach(e=>{if(a)return;const o=e.querySelector("form"),r=l(o);r&&String(r.product_id)===String(t)&&(c&&String(r.variation_id)!==String(n)||(a=o))}),a}const d=["after_success_behavior","after_success_url","after_success_button_label","gate_post_id","newspack_popup_id","prompt_title"];const p={gate:"gate_post_id",prompt:"newspack_popup_id"};const m=newspackBlocksModal.newspack_class_prefix,_="newspack_modal_checkout_iframe",h="newspack_modal_checkout_container",y=`${m}__modal`,w="newspack-blocks__modal-variation",f=`${m}__processing-payment-text`,k=newspackBlocksModal.processing_payment_messages;let v=!1,g=newspackBlocksModal.labels.checkout_modal_title,b=null;const S=e=>{e.overlayId&&window.newspackReaderActivation?.overlays&&window.newspackReaderActivation?.overlays.remove(e.overlayId),e.setAttribute("data-state","closed"),document.body.style.overflow="auto"};window.onpageshow=e=>{e.persisted&&(document.querySelectorAll(".modal-processing").forEach(e=>e.classList.remove("modal-processing")),document.querySelectorAll(".non-modal-checkout-loading").forEach(e=>e.classList.remove("non-modal-checkout-loading")),document.querySelectorAll(`.${y}-container`).forEach(e=>S(e)))};const E=new CustomEvent("checkout-closed");var q;window.newspackRAS=window.newspackRAS||[],q=()=>{const t=document.querySelector("#newspack_modal_checkout");if(!t)return;const n=t.querySelector(`.${y}__content`),o=a("modal_checkout","1"),q=n.querySelector(`.${m}__spinner`);let A=document.querySelector(".newspack-reader__account-link")?.[0];const L="600px",$=document.createElement("iframe");$.name=_,$.style.height=L,$.style.visibility="hidden";const R=e=>{P.observe(e),"none"!==q.style.display&&(q.style.display="none"),"visible"!==$.style.visibility&&($.style.visibility="visible"),$._ready=!0};!function(e,t,n){e._ready=!1,e._readyTimer&&clearTimeout(e._readyTimer);let o=!1,c="";function a(){o||(o=!0,clearTimeout(e._readyTimer),t.call(this))}function r(){"complete"===this.readyState&&a.call(this)}function i(){e._ready=!1,n&&n(),e._readyTimer&&clearTimeout(e._readyTimer);const t=e.contentDocument||e.contentWindow?.document;t&&(t.removeEventListener("DOMContentLoaded",a),t.removeEventListener("readystatechange",r))}function s(){if(e._ready)return void i();const t=e.contentDocument||e.contentWindow?.document;t&&0!==t.URL.indexOf("about:")?"complete"===t?.readyState?a.call(t):(t.addEventListener("DOMContentLoaded",a),t.addEventListener("readystatechange",r)):e._readyTimer=setTimeout(s,10)}e._observer||(e._observer=new MutationObserver(e=>{e.forEach(e=>{"attributes"===e.type&&"src"===e.attributeName&&(o=!1,i(),s())})}),e._observer.observe(e,{attributes:!0})),e._locationObserver||(e._locationObserver=setInterval(function(){const t=e.contentDocument||e.contentWindow?.document;t&&t.URL!==c&&(c=t.URL,o=!1,i(),s())},50)),s()}($,function(){const n=$.contentWindow?.location;if(window.newspackReaderActivation&&n?.href?.includes("order-received")){const e=window.newspackReaderActivation,t=new Proxy(new URLSearchParams(n.search),{get:(e,t)=>e.get(t)});t.email&&(e.setReaderEmail(t.email),e.setAuthenticated(!0))}const o=$?.contentDocument?.querySelector(`#${h}`);if(!o)return;const c=o.querySelector("#modal-checkout-product-details"),a=i(c),r=document.createElement("p");r.classList.add(f);let s=[];const l=()=>{s.forEach(e=>clearTimeout(e)),s=[]},u=()=>{q.style.display="none",l(),q.querySelectorAll(`.${f}`).forEach(e=>e.remove())};!function(e,t){e.checkoutCancel?t():e.addEventListener("checkout-cancel",t)}(o,()=>{D()}),function(e,t){e.checkoutPlaceOrderStart?t():e.addEventListener("checkout-place-order-start",t)}(o,e=>{q.querySelectorAll(`.${f}`).forEach(e=>e.remove()),q.style.display="flex",l(),r.textContent=k[0]?.text??"",k.slice(1).forEach(({text:t,delay:n})=>{const o=setTimeout(()=>{e.target.dispatchEvent(new CustomEvent("checkout-place-order-processing")),r.textContent=t},n);s.push(o)}),q.appendChild(r)}),function(e,t){e.checkoutPlaceOrderError?t():e.addEventListener("checkout-place-order-error",t)}(o,u),function(e,t){e.checkoutReady?t():e.addEventListener("checkout-ready",t)}(o,()=>{const e=c?.querySelector("strong");if(e&&(e.textContent=a.price_summary),t.initialErrors){const e=document.createElement("div");e.classList.add("woocommerce-error"),e.textContent=t.initialErrors,o.prepend(e),delete t.initialErrors}I(),N(g),$.contentWindow?.newspackBlocksModalCheckout?.checkout_nonce&&(t.checkout_nonce=$.contentWindow.newspackBlocksModalCheckout.checkout_nonce),R(o)}),function(e,t){e.checkoutComplete?t():e.addEventListener("checkout-complete",t)}(o,()=>{window.newspackRAS.push(["checkout_completed",a]),window?.newspackReaderActivation?.refreshNewslettersSignupModal&&window?.newspackReaderActivation?.getReader()?.email&&window.newspackReaderActivation.refreshNewslettersSignupModal(window.newspackReaderActivation.getReader().email),I("small"),N(newspackBlocksModal.labels.thankyou_modal_title),R(o),e(t.querySelector(`.${y}`)),u()}),function(e,t){e.checkoutPlaceOrderCriticalError?t():e.addEventListener("checkout-place-order-critical-error",t)}(o,()=>{var e;(e=b)&&(D(),q.style.display="none",t.initialErrors=newspackBlocksModal.labels.critical_error,e.requestSubmit(e.querySelector('button[type="submit"]')),u())})},()=>{q.style.display="flex"});const M=e=>new Promise((t,n)=>{const o=new URLSearchParams(e);o.append("action","modal_checkout_request"),fetch(newspackBlocksModal.ajax_url+"?"+o.toString()).then(e=>{e.ok||n(e),e.json().then(e=>{t(e.url)}).catch(n)}).catch(n)}),C=()=>"undefined"!=typeof newspack_ras_config&&!newspack_ras_config?.is_logged_in&&!window?.newspackReaderActivation?.getReader?.()?.authenticated&&newspackBlocksModal?.is_registration_required&&window?.newspackReaderActivation?.openAuthModal,x=t=>{const n=!newspackBlocksModal.has_unsupported_payment_gateway;n||t.preventDefault();const o=t.target;o.classList.add("modal-processing"),function(e){if(!e||"function"!=typeof e.querySelector)return!1;if(e.closest(".newspack-content-gate__gate"))return!1;const t=function(){let e,t;try{e=window.sessionStorage.getItem("newspack_cta_attribution")}catch(e){return null}if(!e)return null;try{t=JSON.parse(e)}catch(e){return null}return t&&1===t.v&&p[t.type]&&t.id?"number"!=typeof t.ts||Date.now()-t.ts>18e5?null:t:null}();if(!t)return!1;const n=p[t.type];if(e.querySelector(`input[name="${n}"]`))return!1;const o=document.createElement("input");o.type="hidden",o.name=n,o.value=t.id,e.prepend(o)}(o);const u=i(o);if(u.newspack_donate){const e=u.donation_frequency,t=[...o.querySelectorAll(`.donation-tier__${e}, .donation-frequency__${e}`)],n=u.donation_tier_index;let c,a;n?(c=t[n],a=u[`donation_value_${e}`]):(c=t[0],u[`donation_value_${e}_untiered`]?a=u[`donation_value_${e}_untiered`]:(a=u[`donation_value_${e}`],"other"===a&&(a=u[`donation_value_${e}_other`])));const r=i(c);for(const e in r)u[e]=r[e];u.amount=a,u.price_summary=u.summary_template.replace("{{PRICE}}",function(e,t="USD"){return parseFloat(e).toLocaleString(document.documentElement.lang,{style:"currency",currency:t,currencyDisplay:"narrowSymbol"})}(u.amount,u.currency))}u&&Object.keys(u).forEach(e=>{0===o.querySelectorAll('input[name="'+e+'"]').length&&o.prepend(a(e,u[e]))}),u.variation_id||(A=t.submitter);const d=document.querySelectorAll(`.${w}`);if(d.forEach(e=>{(C()||n)&&S(e)}),u.is_grouped||u.is_variable&&!u.variation_id){const n=[...d].find(e=>e.dataset.productId===u.product_id);if(n)return n.querySelectorAll(`form[target="${_}"]`).forEach(e=>{["after_success_behavior","after_success_url","after_success_button_label","gate_post_id","newspack_popup_id","prompt_title"].forEach(t=>{0===e.querySelectorAll('input[name="'+t+'"]').length&&e.prepend(a(t,u[t]))});const t=l(e);t&&Object.keys(t).forEach(n=>{0===e.querySelectorAll('input[name="'+n+'"]').length&&e.prepend(a(n,t[n]))})}),t.preventDefault(),o.classList.remove("modal-processing"),T(n),e(n,!1),v||c(u),void document.getElementById("newspack_modal_checkout").setAttribute("data-checkout",JSON.stringify(u))}if(n||C()){if(o.classList.remove("modal-processing"),v||c(u),v=!0,C()){t.preventDefault();const e=u.price_summary,o=e?`<div class="order-details-summary ${m}__box ${m}__box--text-center"><p><strong>${e}</strong></p></div>`:"",c=M(u);c.then(e=>{window.newspackReaderActivation?.setPendingCheckout?.(e)}),window.newspackReaderActivation.openAuthModal({title:newspackBlocksModal.labels.auth_modal_title,onSuccess:(e,t)=>{c.then(e=>{t?.registered&&n&&(e+=`&${newspackBlocksModal.checkout_registration_flag}=1`),n?r(B(e)):M(u).then(window.location.href=e)}).catch(e=>{console.warn("Unable to generate cart:",e),D()})},onError:()=>{D()},onDismiss:()=>{s(u),v=!1,document.getElementById("newspack_modal_checkout").removeAttribute("data-checkout")},skipSuccess:!0,skipNewslettersSignup:!0,labels:{signin:{title:newspackBlocksModal.labels.signin_modal_title},register:{title:newspackBlocksModal.labels.register_modal_title}},content:o,trigger:t.submitter,closeOnSuccess:n})}else O(),document.getElementById("newspack_modal_checkout").setAttribute("data-checkout",JSON.stringify(u));b=o}else M(u).then(e=>{window.location.href=e}),(!u.is_variable||u.variation_id)&&o.querySelectorAll("button[type=submit]:focus").forEach(e=>{e.classList.add("non-modal-checkout-loading");const t=e.innerHTML;e.innerHTML="<span>"+t+"</span>"})},B=e=>{const t=document.createElement("form");t.method="POST",t.action=e,t.target=_,t.style.display="none";const n=document.createElement("button");return n.setAttribute("type","submit"),t.appendChild(n),document.body.appendChild(t),t.addEventListener("submit",x),t},P=new ResizeObserver(e=>{if(!e||!e.length)return;if(!$.contentDocument)return;const o=e[0].contentRect;if(o){const e=.01*Math.max(document.documentElement.clientHeight,window.innerHeight||0)*90-(t.querySelector(`.${y}__header`)?.offsetHeight||0),c=o.top+o.bottom,a=Math.min(c,e);if(0===a)return void($.style.visibility="hidden");n.style.height=a+"px",$.style.height=a+"px"}}),D=()=>{const e=$?.contentDocument?.querySelector(`#${h}`),o=e?.querySelector('input[name="after_success_url"]'),c=e?.querySelector('input[name="after_success_behavior"]'),a=document?.querySelector(".newspack-newsletters-signup-modal"),r=i(e?.querySelector("#modal-checkout-product-details"));e?.checkoutComplete||(async()=>{const e=new FormData;newspackBlocksModal.has_unsupported_payment_gateway||e.append("modal_checkout","1"),e.append("action","abandon_modal_checkout"),e.append("_wpnonce",t.checkout_nonce),t.checkout_nonce=null;try{await fetch(newspackBlocksModal.ajax_url,{method:"POST",body:e})}catch(e){console.warn("Unable to empty cart:",e)}})();const l=!($.contentDocument&&o&&c&&e?.checkoutComplete);if((l||a)&&(q.style.display="flex",$&&n.contains($)&&($._ready=!1,$.src="about:blank",$.style.height=L,$.style.visibility="hidden",n.style.height=L,n.removeChild($)),P&&P.disconnect(),document.querySelectorAll(`.${y}-container`).forEach(e=>S(e)),A&&A.focus(),document.dispatchEvent(E)),e?.checkoutComplete){const e=()=>{if(o&&c){const e=o.getAttribute("value"),t=c.getAttribute("value");"custom"===t?window.location.href=e:"referrer"===t&&window.history.back()}window?.newspackReaderActivation?.setPendingCheckout?.(),v=!1};"subscription_switch"!==r.action_type&&window?.newspackReaderActivation?.openNewslettersSignupModal?window.newspackReaderActivation.openNewslettersSignupModal({onSuccess:e,onError:e,closeOnSuccess:l}):e(),l&&(g=newspackBlocksModal.labels.checkout_modal_title,I(),N(g))}else window?.newspackReaderActivation?.setPendingCheckout?.(),s(),v=!1,document.getElementById("newspack_modal_checkout").removeAttribute("data-checkout");document.removeEventListener("keydown",U)},O=o=>{o&&($.src=o),q.style.display="flex",T(t),n.appendChild($),t.addEventListener("click",e=>{e.target===t&&D()}),e(t,$),document.addEventListener("keydown",U)},T=e=>{window.newspackReaderActivation?.overlays&&(e.overlayId=window.newspackReaderActivation?.overlays.add()),e.setAttribute("data-state","open"),document.body.style.overflow="hidden"},N=e=>{const n=t.querySelector(`.${y}__header h2`);n&&(n.innerText=e)},I=(e="default")=>{const n=t.querySelector(`.${y}`);n&&("small"===e?n.classList.add(`${y}--small`):n.classList.remove(`${y}--small`))};t.querySelectorAll(`.${y}__close`).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),D()})}),document.querySelectorAll(".newspack-blocks__modal-variation").forEach(e=>{e.addEventListener("click",t=>{t.target===e&&D()}),e.querySelectorAll(`.${y}__close`).forEach(e=>{e.addEventListener("click",e=>{e.preventDefault(),D()})})});const U=e=>{"Escape"===e.key&&D()};document.querySelectorAll(".wpbnbd.wpbnbd--platform-wc, .wp-block-newspack-blocks-checkout-button, .newspack-blocks__modal-variation").forEach(e=>{e.querySelectorAll("form").forEach(e=>{newspackBlocksModal.has_unsupported_payment_gateway||e.prepend(o.cloneNode()),e.target=_,e.addEventListener("submit",x)})}),(()=>{const e=new URLSearchParams(window.location.search);if(!e.has("checkout"))return;let t=!0;const n=e.get("type");if("donate"===n){const t=e.get("layout"),n=e.get("frequency"),o=e.get("amount"),c=e.get("other");t&&n&&o&&((e,t,n,o=null)=>{let c;document.querySelectorAll(".wpbnbd.wpbnbd--platform-wc form").forEach(a=>{const r=a.querySelector(`input[name="donation_frequency"][value="${t}"]`);if(r)if("tiered"===e){const e=document.querySelector(`button[data-frequency-slug="${t}"]`);if(!e)return;e.click();const o=a.querySelector(`button[type="submit"][name="donation_value_${t}"][value="${n}"]`);if(!o)return;o.click()}else{const i="untiered"===e?a.querySelector(`input[name="donation_value_${t}_untiered"]`):a.querySelector(`input[name="donation_value_${t}"][value="${n}"]`);if(r&&i){if(r.checked=!0,"untiered"===e)i.value=n;else if("other"===n){i.click();const e=a.querySelector(`input[name="donation_value_${t}_other"]`);e&&o&&(e.value=o)}else i.checked=!0;c=a}}}),c&&r(c)})(t,n,o,c)}else if("checkout_button"===n){const n=e.get("product_id"),o=e.get("variation_id");n?t=((e,t=null)=>{const n=function(e,t,n,o={}){const{allowProductOnlyFallback:c=!1}=o;if(null==n||""===String(n))return u(e,t,null);const a=u(e,t,n);if(a)return a;const r=function(e,t,n,o={}){const{variationModalClassPrefix:c,iframeName:a}=o,r=[...e.querySelectorAll(`.${c}`)].find(e=>String(e.dataset.productId)===String(t));if(!r)return null;const i=r.querySelectorAll("form"),s=a?[...i].find(e=>e.getAttribute("target")===a):i[0];if(!s)return null;const l=[...s.querySelectorAll('input[type="radio"][name="product_id"]')].find(e=>String(e.value)===String(n));return l?(l.checked=!0,s):null}(e,t,n,o);return r?(function(e,t,n=d){if(!e||!t)return;const o=t.ownerDocument,c=new FormData(e);n.forEach(e=>{if(t.querySelector(`input[name="${e}"]`))return;const n=c.getAll(e).filter(e=>"string"==typeof e&&e);if(!n.length)return;const a=o.createElement("input");a.type="hidden",a.name=e,a.value=n[n.length-1],t.prepend(a)})}(u(e,t,null),r),r):c?u(e,t,null):null}(document,e,t,{variationModalClassPrefix:w,iframeName:_});if(n)return r(n),!0;const o=`Newspack modal checkout: no checkout form found for product_id "${e}"`+(t?` and variation_id "${t}"`:"")+". The checkout was not triggered.";return console.warn(o),!1})(n,o):(t=!1,console.warn("Newspack modal checkout: checkout_button trigger is missing product_id. The checkout was not triggered."))}else{const e=window.newspackReaderActivation?.getPendingCheckout?.();e&&r(B(e))}t&&window.history.replaceState(null,null,window.location.pathname)})(),window.newspackOpenModalCheckout=({url:e=null,title:t=null,actionType:n=null,afterSuccess:o={},onCheckoutComplete:c=null,onClose:a=null})=>{if(g=t||newspackBlocksModal.labels.checkout_modal_title,N(g),(e=new URL(e||newspackBlocksModal.checkout_url)).searchParams.has("my_account_checkout")&&e.searchParams.delete("my_account_checkout"),e.searchParams.has("modal_checkout")||e.searchParams.set("modal_checkout","1"),n&&e.searchParams.set("action_type",n),o?.url&&e.searchParams.set("after_success_url",o.url),(o?.behavior||o?.url)&&e.searchParams.set("after_success_behavior",o.behavior||"custom"),o?.buttonLabel&&e.searchParams.set("after_success_button_label",o.buttonLabel),c){const e=({detail:{action:e,data:t}})=>{"checkout_completed"===e&&c(t)};window.newspackRAS.push(t=>{t.on("activity",e);const n=()=>{t.off("activity",e),document.removeEventListener("checkout-closed",n)};document.addEventListener("checkout-closed",n)})}if(a){const e=()=>{a(),document.removeEventListener("checkout-closed",e)};document.addEventListener("checkout-closed",e)}O(e.toString())},window.newspackCloseModalCheckout=D},"undefined"!=typeof document&&("complete"!==document.readyState&&"interactive"!==document.readyState?document.addEventListener("DOMContentLoaded",q):q())})();
@@ -7,7 +7,7 @@
7
7
  * Author URI: https://newspack.com/
8
8
  * Text Domain: newspack-blocks
9
9
  * Domain Path: /languages
10
- * Version: 4.26.8
10
+ * Version: 4.27.0
11
11
  *
12
12
  * @package Newspack_Blocks
13
13
  */
@@ -15,7 +15,7 @@
15
15
  define( 'NEWSPACK_BLOCKS__PLUGIN_FILE', __FILE__ );
16
16
  define( 'NEWSPACK_BLOCKS__BLOCKS_DIRECTORY', 'dist/' );
17
17
  define( 'NEWSPACK_BLOCKS__PLUGIN_DIR', plugin_dir_path( NEWSPACK_BLOCKS__PLUGIN_FILE ) );
18
- define( 'NEWSPACK_BLOCKS__VERSION', '4.26.8' );
18
+ define( 'NEWSPACK_BLOCKS__VERSION', '4.27.0' );
19
19
 
20
20
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks.php';
21
21
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks-api.php';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/newspack-blocks",
3
- "version": "4.26.8",
3
+ "version": "4.27.0",
4
4
  "author": "Automattic",
5
5
  "description": "=== Newspack Blocks === Contributors: (this should be a list of wordpress.org userid's) Donate link: https://example.com/ Tags: comments, spam Requires at least: 4.5 Tested up to: 5.1.1 Stable tag: 0.1.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html",
6
6
  "repository": {
@@ -25,6 +25,7 @@ import {
25
25
  getFormattedAmount,
26
26
  } from './utils';
27
27
  import { resolveCheckoutButtonForm, readCheckoutData } from './checkout-button-trigger';
28
+ import { applyCtaAttribution } from '../shared/js/cta-attribution';
28
29
 
29
30
  const CLASS_PREFIX = newspackBlocksModal.newspack_class_prefix;
30
31
  const IFRAME_NAME = 'newspack_modal_checkout_iframe';
@@ -301,6 +302,13 @@ domReady( () => {
301
302
  const form = ev.target;
302
303
  form.classList.add( 'modal-processing' );
303
304
 
305
+ // NPPD-1887: if the reader arrived here by clicking a paid-intent CTA in a gate
306
+ // or prompt, replay that surface's id as a hidden field so the order carries
307
+ // `_gate_post_id` / `_newspack_popup_id`. No-ops when the form already has one
308
+ // (a form rendered inside the surface itself always wins) or when the form is
309
+ // inside a gate. Must run BEFORE getCheckoutData(), which snapshots the form.
310
+ applyCtaAttribution( form );
311
+
304
312
  const checkoutData = getCheckoutData( form );
305
313
 
306
314
  const isDonateBlock = checkoutData.newspack_donate;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * CTA attribution replay (NPPD-1887).
3
+ *
4
+ * The consumer half of the hand-off written by newspack-plugin when a reader clicks a
5
+ * paid-intent CTA inside a gate or prompt and is sent to a landing page. We re-inject
6
+ * the surface's id as a hidden form field, so the existing server chain
7
+ * (`INPUT_GET` -> cart item data -> `_gate_post_id` / `_newspack_popup_id` order meta)
8
+ * fires exactly as it does for a form rendered inside the gate itself.
9
+ *
10
+ * Two cart paths exist, and they apply DIFFERENT filters. Checkout buttons go through
11
+ * `Modal_Checkout::add_to_cart_and_redirect()` -> `newspack_blocks_modal_checkout_cart_item_data`;
12
+ * donations go through `Donations::process_donation_request()` -> `newspack_donations_cart_item_data`.
13
+ * Injecting the field here is necessary but not sufficient — a listener must exist on
14
+ * whichever filter the target form uses, or the id rides all the way into `INPUT_GET`
15
+ * and is then dropped.
16
+ *
17
+ * ── Storage contract ────────────────────────────────────────────────────────────
18
+ * DUPLICATED from newspack-plugin (`src/shared/js/cta-attribution.js`). The two
19
+ * plugins ship independently and share no JS package. Change one, change the other.
20
+ *
21
+ * sessionStorage[ 'newspack_cta_attribution' ] = JSON.stringify( {
22
+ * v: 1,
23
+ * type: 'gate' | 'prompt',
24
+ * id: '123',
25
+ * ts: 1751932800000,
26
+ * } )
27
+ */
28
+
29
+ /** Storage key. Shared with newspack-plugin. */
30
+ export const STORAGE_KEY = 'newspack_cta_attribution';
31
+
32
+ /**
33
+ * Storage-record schema version. Must match the writer's in newspack-plugin. A record
34
+ * whose `v` differs is treated as absent rather than trusted, so a drift between the two
35
+ * duplicated copies (e.g. one changes the record shape or TTL) fails safe instead of
36
+ * silently honoring a stale record. (dkoo review, #575.)
37
+ */
38
+ export const SCHEMA = 1;
39
+
40
+ /** Attribution lifetime, in ms. Kept in sync with newspack-plugin. */
41
+ export const TTL_MS = 30 * 60 * 1000;
42
+
43
+ /** Surface type -> the form field the server reads. */
44
+ const FIELD_BY_TYPE = {
45
+ gate: 'gate_post_id',
46
+ prompt: 'newspack_popup_id',
47
+ };
48
+
49
+ /**
50
+ * Read a fresh attribution record, or null.
51
+ *
52
+ * Never throws: `sessionStorage` access raises in Safari private mode, and the stored
53
+ * value is user-writable so `JSON.parse` can fail. Attribution is best-effort — a bad
54
+ * record must never block a checkout.
55
+ *
56
+ * @return {{type: string, id: string, ts: number}|null} The record, or null.
57
+ */
58
+ export function readCtaAttribution() {
59
+ let raw;
60
+ try {
61
+ raw = window.sessionStorage.getItem( STORAGE_KEY );
62
+ } catch ( e ) {
63
+ return null;
64
+ }
65
+ if ( ! raw ) {
66
+ return null;
67
+ }
68
+ let record;
69
+ try {
70
+ record = JSON.parse( raw );
71
+ } catch ( e ) {
72
+ return null;
73
+ }
74
+ // Reject records written by a different schema version (drift fail-safe).
75
+ if ( ! record || record.v !== SCHEMA || ! FIELD_BY_TYPE[ record.type ] || ! record.id ) {
76
+ return null;
77
+ }
78
+ // Session-scoped AND time-bounded (NPPD-1887 product decision). A reader who clicks
79
+ // the CTA, wanders off, and converts two hours later in the same tab gets no credit.
80
+ if ( 'number' !== typeof record.ts || Date.now() - record.ts > TTL_MS ) {
81
+ return null;
82
+ }
83
+ return record;
84
+ }
85
+
86
+ /**
87
+ * Inject the persisted surface id into a checkout/donate form, if it needs one.
88
+ *
89
+ * Two guards, both load-bearing:
90
+ *
91
+ * 1. A form that ALREADY carries the field wins. That is a form rendered inside the
92
+ * gate or prompt itself, whose id was stamped server-side (prompts) or by
93
+ * `gate.js::addFormInputs()` (gates). A replayed value must never overwrite the
94
+ * surface the reader is actually looking at. Same precedence rule that
95
+ * `copyContextFields()` applies to PICKER_CONTEXT_FIELDS.
96
+ *
97
+ * 2. A form inside a gate is never replayed into. `gate.js` owns attribution there,
98
+ * and it binds on the gate's 'seen' event — which may not have fired yet when
99
+ * this runs. Without this guard, a stale id from a gate on a PREVIOUS page could
100
+ * beat the current page's gate to the field.
101
+ *
102
+ * Called from the submit handler rather than at DOM-ready, so it observes the final
103
+ * state of the form after every other script has had its turn.
104
+ *
105
+ * @param {HTMLFormElement} form The form about to be submitted.
106
+ *
107
+ * @return {boolean} Whether a field was injected.
108
+ */
109
+ export function applyCtaAttribution( form ) {
110
+ if ( ! form || 'function' !== typeof form.querySelector ) {
111
+ return false;
112
+ }
113
+ if ( form.closest( '.newspack-content-gate__gate' ) ) {
114
+ return false;
115
+ }
116
+ const record = readCtaAttribution();
117
+ if ( ! record ) {
118
+ return false;
119
+ }
120
+ const name = FIELD_BY_TYPE[ record.type ];
121
+ if ( form.querySelector( `input[name="${ name }"]` ) ) {
122
+ return false;
123
+ }
124
+ const input = document.createElement( 'input' );
125
+ input.type = 'hidden';
126
+ input.name = name;
127
+ input.value = record.id;
128
+ form.prepend( input );
129
+ return true;
130
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Tests for the CTA attribution replay (NPPD-1887).
3
+ */
4
+
5
+ import { STORAGE_KEY, SCHEMA, TTL_MS, readCtaAttribution, applyCtaAttribution } from './cta-attribution';
6
+
7
+ /**
8
+ * Write a raw value into sessionStorage.
9
+ *
10
+ * Object records are stamped with the current SCHEMA unless they specify their own `v`,
11
+ * mirroring how persistCtaAttribution actually writes — so tests read realistic records,
12
+ * while the version-mismatch test can still supply a drifted `v`.
13
+ *
14
+ * @param {*} value Value to store (stringified unless already a string).
15
+ */
16
+ function store( value ) {
17
+ if ( value && 'object' === typeof value && undefined === value.v ) {
18
+ value = { v: SCHEMA, ...value };
19
+ }
20
+ window.sessionStorage.setItem( STORAGE_KEY, 'string' === typeof value ? value : JSON.stringify( value ) );
21
+ }
22
+
23
+ /**
24
+ * Build a detached form, optionally with a pre-existing hidden input.
25
+ *
26
+ * @param {Object} options Options.
27
+ * @param {string} options.existing Name of a hidden input to pre-add.
28
+ * @param {string} options.wrapper Class name for a wrapping element.
29
+ * @return {HTMLFormElement} The form (attached to document.body).
30
+ */
31
+ function makeForm( { existing = null, wrapper = null } = {} ) {
32
+ const form = document.createElement( 'form' );
33
+ if ( existing ) {
34
+ const input = document.createElement( 'input' );
35
+ input.type = 'hidden';
36
+ input.name = existing;
37
+ input.value = 'original';
38
+ form.appendChild( input );
39
+ }
40
+ if ( wrapper ) {
41
+ const el = document.createElement( 'div' );
42
+ el.className = wrapper;
43
+ el.appendChild( form );
44
+ document.body.appendChild( el );
45
+ } else {
46
+ document.body.appendChild( form );
47
+ }
48
+ return form;
49
+ }
50
+
51
+ describe( 'cta-attribution', () => {
52
+ beforeEach( () => {
53
+ window.sessionStorage.clear();
54
+ document.body.innerHTML = '';
55
+ } );
56
+
57
+ describe( 'readCtaAttribution', () => {
58
+ it( 'returns null when nothing is stored', () => {
59
+ expect( readCtaAttribution() ).toBeNull();
60
+ } );
61
+
62
+ it( 'returns a fresh gate record', () => {
63
+ store( { type: 'gate', id: '42', ts: Date.now() } );
64
+ expect( readCtaAttribution() ).toEqual( { v: SCHEMA, type: 'gate', id: '42', ts: expect.any( Number ) } );
65
+ } );
66
+
67
+ // Drift fail-safe: the storage contract is duplicated across newspack-plugin and
68
+ // newspack-blocks. A record written by a different schema version must be ignored,
69
+ // not silently honored. (dkoo review, #575.)
70
+ it( 'rejects a record written by a different schema version', () => {
71
+ store( { v: SCHEMA + 1, type: 'gate', id: '42', ts: Date.now() } );
72
+ expect( readCtaAttribution() ).toBeNull();
73
+ } );
74
+
75
+ it( 'rejects a record older than the TTL', () => {
76
+ store( { type: 'gate', id: '42', ts: Date.now() - TTL_MS - 1000 } );
77
+ expect( readCtaAttribution() ).toBeNull();
78
+ } );
79
+
80
+ it( 'accepts a record right at the TTL boundary', () => {
81
+ store( { type: 'gate', id: '42', ts: Date.now() - TTL_MS + 5000 } );
82
+ expect( readCtaAttribution() ).not.toBeNull();
83
+ } );
84
+
85
+ it( 'rejects an unknown surface type', () => {
86
+ store( { type: 'banner', id: '42', ts: Date.now() } );
87
+ expect( readCtaAttribution() ).toBeNull();
88
+ } );
89
+
90
+ it( 'rejects a record with no id', () => {
91
+ store( { type: 'gate', ts: Date.now() } );
92
+ expect( readCtaAttribution() ).toBeNull();
93
+ } );
94
+
95
+ it( 'rejects a non-numeric timestamp rather than treating it as fresh', () => {
96
+ store( { type: 'gate', id: '42', ts: 'now' } );
97
+ expect( readCtaAttribution() ).toBeNull();
98
+ } );
99
+
100
+ it( 'survives malformed JSON', () => {
101
+ store( '{not json' );
102
+ expect( readCtaAttribution() ).toBeNull();
103
+ } );
104
+ } );
105
+
106
+ describe( 'applyCtaAttribution', () => {
107
+ it( 'injects gate_post_id on a bare landing-page form', () => {
108
+ store( { type: 'gate', id: '42', ts: Date.now() } );
109
+ const form = makeForm();
110
+
111
+ expect( applyCtaAttribution( form ) ).toBe( true );
112
+ expect( form.querySelector( 'input[name="gate_post_id"]' ).value ).toBe( '42' );
113
+ } );
114
+
115
+ it( 'injects newspack_popup_id for a prompt record', () => {
116
+ store( { type: 'prompt', id: '7', ts: Date.now() } );
117
+ const form = makeForm();
118
+
119
+ expect( applyCtaAttribution( form ) ).toBe( true );
120
+ expect( form.querySelector( 'input[name="newspack_popup_id"]' ).value ).toBe( '7' );
121
+ } );
122
+
123
+ // Precedence: a form rendered inside the surface itself already carries the id,
124
+ // stamped server-side (prompts) or by gate.js::addFormInputs (gates). A replayed
125
+ // value must never overwrite the surface the reader is actually looking at.
126
+ it( 'never overwrites an existing hidden input', () => {
127
+ store( { type: 'gate', id: '42', ts: Date.now() } );
128
+ const form = makeForm( { existing: 'gate_post_id' } );
129
+
130
+ expect( applyCtaAttribution( form ) ).toBe( false );
131
+ expect( form.querySelector( 'input[name="gate_post_id"]' ).value ).toBe( 'original' );
132
+ expect( form.querySelectorAll( 'input[name="gate_post_id"]' ) ).toHaveLength( 1 );
133
+ } );
134
+
135
+ // gate.js owns attribution inside a gate, and it binds on the gate's 'seen'
136
+ // event, which may not have fired yet. Without this guard a stale id from a gate
137
+ // on a PREVIOUS page could beat the current page's gate to the field.
138
+ it( 'never injects into a form inside a gate', () => {
139
+ store( { type: 'gate', id: '42', ts: Date.now() } );
140
+ const form = makeForm( { wrapper: 'newspack-content-gate__gate' } );
141
+
142
+ expect( applyCtaAttribution( form ) ).toBe( false );
143
+ expect( form.querySelector( 'input[name="gate_post_id"]' ) ).toBeNull();
144
+ } );
145
+
146
+ it( 'does nothing when the record is stale', () => {
147
+ store( { type: 'gate', id: '42', ts: Date.now() - TTL_MS - 1 } );
148
+ const form = makeForm();
149
+
150
+ expect( applyCtaAttribution( form ) ).toBe( false );
151
+ expect( form.querySelector( 'input[name="gate_post_id"]' ) ).toBeNull();
152
+ } );
153
+
154
+ it( 'does nothing when nothing is stored', () => {
155
+ const form = makeForm();
156
+ expect( applyCtaAttribution( form ) ).toBe( false );
157
+ } );
158
+
159
+ it( 'tolerates a missing form', () => {
160
+ store( { type: 'gate', id: '42', ts: Date.now() } );
161
+ expect( applyCtaAttribution( null ) ).toBe( false );
162
+ expect( applyCtaAttribution( undefined ) ).toBe( false );
163
+ } );
164
+ } );
165
+ } );
@@ -3,7 +3,7 @@
3
3
  'name' => 'automattic/newspack-blocks',
4
4
  'pretty_version' => 'dev-main',
5
5
  'version' => 'dev-main',
6
- 'reference' => 'cde3e84ebf2c8e653f085560799f32c69bd75a36',
6
+ 'reference' => '20e84f87dc1be96b5575548593fd0f7e77da52e6',
7
7
  'type' => 'wordpress-plugin',
8
8
  'install_path' => __DIR__ . '/../../',
9
9
  'aliases' => array(),
@@ -13,7 +13,7 @@
13
13
  'automattic/newspack-blocks' => array(
14
14
  'pretty_version' => 'dev-main',
15
15
  'version' => 'dev-main',
16
- 'reference' => 'cde3e84ebf2c8e653f085560799f32c69bd75a36',
16
+ 'reference' => '20e84f87dc1be96b5575548593fd0f7e77da52e6',
17
17
  'type' => 'wordpress-plugin',
18
18
  'install_path' => __DIR__ . '/../../',
19
19
  'aliases' => array(),