PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.8
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.8
2.12.8 2.12.7 2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 All 98 releases
← All changes | inc/payments/payment-history-shortcode.php +109 -44 2.8.2 → 2.12.8 View file →
@@ -37,9 +37,18 @@
37 37 * @since 2.8.0
38 38 */
39 39 public function __construct() {
40 40 add_shortcode( self::SHORTCODE_TAG, [ $this, 'render' ] );
41 - add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );
41 + // Register the handles early (priority 1) — before Elementor/Bricks enqueue
42 + // their widget assets on wp_enqueue_scripts — so the page-builder widgets can
43 + // enqueue the stylesheet by handle in the <head> via get_style_depends() /
44 + // enqueue_scripts(). Registration is unconditional and cheap; the actual
45 + // enqueue below stays gated on the block/shortcode being present.
46 + add_action( 'wp_enqueue_scripts', [ $this, 'register_assets' ], 1 );
47 + // accepted_args 0: WordPress fires this hook with an empty-string sentinel arg,
48 + // which would land in $from_render (falsy, so harmless, but it contradicts the
49 + // bool contract). Capping to 0 args means the bool default (false) is used.
50 + add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ], 10, 0 );
42 51
43 52 // Frontend AJAX handlers.
44 53 add_action( 'wp_ajax_srfm_frontend_cancel_subscription', [ $this, 'ajax_cancel_subscription' ] );
45 54 }
@@ -44,31 +53,27 @@
44 53 add_action( 'wp_ajax_srfm_frontend_cancel_subscription', [ $this, 'ajax_cancel_subscription' ] );
45 54 }
46 55
47 56 /**
48 - * Conditionally enqueue assets when the shortcode is present on the page.
57 + * Register the payment-history stylesheet and script handles unconditionally so
58 + * they can later be enqueued by handle. This exists for the Elementor
59 + * (get_style_depends()) and Bricks (enqueue_scripts()) payment-history widgets,
60 + * whose content lives in postmeta and so isn't caught by the has_block() /
61 + * has_shortcode() gate in enqueue_assets(); declaring the style as a widget
62 + * dependency lets those builders load it in the <head> and avoid a FOUC.
49 63 *
50 - * Also called at render time (shortcode/block callback) to support
51 - * FSE themes and page builders where global $post is unavailable.
64 + * Registering (not enqueuing) keeps the assets off pages that don't use the
65 + * feature — nothing is printed until something enqueues the handle.
52 66 *
53 - * CSS is always enqueued during wp_enqueue_scripts (lightweight, prevents FOUC
54 - * on page builders like Elementor/Bricks that store content in postmeta).
55 - * JS + localized data are only enqueued when the shortcode/block is detected.
56 - *
57 - * @since 2.8.0
67 + * @since 2.12.3
58 68 * @return void
59 69 */
60 - public function enqueue_assets() {
70 + public function register_assets() {
61 71 $file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
62 72 $dir_name = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
63 73
64 - // Always enqueue CSS during wp_enqueue_scripts to ensure it loads in <head>.
65 - // WordPress silently ignores late-enqueued styles (after wp_head), so page builders
66 - // like Elementor, Bricks, and Beaver Builder (which store content in postmeta, not
67 - // post_content) would get no CSS at all if we only enqueued conditionally.
68 - // The CSS file is lightweight — one small stylesheet on frontend pages is acceptable.
69 - if ( doing_action( 'wp_enqueue_scripts' ) && ! wp_style_is( 'srfm-payment-history', 'enqueued' ) ) {
70 - wp_enqueue_style(
74 + if ( ! wp_style_is( 'srfm-payment-history', 'registered' ) ) {
75 + wp_register_style(
71 76 'srfm-payment-history',
72 77 SRFM_URL . 'assets/css/' . $dir_name . '/payment-history' . $file_prefix . '.css',
73 78 [],
74 79 SRFM_VER
@@ -74,17 +79,52 @@
74 79 SRFM_VER
75 80 );
76 81 }
77 82
78 - // JS + localized data: only enqueue when the shortcode/block is actually present.
79 - // JS can be late-enqueued (footer scripts) but CSS cannot, hence the split above.
80 - if ( wp_script_is( 'srfm-payment-history', 'enqueued' ) ) {
81 - return;
83 + if ( ! wp_script_is( 'srfm-payment-history', 'registered' ) ) {
84 + wp_register_script(
85 + 'srfm-payment-history',
86 + SRFM_URL . 'assets/js/payment-history.js',
87 + [],
88 + SRFM_VER,
89 + true
90 + );
82 91 }
92 + }
83 93
84 - // When called from the wp_enqueue_scripts hook, check if the shortcode/block is present.
85 - // When called from render(), we know it's needed — skip the check.
86 - if ( doing_action( 'wp_enqueue_scripts' ) ) {
94 + /**
95 + * Conditionally enqueue assets only when the payment history block/shortcode is present.
96 + *
97 + * Runs on wp_enqueue_scripts (where the global $post is available for detection) and
98 + * again at render time (shortcode/block callback). Elementor and Bricks store their
99 + * content in postmeta rather than post_content, so has_block()/has_shortcode() can't
100 + * detect them here — those widgets instead declare the (pre-registered) stylesheet as
101 + * a dependency so it loads in the <head> (see register_assets() + the widget classes).
102 + *
103 + * Both the stylesheet and the script are gated on the block/shortcode actually being
104 + * present, so the CSS is no longer loaded on every frontend page. When enqueued from
105 + * render() the stylesheet is printed with the footer styles, which is acceptable for the
106 + * rare case of the block placed via an FSE template part or block widget.
107 + *
108 + * The stylesheet is always enqueued for a detected placement (logged in or out) so the
109 + * login message stays styled; the script + localized nonce are enqueued only for
110 + * logged-in users, since the cancel handler re-checks auth server-side and there is no
111 + * `wp_ajax_nopriv_` endpoint — a logged-out visitor would only receive an inert script.
112 + *
113 + * @since 2.8.0
114 + * @since 2.12.3 Enqueue the stylesheet only when the block/shortcode is present instead of on every frontend page; withhold the script + nonce from logged-out visitors.
115 + * @param bool $from_render Whether this is the render()-time fallback call. When
116 + * true the block/shortcode presence gate is skipped
117 + * because render() only runs when the widget is on the
118 + * page. Passed explicitly rather than sniffed via
119 + * doing_action(), which would also match a nested
120 + * do_shortcode() invoked inside a wp_enqueue_scripts callback.
121 + * @return void
122 + */
123 + public function enqueue_assets( $from_render = false ) {
124 + // On the wp_enqueue_scripts hook, confirm the shortcode/block is present on the
125 + // current page. From render() we already know it is needed.
126 + if ( ! $from_render ) {
87 127 global $post;
88 128
89 129 if ( ! $post instanceof \WP_Post ) {
90 130 return;
@@ -97,25 +137,46 @@
97 137 return;
98 138 }
99 139 }
100 140
101 - wp_enqueue_script(
102 - 'srfm-payment-history',
103 - SRFM_URL . 'assets/js/payment-history.js',
104 - [],
105 - SRFM_VER,
106 - true
107 - );
141 + // Ensure both handles exist (register_assets() is idempotent). Guarding on only
142 + // the style handle would miss a script handle that was separately deregistered
143 + // (asset-optimisation plugins do this by handle), leaving wp_localize_script()
144 + // below with nothing to attach to and silently dropping the nonce.
145 + $this->register_assets();
108 146
109 - wp_localize_script(
110 - 'srfm-payment-history',
111 - 'srfm_payment_history',
112 - [
113 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
114 - 'nonce' => wp_create_nonce( 'srfm_frontend_payment_nonce' ),
115 - 'i18n' => $this->get_i18n_strings(),
116 - ]
117 - );
147 + // Enqueue the stylesheet only for pages that actually use payment history.
148 + if ( ! wp_style_is( 'srfm-payment-history', 'enqueued' ) ) {
149 + wp_enqueue_style( 'srfm-payment-history' );
150 + }
151 +
152 + // JS + localized data are only useful to logged-in users: the cancel handler
153 + // re-checks authentication server-side and there is no `wp_ajax_nopriv_`
154 + // registration, so an anonymous visitor (who only ever sees the login message)
155 + // would receive an inert script and a pointless nonce. The CSS above still loads
156 + // so the login message stays styled; only the script + localize are gated here.
157 + if ( ! is_user_logged_in() ) {
158 + return;
159 + }
160 +
161 + if ( ! wp_script_is( 'srfm-payment-history', 'enqueued' ) ) {
162 + wp_enqueue_script( 'srfm-payment-history' );
163 + }
164 +
165 + // Gate the localize on whether the data is already attached, not on the enqueued
166 + // state: the handle is now registered on every frontend page, so a foreign
167 + // enqueue-by-handle before this runs must not cause the nonce to be skipped.
168 + if ( ! wp_scripts()->get_data( 'srfm-payment-history', 'data' ) ) {
169 + wp_localize_script(
170 + 'srfm-payment-history',
171 + 'srfm_payment_history',
172 + [
173 + 'ajax_url' => admin_url( 'admin-ajax.php' ),
174 + 'nonce' => wp_create_nonce( 'srfm_frontend_payment_nonce' ),
175 + 'i18n' => $this->get_i18n_strings(),
176 + ]
177 + );
178 + }
118 179 }
119 180
120 181 /**
121 182 * Render the payment history shortcode.
@@ -138,15 +199,19 @@
138 199 if ( $per_page <= 0 ) {
139 200 $per_page = 10;
140 201 }
141 202
203 + // Enqueue assets at render time — the last-resort fallback for FSE template
204 + // parts / block widgets where $post can't be detected on wp_enqueue_scripts and
205 + // there is no builder style-dependency API. Elementor/Bricks widgets enqueue the
206 + // stylesheet in the <head> via their own dependency hooks, so this mainly serves
207 + // the genuinely rare FSE case (footer-loaded CSS, acceptable there). Runs before
208 + // the logged-out early return so the login message is styled too.
209 + $this->enqueue_assets( true );
210 +
142 211 if ( ! is_user_logged_in() ) {
143 212 return $this->get_login_message();
144 213 }
145 -
146 - // Enqueue assets at render time — handles FSE themes, Elementor, and
147 - // other page builders where global $post is unavailable during wp_enqueue_scripts.
148 - $this->enqueue_assets();
149 214
150 215 $user_id = get_current_user_id();
151 216 $where = $this->build_where_conditions( $user_id, $atts );
152 217