PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13
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/public/charitable-template-helpers.php +179 -27 1.6.6 → 1.8.13 View file →
@@ -3,17 +3,20 @@
3 3 * Charitable Template Helpers.
4 4 *
5 5 * Functions used to assist with rendering templates.
6 6 *
7 - * @package Charitable/Functions/Templates
8 - * @version 1.2.0
9 - * @author Eric Daams
10 - * @copyright Copyright (c) 2018, Studio 164a
11 - * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
7 + * @package Charitable/Functions/Templates
8 + * @author David Bisset
9 + * @copyright Copyright (c) 2023, WP Charitable LLC
10 + * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
11 + * @since 1.0.0
12 + * @version 1.8.13
12 13 */
13 14
14 15 // Exit if accessed directly.
15 -if ( ! defined( 'ABSPATH' ) ) { exit; }
16 +if ( ! defined( 'ABSPATH' ) ) {
17 + exit;
18 +}
16 19
17 20 /**
18 21 * Displays a template.
19 22 *
@@ -64,9 +67,9 @@
64 67 ob_start();
65 68
66 69 charitable_template( $template_name, $args, $classname );
67 70
68 - echo charitable_template_from_session_content( $template_key, $wrapper_args, ob_get_clean() );
71 + echo charitable_template_from_session_content( $template_key, $wrapper_args, ob_get_clean() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
69 72 }
70 73
71 74 /**
72 75 * Returns a piece of content or an empty string, wrapped in the correct markup
@@ -88,9 +91,9 @@
88 91 if ( charitable_get_session()->has_session_id() ) {
89 92 return $default;
90 93 }
91 94
92 - $content = '<div class="charitable-session-content" data-template="' . esc_attr( $template_key ) . '" data-args="' . esc_attr( json_encode( $wrapper_args ) ) . '" style="display: none;">' . $default . '</div>';
95 + $content = '<div class="charitable-session-content" data-template="' . esc_attr( $template_key ) . '" data-args="' . esc_attr( wp_json_encode( $wrapper_args ) ) . '" style="display: none;">' . $default . '</div>';
93 96 $content .= '<noscript>' . $default . '</noscript>';
94 97
95 98 return $content;
96 99 }
@@ -99,13 +102,14 @@
99 102 * Return the template path if the template exists. Otherwise, return default.
100 103 *
101 104 * @since 1.0.0
102 105 *
103 - * @param string|string[] $template
106 + * @param string|string[] $template The template to load.
107 + * @param string $default The default to load if the template file doesn't exist.
104 108 * @return string The template path if the template exists. Otherwise, return default.
105 109 */
106 110 function charitable_get_template_path( $template, $default = '' ) {
107 - $t = new Charitable_Template( $template, false );
111 + $t = new Charitable_Template( $template, false );
108 112 $path = $t->locate_template();
109 113
110 114 if ( ! file_exists( $path ) ) {
111 115 $path = $default;
@@ -202,9 +206,10 @@
202 206 * Filter the classes to be applied to the campaign loop.
203 207 *
204 208 * @since 1.5.7
205 209 *
206 - * @param array $classes Array of classes.
210 + * @param array $classes Array of classes.
211 + * @param array $view_args View args.
207 212 */
208 213 $classes = apply_filters( 'charitable_campaign_loop_classes', $classes, $view_args );
209 214
210 215 return implode( ' ', $classes );
@@ -230,14 +235,18 @@
230 235 if ( ! array_key_exists( $attr, $field ) || array_key_exists( $attr, $field['attrs'] ) ) {
231 236 continue;
232 237 }
233 238
234 - /* Skip required attribute unless it's true. */
235 - if ( 'required' == $attr && ! $field['required'] ) {
236 - continue;
239 + if ( 'required' == $attr ) {
240 + /* Skip required attribute if it's not true. */
241 + if ( ! $field['required'] ) {
242 + continue;
243 + }
244 +
245 + $field['attrs'][ $attr ] = 'required';
246 + } else {
247 + $field['attrs'][ $attr ] = $field[ $attr ];
237 248 }
238 -
239 - $field['attrs'][ $attr ] = $field[ $attr ];
240 249 }
241 250
242 251 $output = '';
243 252
@@ -256,26 +265,101 @@
256 265 * Charitable's filters touching other the_content instances outside the main
257 266 * loop.
258 267 *
259 268 * @since 1.4.11
269 + * @since 1.8.1 added 'charitable_is_main_loop' filter.
260 270 *
261 - * @return boolean
271 + * @return boolean
262 272 */
263 273 function charitable_is_main_loop() {
264 - return is_single() && in_the_loop() && is_main_query();
274 + $is_main_loop = is_single() && in_the_loop() && is_main_query();
275 + return apply_filters( 'charitable_is_main_loop', $is_main_loop );
265 276 }
266 277
267 278 /**
279 + * Re-encodes a raw query string so that every key and value is percent-encoded.
280 + *
281 + * The query string arrives from the request untrusted and unencoded. Rebuilding it
282 + * pair by pair guarantees the result contains no character that could break out of
283 + * the HTML attribute, JavaScript string or URL context it is later printed into --
284 + * including HTML entities such as `&apos;` that survive esc_url() and are decoded
285 + * back into a quote by the browser's HTML parser.
286 + *
287 + * Query strings are application/x-www-form-urlencoded, so urldecode()/urlencode()
288 + * is the correct pair here: it round-trips `+` as a space the way the request did.
289 + *
290 + * @since 1.8.13
291 + *
292 + * @param string $query_string Raw query string, without the leading '?'.
293 + * @return string Re-encoded query string, or an empty string if nothing usable remains.
294 + */
295 +function charitable_sanitize_query_string( $query_string ) {
296 + $pairs = array();
297 +
298 + foreach ( explode( '&', (string) $query_string ) as $pair ) {
299 + if ( '' === $pair ) {
300 + continue;
301 + }
302 +
303 + $parts = explode( '=', $pair, 2 );
304 + $key = urlencode( urldecode( $parts[0] ) );
305 +
306 + if ( '' === $key ) {
307 + continue;
308 + }
309 +
310 + $pairs[] = isset( $parts[1] ) ? $key . '=' . urlencode( urldecode( $parts[1] ) ) : $key;
311 + }
312 +
313 + return implode( '&', $pairs );
314 +}
315 +
316 +/**
268 317 * Returns the current URL.
269 318 *
270 319 * @see https://gist.github.com/leereamsnyder/fac3b9ccb6b99ab14f36
271 320 *
272 321 * @since 1.0.0
322 + * @since 1.8.13 The path and query string are both percent-encoded instead of being
323 + * reflected raw, and the trailing slash is applied to the path rather
324 + * than to the end of the query string.
273 325 *
326 + * @global WP $wp
274 327 * @return string
275 328 */
276 329 function charitable_get_current_url() {
277 - return home_url( add_query_arg( null, null ) );
330 + global $wp;
331 +
332 + $path = isset( $wp->request ) ? (string) $wp->request : '';
333 +
334 + /*
335 + * The requested path is reflected here too, and WordPress hands it over exactly as
336 + * it arrived: any %XX escapes are already inert, but characters that arrived
337 + * literally are not. Encoding '&' closes off every HTML entity form at once
338 + * (&apos;, &#39;, &#x27;, &quot;, ...), which is what a URL escaper cannot do
339 + * because those entities are made of characters it considers legal; the quotes and
340 + * angle brackets cover the literal cases. Only these five are touched, so existing
341 + * %XX escapes and ordinary slugs pass through unchanged and the result is idempotent.
342 + */
343 + $path = str_replace(
344 + array( '&', "'", '"', '<', '>' ),
345 + array( '%26', '%27', '%22', '%3C', '%3E' ),
346 + $path
347 + );
348 +
349 + $url = trailingslashit( home_url( $path ) );
350 +
351 + if ( empty( $_SERVER['QUERY_STRING'] ) ) {
352 + return $url;
353 + }
354 +
355 + $query_string = charitable_sanitize_query_string( wp_unslash( $_SERVER['QUERY_STRING'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
356 +
357 + if ( '' === $query_string ) {
358 + return $url;
359 + }
360 +
361 + return $url . '?' . $query_string;
278 362 }
279 363
280 364 /**
281 365 * Returns the URL to which the user should be redirected after signing on or registering an account.
@@ -284,10 +368,10 @@
284 368 *
285 369 * @return string
286 370 */
287 371 function charitable_get_login_redirect_url() {
288 - if ( isset( $_REQUEST['redirect_to'] ) ) {
289 - $redirect = $_REQUEST['redirect_to'];
372 + if ( isset( $_REQUEST['redirect_to'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
373 + $redirect = $_REQUEST['redirect_to']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
290 374 } elseif ( charitable_get_permalink( 'profile_page' ) ) {
291 375 $redirect = charitable_get_permalink( 'profile_page' );
292 376 } else {
293 377 $redirect = home_url();
@@ -314,16 +398,84 @@
314 398 /**
315 399 * Return the email verification link.
316 400 *
317 401 * @since 1.5.0
402 + * @since 1.6.32 Added $force_send argument.
318 403 *
319 404 * @param WP_User $user An instance of `WP_User`.
320 405 * @param string|false $redirect_url The URL to redirect to after verification.
406 + * @param boolean $force_send Whether the link should include an argument to force
407 + * resending the email, even if it was sent recently.
321 408 * @return string
322 409 */
323 -function charitable_get_email_verification_link( WP_User $user, $redirect_url = false ) {
324 - return add_query_arg( array(
325 - 'charitable_action' => 'verify_email',
326 - 'user' => $user->ID,
327 - 'redirect_url' => $redirect_url,
328 - ) );
410 +function charitable_get_email_verification_link( WP_User $user, $redirect_url = false, $force_send = false ) {
411 + return add_query_arg(
412 + array(
413 + 'charitable_action' => 'verify_email',
414 + 'user' => $user->ID,
415 + 'redirect_url' => $redirect_url,
416 + 'force_send' => (int) $force_send,
417 + )
418 + );
329 419 }
420 +
421 +/**
422 + * Get the button classes for a Charitable button.
423 + *
424 + * @since 1.6.29
425 + * @version 1.8.3.6 Added $disabled parameter.
426 + *
427 + * @param string $button Which button we're showing.
428 + * @return string
429 + */
430 +function charitable_get_button_class( $button, $disabled = false ) {
431 + $classes = [ 'button', 'charitable-button' ];
432 +
433 + switch ( $button ) {
434 + case 'donate':
435 + $classes[] = 'donate-button';
436 + $classes[] = 'button-primary';
437 + break;
438 +
439 + default:
440 + $classes[] = $button . '-button';
441 + }
442 +
443 + if ( $disabled ) {
444 + $classes[] = 'charitable-disabled';
445 + }
446 +
447 + /**
448 + * Filter the button classes.
449 + *
450 + * @since 1.6.29
451 + *
452 + * @param array $classes The button classes.
453 + * @param string $button The specific button we're showing.
454 + */
455 + $classes = apply_filters( 'charitable_button_class', $classes, $button );
456 +
457 + return implode( ' ', $classes );
458 +}
459 +
460 +/**
461 + * Get a unique donation form ID.
462 + *
463 + * @since 1.6.57
464 + *
465 + * @return string
466 + */
467 +function charitable_get_donation_form_id() {
468 + static $form_number;
469 +
470 + if ( ! isset( $form_number ) ) {
471 + $form_id = 'charitable-donation-form';
472 + $form_number = 1;
473 + } else {
474 + $form_id = 'charitable-donation-form-' . $form_number;
475 + $form_number += 1;
476 + }
477 +
478 + // $form_number = ;
479 +
480 + return $form_id;
481 +}