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.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 / user-management / class-charitable-user-management.php

class-charitable-user-management.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.13, at includes/user-management/class-charitable-user-management.php

435 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class that manages the hook functions for the forgot password form.
4 *
5 * @package Charitable/User Management/User Management
6 * @author Rafe Colton, David Bisset
7 * @copyright Copyright (c) 2023, WP Charitable LLC
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.4.0
10 * @version 1.8.12.1
11 */
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 if ( ! class_exists( 'Charitable_User_Management' ) ) :
18
19 /**
20 * Charitable_User_Management class
21 *
22 * @since 1.4.0
23 */
24 class Charitable_User_Management {
25
26 /**
27 * The class instance.
28 *
29 * @var Charitable_User_Management
30 *
31 * @since 1.4.0
32 */
33 private static $instance;
34
35 /**
36 * Returns and/or create the single instance of this class.
37 *
38 * @since 1.4.0
39 *
40 * @return Charitable_User_Management
41 */
42 public static function get_instance() {
43 return self::$instance ??= new self();
44 }
45
46 /**
47 * Set up the class.
48 *
49 * @since 1.4.0
50 */
51 private function __construct() {
52 }
53
54 /**
55 * Check whether we have clicked on a password reset link.
56 *
57 * If so, redirect to the password reset page without the query string.
58 *
59 * @since 1.4.0
60 *
61 * @return false|void False if no redirect takes place.
62 */
63 public function maybe_redirect_to_password_reset() {
64 if ( ! charitable_is_page( 'reset_password_page' ) ) {
65 return false;
66 }
67
68 if ( ! isset( $_GET['key'] ) || ! isset( $_GET['login'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
69 return false;
70 }
71
72 $value = sprintf( '%s:%s', wp_unslash( $_GET['login'] ), wp_unslash( $_GET['key'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
73
74 $this->set_reset_cookie( $value );
75
76 wp_safe_redirect( esc_url_raw( charitable_get_permalink( 'reset_password_page' ) ) );
77
78 exit();
79 }
80
81 /**
82 * Check if user is attempting to access the default reset password page
83 *
84 * If so, and charitable_disable_wp_login is set, redirect them to the custom reset password page
85 *
86 * @since 1.4.0
87 *
88 * @return void
89 */
90 public function maybe_redirect_to_custom_password_reset_page() {
91 if ( ! apply_filters( 'charitable_disable_wp_login', false ) ) {
92 return;
93 }
94
95 if ( 'wp' == charitable_get_option( 'login_page', 'wp' ) ) {
96 return;
97 }
98
99 $redirect_url = charitable_get_permalink( 'reset_password_page' );
100 $redirect_url = add_query_arg( 'login', esc_attr( $_REQUEST['login'] ), $redirect_url ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotValidated,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
101 $redirect_url = add_query_arg( 'key', esc_attr( $_REQUEST['key'] ), $redirect_url ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotValidated,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
102
103 wp_safe_redirect( esc_url_raw( $redirect_url ) );
104
105 exit();
106 }
107
108 /**
109 * Check if a failed user login attempt originated from Charitable login form.
110 *
111 * If so redirect user to Charitable login page.
112 *
113 * @since 1.4.0
114 *
115 * @param WP_User|WP_Error $user_or_error
116 * @param string $username
117 * @return WP_User|void
118 */
119 public function maybe_redirect_at_authenticate( $user_or_error, $username ) {
120 if ( 'POST' != $_SERVER['REQUEST_METHOD'] ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
121 return $user_or_error;
122 }
123
124 if ( ! is_wp_error( $user_or_error ) ) {
125 return $user_or_error;
126 }
127
128 if ( ! isset( $_POST['charitable'] ) || ! $_POST['charitable'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
129 return $user_or_error;
130 }
131
132 /*
133 * Resolve our redirect target before touching the notice session.
134 *
135 * If the site uses the default WordPress login (the 'login_page' setting is
136 * 'wp', which is the default), or the configured login page has since been
137 * deleted, then there is no page of ours to send the user to. Bail out and let
138 * wp-login.php render its own error, which it does natively.
139 *
140 * Redirecting to wp_login_url() in that case would be strictly worse than
141 * doing nothing: our messages are queued in the Charitable notice session and
142 * nothing renders them on wp-login.php, so the user would land on a blank
143 * login form with no indication of what went wrong.
144 */
145 $redirect_url = charitable_get_permalink( 'login_page' );
146
147 if ( ! $redirect_url || wp_login_url() === $redirect_url ) {
148 return $user_or_error;
149 }
150
151 foreach ( $user_or_error->errors as $code => $error ) {
152
153 /* Make sure the error messages link to our forgot password page, not WordPress' */
154 switch ( $code ) {
155
156 case 'invalid_email':
157 $error = __( '<strong>ERROR</strong>: Invalid email address.', 'charitable' ) .
158 ' <a href="' . esc_url( charitable_get_permalink( 'forgot_password_page' ) ) . '">' .
159 __( 'Lost your password?', 'charitable' ) .
160 '</a>';
161 break;
162
163 case 'incorrect_password':
164 $error = sprintf(
165 /* translators: %s: email address */
166 __( '<strong>ERROR</strong>: The password you entered for %s is incorrect.', 'charitable' ),
167 /*
168 * Escape the submitted username. This string is queued as a notice and
169 * rendered as HTML, so it must not carry markup through. In practice
170 * wp_authenticate() has already run sanitize_user() on it before the
171 * authenticate filter fires, which strips tags -- this is belt and
172 * braces so the escaping does not depend on that upstream behaviour.
173 */
174 '<strong>' . esc_html( $username ) . '</strong>'
175 ) .
176 ' <a href="' . esc_url( charitable_get_permalink( 'forgot_password_page' ) ) . '">' .
177 __( 'Lost your password?', 'charitable' ) .
178 '</a>';
179 break;
180
181 default:
182 $error = $error[0];
183 }
184
185 charitable_get_notices()->add_error( $error );
186 }
187
188 charitable_get_session()->add_notices();
189
190 if ( strlen( $username ) ) {
191 $redirect_url = add_query_arg( 'username', $username, $redirect_url );
192 }
193
194 wp_safe_redirect( esc_url_raw( $redirect_url ) );
195
196 exit();
197 }
198
199 /**
200 * Check if user is attempting to access the forgot password page
201 *
202 * If so, and charitable_disable_wp_login is set, redirect them to the custom forgot password page
203 *
204 * @since 1.4.0
205 *
206 * @return void
207 */
208 public function maybe_redirect_to_custom_lostpassword() {
209 /**
210 * Set whether the WP login should be disabled.
211 *
212 * @since 1.4.0
213 *
214 * @param boolean $disable Whether to disable the WP login.
215 */
216 if ( ! apply_filters( 'charitable_disable_wp_login', false ) ) {
217 return;
218 }
219
220 if ( 'wp' == charitable_get_option( 'login_page', 'wp' ) ) {
221 return;
222 }
223
224 if ( $_SERVER['REQUEST_METHOD'] != 'GET' ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
225 return;
226 }
227
228 wp_safe_redirect( esc_url_raw( charitable_get_permalink( 'forgot_password_page' ) ) );
229
230 exit();
231 }
232
233 /**
234 * Set the password reset cookie.
235 *
236 * This is based on the WC_Shortcode_My_Account::set_reset_password_cookie()
237 * method in WooCommerce, which in turn is based on the core implementation
238 * in wp-login.php.
239 *
240 * @since 1.4.0
241 *
242 * @param string $value
243 * @return void
244 */
245 public function set_reset_cookie( $value = '' ) {
246 $rp_cookie = 'wp-resetpass-' . COOKIEHASH;
247 $rp_path = current( explode( '?', wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
248
249 if ( $value ) {
250 setcookie( $rp_cookie, $value, 0, $rp_path, COOKIE_DOMAIN, is_ssl(), true );
251 } else {
252 setcookie( $rp_cookie, ' ', time() - YEAR_IN_SECONDS, $rp_path, COOKIE_DOMAIN, is_ssl(), true );
253 }
254 }
255
256 /**
257 * Hides WP Admin bar if the user is not allowed to see it.
258 *
259 * Uses the builtin show_admin_bar function.
260 *
261 * @see show_admin_bar()
262 *
263 * @since 1.4.0
264 */
265 public function maybe_remove_admin_bar() {
266 /**
267 * To enable the admin bar for users without admin bar access,
268 * you can use this one-liner:
269 *
270 * add_filter( 'charitable_disable_admin_bar', '__return_true' );
271 */
272 if ( ! apply_filters( 'charitable_disable_admin_bar', true ) ) {
273 return;
274 }
275
276 if ( ! $this->user_has_admin_access() ) {
277 show_admin_bar( false );
278 }
279 }
280
281 /**
282 * Redirects the user away from /wp-admin if they are not authorized to access it.
283 *
284 * @since 1.4.0
285 */
286 public function maybe_redirect_away_from_admin() {
287 /* Leave AJAX requests alone. */
288 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
289 return;
290 }
291
292 /**
293 * To enable admin access for users without admin access,
294 * you can use this one-liner:
295 *
296 * add_filter( 'charitable_disable_admin_access', '__return_true' );
297 */
298 if ( ! apply_filters( 'charitable_disable_admin_access', true ) ) {
299 return;
300 }
301
302 if ( $this->user_has_admin_access() ) {
303 return;
304 }
305
306 /**
307 * Specify a custom URL that users should be redirected to.
308 *
309 * @since 1.4.0
310 *
311 * @param false|string Return false to use the default. Otherwise this
312 * must return a URL on the same domain.
313 */
314 $redirect_url = apply_filters( 'charitable_admin_redirect_url', false );
315
316 if ( ! $redirect_url ) {
317 $redirect_url = charitable_get_permalink( 'profile_page' );
318
319 if ( ! $redirect_url ) {
320 $redirect_url = home_url();
321 }
322 }
323
324 wp_safe_redirect( esc_url_raw( $redirect_url ) );
325
326 exit();
327 }
328
329 /**
330 * Redirect the user to the Charitable login page.
331 *
332 * @since 1.4.0
333 *
334 * @return void
335 */
336 public function maybe_redirect_to_charitable_login() {
337 if ( ! apply_filters( 'charitable_disable_wp_login', false ) ) {
338 return;
339 }
340
341 if ( 'wp' == charitable_get_option( 'login_page', 'wp' ) ) {
342 return;
343 }
344
345 /* Don't prevent logging out. */
346 if ( 'GET' != $_SERVER['REQUEST_METHOD'] ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
347 return;
348 }
349
350 wp_safe_redirect( esc_url_raw( charitable_get_permalink( 'login_page' ) ) );
351
352 exit();
353 }
354
355 /**
356 * Send the user verification email.
357 *
358 * @since 1.5.0
359 * @since 1.6.32
360 *
361 * @param WP_User $user An instance of `WP_User`.
362 * @param string $redirect_url Where the user should be redirected to after verifying their email.
363 * @param boolean|null $force_send Whether the link should include an argument to force
364 * resending the email, even if it was sent recently.
365 * @return boolean True if the email is sent. False otherwise.
366 */
367 public function send_verification_email( $user = '', $redirect_url = '', $force_send = null ) {
368 if ( empty( $user ) && array_key_exists( 'user', $_GET ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
369 $user = wp_get_current_user();
370 }
371
372 if ( ! is_a( $user, 'WP_User' ) ) {
373 return false;
374 }
375
376 if ( is_null( $force_send ) ) {
377 $force_send = array_key_exists( 'force_send', $_GET ) && $_GET['force_send']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
378 }
379
380 if ( empty( $redirect_url ) && array_key_exists( 'redirect_url', $_GET ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
381 $redirect_url = $_GET['redirect_url']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
382 }
383
384 /* Prepare the email. */
385 $email = new Charitable_Email_Email_Verification( array( 'user' => $user ) );
386
387 if ( ! $force_send && $email->sent_recently() ) {
388 return false;
389 }
390
391 if ( ! empty( $redirect_url ) ) {
392 /* Ensure that the redirect URL is encoded, but not double-encoded. */
393 $redirect_url = urlencode( urldecode( $redirect_url ) );
394
395 $email->set_redirect_url( $redirect_url );
396 }
397
398 /* If the confirmation link is generated correctly and the email is sent, set a notice. */
399 if ( is_wp_error( $email->get_confirmation_url() ) ) {
400 return false;
401 }
402
403 return $email->send();
404 }
405
406 /**
407 * Check whether the user has admin access.
408 *
409 * @since 1.4.0
410 *
411 * @return boolean
412 */
413 private function user_has_admin_access() {
414 if ( ! is_user_logged_in() ) {
415 return false;
416 }
417
418 $has_access = current_user_can( 'edit_posts' )
419 || current_user_can( 'manage_charitable_settings' )
420 || current_user_can( 'edit_products' )
421 || current_user_can( 'translate' );
422
423 /**
424 * Filter whether the user should have access to the WordPress dashboard.
425 *
426 * @since 1.4.0
427 *
428 * @param boolean $has_access Whether the user has access.
429 */
430 return apply_filters( 'charitable_user_has_admin_access', $has_access );
431 }
432 }
433
434 endif;
435