PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13.1
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13.1
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
← All changes | includes/admin/campaign-builder/fields/class-donate-button.php +92 -1 1.8.13 → 1.8.13.1 View file →
@@ -138,8 +138,51 @@
138 138
139 139 // We want the button to contain a donation amount, but we go with what is in the SESSION first, then the DEFAULT amount.
140 140 $donation_amount = $campaign->get_donation_amount_in_session();
141 141
142 + /*
143 + * Nothing in the session means a first-time visitor, who still sees one of the
144 + * suggested amounts rendered as pre-selected by the Donate Amount field. Without
145 + * falling back to it here, accepting that default and clicking straight through
146 + * posted an empty amount. The default index is read from the same place
147 + * class-donate-amount.php reads it so the button and the checked radio cannot
148 + * disagree. Skipped when the campaign has no Donate Amount field, since a
149 + * button-only layout should leave the donation form to ask for the amount.
150 + */
151 + if ( ! $donation_amount
152 + && is_array( $campaign_data )
153 + && ! empty( $campaign_data['layout'] )
154 + && $this->layout_has_field_type( $campaign_data['layout'], 'donate-amount' ) ) {
155 +
156 + $suggested_donations = $campaign->get_suggested_donations();
157 +
158 + if ( ! empty( $suggested_donations ) ) {
159 + $default_index = ! empty( $campaign_data['settings']['donation-options']['suggested_donations_default'] )
160 + ? intval( $campaign_data['settings']['donation-options']['suggested_donations_default'] )
161 + : 0;
162 +
163 + /*
164 + * is_scalar() because a row imported or edited by hand can carry an array
165 + * where an amount belongs, which would otherwise reach esc_attr() and emit
166 + * an "Array to string conversion" warning on the campaign page.
167 + */
168 + if ( isset( $suggested_donations[ $default_index ]['amount'] )
169 + && is_scalar( $suggested_donations[ $default_index ]['amount'] ) ) {
170 + $donation_amount = $suggested_donations[ $default_index ]['amount'];
171 + }
172 + }
173 + }
174 +
175 + /*
176 + * An empty session comes back from get_donation_amount_in_session() as integer 0.
177 + * Keep the field empty in that case rather than printing a literal zero: the
178 + * processor reads the two the same way, but an empty value is what this field has
179 + * always emitted, so button-only campaigns keep byte-identical markup.
180 + */
181 + if ( ! $donation_amount ) {
182 + $donation_amount = '';
183 + }
184 +
142 185 ob_start();
143 186
144 187 ?>
145 188
@@ -190,9 +233,9 @@
190 233 <form class="campaign-donation" method="post">
191 234 <?php wp_nonce_field( 'charitable-donate', 'charitable-donate-now' ); ?>
192 235 <input type="hidden" name="charitable_action" value="start_donation" />
193 236 <input type="hidden" name="campaign_id" value="<?php echo esc_html( $campaign->ID ); ?>" />
194 - <input type="hidden" name="charitable_donation_amount" value="<?php $donation_amount; ?>" />
237 + <input type="hidden" name="charitable_donation_amount" value="<?php echo esc_attr( $donation_amount ); ?>" />
195 238 <input type="hidden" name="charitable_builder" value="true" />
196 239 <button type="submit" name="charitable_submit" class="<?php echo esc_attr( charitable_get_button_class( 'donate' ) ); ?>"><?php echo esc_html( wp_strip_all_tags( $button_label ) ); ?></button>
197 240 </form>
198 241
@@ -208,8 +251,56 @@
208 251
209 252 $html = $this->field_display_wrapper( $html, $field_data );
210 253
211 254 echo apply_filters( 'charitable_campaign_builder_' . $this->type . '_field_display', $html, $campaign_data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
255 + }
256 +
257 + /**
258 + * Whether a field type appears anywhere in a campaign's builder layout.
259 + *
260 + * Used to decide whether the donate button should inherit the pre-selected suggested
261 + * amount: a campaign that lays out no amounts has nothing for the button to inherit,
262 + * and should keep sending the donor to the donation form to choose.
263 + *
264 + * The layout nests rows inside columns inside sections, and tabs alongside them, with
265 + * each recording its field types in a 'fields' list. This walks the structure rather
266 + * than assuming a depth. The entries in $campaign_data['fields'] hold only a field's
267 + * own settings and never its type, which is why the layout is what gets consulted.
268 + *
269 + * Only 'fields' lists are matched against. Reading every string in the layout would
270 + * also read presentational values, and a row's css_class is free text that can itself
271 + * be set to a field slug, which would report a field the campaign does not lay out.
272 + *
273 + * @since 1.8.13.1
274 + *
275 + * @param mixed $layout Layout node to search.
276 + * @param string $type Field type slug to look for.
277 + * @param int $depth Current recursion depth, to bound malformed data.
278 + * @return boolean
279 + */
280 + protected function layout_has_field_type( $layout, $type, $depth = 0 ) {
281 +
282 + if ( ! is_array( $layout ) || $depth > 32 ) {
283 + return false;
284 + }
285 +
286 + foreach ( $layout as $key => $node ) {
287 +
288 + if ( ! is_array( $node ) ) {
289 + continue;
290 + }
291 +
292 + /* Section-level 'fields' lists hold field IDs, so the comparison is strict. */
293 + if ( 'fields' === $key && in_array( $type, $node, true ) ) {
294 + return true;
295 + }
296 +
297 + if ( $this->layout_has_field_type( $node, $type, $depth + 1 ) ) {
298 + return true;
299 + }
300 + }
301 +
302 + return false;
212 303 }
213 304
214 305 /**
215 306 * The display on the form settings backend when the user clicks on the field/block.