PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.12.3
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.12.3
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 1.7.0.9 All 211 releases
charitable / includes / public / class-charitable-session.php

class-charitable-session.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.12.3, at includes/public/class-charitable-session.php

508 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Responsible for managing user sessions.
4 *
5 * @package Charitable/Classes/Charitable Session
6 * @copyright Copyright (c) 2023, WP Charitable LLC
7 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
8 * @since 1.0.0
9 * @version 1.8.9.1
10 */
11
12 // Exit if accessed directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 if ( ! class_exists( 'Charitable_Session' ) ) :
18
19 /**
20 * Charitable_Session
21 *
22 * @since 1.0.0
23 */
24 class Charitable_Session {
25
26 /**
27 * Holds our session data
28 *
29 * @since 1.0.0
30 *
31 * @var WP_Session
32 */
33 private $session;
34
35 /**
36 * Whether to disable the cookie.
37 *
38 * @since 1.6.27
39 *
40 * @var boolean
41 */
42 private $disable_cookie;
43
44 /**
45 * Instantiate session object.
46 *
47 * @since 1.0.0
48 * @since 1.5.4 Access changed to public.
49 */
50 public function __construct() {
51 /**
52 * Filter to disable the cookie.
53 *
54 * @since 1.6.27
55 *
56 * @param boolean $disable Whether to disable the cookie.
57 */
58 $this->disable_cookie = apply_filters( 'charitable_disable_cookie', false );
59
60 if ( ! $this->should_start_session() ) {
61 return;
62 }
63
64 if ( ! defined( 'WP_SESSION_COOKIE' ) ) {
65 define( 'WP_SESSION_COOKIE', 'charitable_session' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- This is a bundled library constant. Changing it would break existing functionality.
66 }
67
68 if ( ! class_exists( 'Recursive_ArrayAccess' ) ) {
69 require_once( charitable()->get_path( 'includes' ) . 'libraries/wp-session/class-recursive-arrayaccess.php' );
70 }
71
72 if ( ! class_exists( 'WP_Session_Utils' ) ) {
73 require_once( charitable()->get_path( 'includes' ) . 'libraries/wp-session/class-wp-session-utils.php' );
74 }
75
76 if ( defined( 'WP_CLI' ) && WP_CLI ) {
77 require_once( charitable()->get_path( 'includes' ) . 'libraries/wp-session/wp-cli.php' );
78 }
79
80 if ( ! class_exists( 'WP_Session' ) ) {
81 require_once( charitable()->get_path( 'includes' ) . 'libraries/wp-session/class-wp-session.php' );
82 require_once( charitable()->get_path( 'includes' ) . 'libraries/wp-session/wp-session.php' );
83 }
84
85 /* Set the expiration length & variant of the session */
86 add_filter( 'wp_session_expiration', array( $this, 'set_session_length' ), 99999 );
87 add_filter( 'wp_session_expiration_variant', array( $this, 'set_session_expiration_variant_length' ), 99999 );
88
89 $this->init();
90 }
91
92 /**
93 * Create the session.
94 *
95 * @since 1.4.17
96 *
97 * @return WP_Session
98 */
99 public function init() {
100 $this->session = WP_Session::get_instance();
101
102 /* We're missing a Session ID, so we'll need to queue up our session scripts. */
103 if ( ! $this->has_session_id() || $this->disable_cookie ) {
104 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ), 1 );
105 }
106
107 return $this->session;
108 }
109
110 /**
111 * Set up scripts to create & manage cookies client-side.
112 *
113 * @since 1.5.0
114 *
115 * @return void
116 */
117 public function enqueue_scripts() {
118
119 $min = charitable_get_min_suffix();
120 $version = charitable()->get_version();
121
122 $assets_dir = charitable()->get_path( 'assets', false );
123
124 wp_register_script(
125 'js-cookie',
126 $assets_dir . 'js/libraries/js-cookie' . $min . '.js',
127 array(),
128 '2.1.4',
129 false
130 );
131
132 wp_enqueue_script(
133 'charitable-sessions',
134 $assets_dir . 'js/charitable-session' . $min . '.js',
135 array( 'js-cookie' ),
136 $version,
137 false
138 );
139
140 wp_localize_script(
141 'charitable-sessions',
142 'CHARITABLE_SESSION',
143 array(
144 'ajaxurl' => admin_url( 'admin-ajax.php' ),
145 'id' => $this->get_session_id(),
146 'cookie_name' => WP_SESSION_COOKIE,
147 'expiration' => $this->set_session_length(),
148 'expiration_variant' => $this->set_session_expiration_variant_length(),
149 'secure' => (bool) apply_filters( 'wp_session_cookie_secure', false ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- This is a bundled library hook. Changing it would break existing functionality.
150 'cookie_path' => COOKIEPATH,
151 'cookie_domain' => COOKIE_DOMAIN,
152 'generated_id' => WP_Session_Utils::generate_id(),
153 'disable_cookie' => $this->disable_cookie,
154 )
155 );
156 }
157
158 /**
159 * Returns and/or create the single instance of this class.
160 *
161 * @since 1.2.0
162 *
163 * @return Charitable_Session
164 */
165 public static function get_instance() {
166 return charitable()->registry()->get( 'session' );
167 }
168
169 /**
170 * Checks whether the current request has a session ID.
171 *
172 * @since 1.5.0
173 *
174 * @return boolean
175 */
176 public function has_session_id() {
177 return ! empty( $this->session->session_id );
178 }
179
180 /**
181 * Return a session variable.
182 *
183 * @since 1.0.0
184 *
185 * @param string $key Session variable key.
186 * @return mixed Session variable
187 */
188 public function get( $key ) {
189 $key = sanitize_key( $key );
190 return isset( $this->session[ $key ] ) ? maybe_unserialize( $this->session[ $key ] ) : false;
191 }
192
193 /**
194 * Set a session variable.
195 *
196 * @since 1.0.0
197 *
198 * @param string $key Session variable key.
199 * @param mixed $value The value of the session variable.
200 * @return mixed The session variable value.
201 */
202 public function set( $key, $value ) {
203 $key = sanitize_key( $key );
204
205 if ( is_array( $value ) ) {
206 $this->session[ $key ] = serialize( $value );
207 } else {
208 $this->session[ $key ] = $value;
209 }
210
211 return $this->session[ $key ];
212 }
213
214 /**
215 * Remove a session variable.
216 *
217 * @since 1.5.0
218 *
219 * @param string $key Session variable key.
220 * @return mixed The session variable value.
221 */
222 public function remove( $key ) {
223 unset( $this->session[ $key ] );
224 }
225
226 /**
227 * Set the length of the cookie session to 24 hours.
228 *
229 * @since 1.0.0
230 *
231 * @return int
232 */
233 public function set_session_length() {
234 if ( ! defined( 'DAY_IN_SECONDS' ) ) {
235 define( 'DAY_IN_SECONDS', 86400 );
236 }
237
238 /**
239 * Filter the length of the session.
240 *
241 * @since 1.0.0
242 *
243 * @param int $seconds Defaults to 86400 — one day.
244 */
245 return apply_filters( 'charitable_session_length', DAY_IN_SECONDS );
246 }
247
248 /**
249 * Set the cookie expiration variant time to 23 hours.
250 *
251 * @since 1.0.0
252 *
253 * @return int
254 */
255 public function set_session_expiration_variant_length() {
256 if ( ! defined( 'HOUR_IN_SECONDS' ) ) {
257 define( 'HOUR_IN_SECONDS', 3600 );
258 }
259
260 /**
261 * Filter the variation length of the session.
262 *
263 * @since 1.0.0
264 *
265 * @param int $seconds Defaults to 82800 — 23 hours.
266 */
267 return apply_filters( 'charitable_session_expiration_variant_length', HOUR_IN_SECONDS * 23 );
268 }
269
270 /**
271 * Add a donation to a campaign to the session.
272 *
273 * @since 1.0.0
274 * @since 1.6.25 Added $period parameter.
275 *
276 * @param int $campaign_id Campaign ID.
277 * @param int $amount Donation amount.
278 * @param string $period Set the donation period. Defaults to once.
279 * @return void
280 */
281 public function add_donation( $campaign_id, $amount, $period = 'once' ) {
282 $donations = $this->get( 'donations' );
283
284 /*
285 * get() returns false when the key is not in the session, which is the normal state for the
286 * first donation of a fresh session. Assigning $donations[ $campaign_id ] to that false
287 * relied on PHP silently converting it to an array - deprecated in PHP 8.1 and a fatal
288 * TypeError in PHP 9, on the donation path. add_donation_key() below already guards exactly
289 * this way; this method was simply missing it.
290 */
291 if ( ! is_array( $donations ) ) {
292 $donations = array();
293 }
294
295 $campaign_donation = isset( $donations[ $campaign_id ] ) ? $donations[ $campaign_id ] : array();
296 $campaign_donation['amount'] = floatval( charitable_get_currency_helper()->cast_to_decimal_format( $amount ) );
297 $campaign_donation['donation_period'] = $period;
298
299 $donations[ $campaign_id ] = $campaign_donation;
300
301 $this->set( 'donations', $donations );
302 }
303
304 /**
305 * Remove a donation from the session.
306 *
307 * @since 1.0.0
308 *
309 * @param int $campaign_id Campaign ID.
310 * @return void
311 */
312 public function remove_donation( $campaign_id ) {
313 $donations = $this->get( 'donations' );
314
315 if ( isset( $donations[ $campaign_id ] ) ) {
316 unset( $donations[ $campaign_id ] );
317 }
318
319 $this->set( 'donations', $donations );
320 }
321
322 /**
323 * Return the donation in session for a campaign.
324 *
325 * @since 1.0.0
326 *
327 * @param int $campaign_id The campaign ID.
328 * @return false|array
329 */
330 public function get_donation_by_campaign( $campaign_id ) {
331 $donations = $this->get( 'donations' );
332 return isset( $donations[ $campaign_id ] ) ? $donations[ $campaign_id ] : false;
333 }
334
335 /**
336 * Store the donation key in the session, to ensure the user can access their receipt.
337 *
338 * @since 1.1.2
339 *
340 * @param string $donation_key The transaction key for the donation.
341 * @return void
342 */
343 public function add_donation_key( $donation_key ) {
344 $keys = $this->get( 'donation-keys' );
345
346 if ( ! $keys ) {
347 $keys = array();
348 }
349
350 $keys[] = $donation_key;
351
352 $this->set( 'donation-keys', $keys );
353 }
354
355 /**
356 * Checks whether the donation key is stored in the session.
357 *
358 * @since 1.0.0
359 *
360 * @param string $donation_key The transaction key for the donation.
361 * @return boolean
362 */
363 public function has_donation_key( $donation_key ) {
364 $keys = $this->get( 'donation-keys' );
365
366 if ( ! $keys ) {
367 return false;
368 }
369
370 return in_array( $donation_key, $keys );
371 }
372
373 /**
374 * Add the all notices to the session.
375 *
376 * @since 1.0.0
377 *
378 * @return void
379 */
380 public function add_notices() {
381 $this->set( 'notices', charitable_get_notices()->get_notices() );
382 }
383
384 /**
385 * Return any notices set in the session.
386 *
387 * @since 1.4.0
388 *
389 * @return array Session variable
390 */
391 public function get_notices() {
392 $notices = $this->get( 'notices' );
393
394 if ( $notices ) {
395 return $notices;
396 }
397
398 return array(
399 'error' => array(),
400 'warning' => array(),
401 'success' => array(),
402 'info' => array(),
403 );
404 }
405
406 /**
407 * Returns the session ID.
408 *
409 * @since 1.3.5
410 *
411 * @return string Session ID
412 */
413 public function get_session_id() {
414 if ( isset( $this->session->session_id ) ) {
415 return $this->session->session_id;
416 } else {
417 return false;
418 }
419 }
420
421 /**
422 * Determines if we should start sessions
423 *
424 * @since 1.4.17
425 *
426 * @return boolean
427 */
428 public function should_start_session() {
429 $start_session = true;
430
431 if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
432 $blocklist = $this->get_blocklist();
433 $uri = ltrim( $_SERVER['REQUEST_URI'], '/' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
434 $uri = untrailingslashit( $uri );
435
436 if ( in_array( $uri, $blocklist ) ) {
437 $start_session = false;
438 }
439
440 if ( false !== strpos( $uri, 'feed=' ) ) {
441 $start_session = false;
442 }
443 }
444
445 /**
446 * Filter whether the session should be started for the current request.
447 *
448 * @since 1.4.17
449 *
450 * @param boolean $start_session Whether to start the session.
451 */
452 return apply_filters( 'charitable_start_session', $start_session );
453 }
454
455 /**
456 * Retrieve the URI blocklist.
457 *
458 * These are the URIs where we never start sessions.
459 *
460 * @since 1.4.17
461 *
462 * @return array
463 */
464 public function get_blocklist() {
465 /**
466 * Filter the blocklist of URIs where sessions should not be started.
467 *
468 * @since 1.4.17
469 *
470 * @param string[] $blocklist Array of URIs.
471 */
472 $blocklist = apply_filters(
473 'charitable_session_start_uri_blocklist',
474 array(
475 'feed',
476 'feed/rss',
477 'feed/rss2',
478 'feed/rdf',
479 'feed/atom',
480 'comments/feed',
481 )
482 );
483
484 /**
485 * Deprecated filter retained for backwards compatibility.
486 *
487 * @since 1.4.17
488 * @since 1.6.42 Deprecated in favour of charitable_session_start_uri_blocklist
489 *
490 * @param string[] $blocklist Array of URIs.
491 */
492 $blocklist = apply_filters( 'charitable_session_start_uri_blacklist', $blocklist );
493
494 /* Look to see if WordPress is in a sub folder or this is a network site that uses sub folders */
495 $folder = str_replace( network_home_url(), '', get_site_url() );
496
497 if ( ! empty( $folder ) ) {
498 foreach ( $blocklist as $path ) {
499 $blocklist[] = $folder . '/' . $path;
500 }
501 }
502
503 return $blocklist;
504 }
505 }
506
507 endif;
508