Completed
Push — master ( fc6097...930cc3 )
by Mike
08:01
created

WC_Shortcode_My_Account::lost_password()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 38
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 17
c 1
b 0
f 0
nc 6
nop 0
dl 0
loc 38
rs 6.7272
1
<?php
2
/**
3
 * My Account Shortcodes
4
 *
5
 * Shows the 'my account' section where the customer can view past orders and update their information.
6
 *
7
 * @author 		WooThemes
8
 * @category 	Shortcodes
9
 * @package 	WooCommerce/Shortcodes/My_Account
10
 * @version     2.0.0
11
 */
12
class WC_Shortcode_My_Account {
13
14
	/**
15
	 * Get the shortcode content.
16
	 *
17
	 * @param array $atts
18
	 * @return string
19
	 */
20
	public static function get( $atts ) {
21
		return WC_Shortcodes::shortcode_wrapper( array( __CLASS__, 'output' ), $atts );
22
	}
23
24
	/**
25
	 * Output the shortcode.
26
	 *
27
	 * @param array $atts
28
	 */
29
	public static function output( $atts ) {
30
		global $wp;
31
32
		// Check cart class is loaded or abort
33
		if ( is_null( WC()->cart ) ) {
34
			return;
35
		}
36
37
		if ( ! is_user_logged_in() ) {
38
			$message = apply_filters( 'woocommerce_my_account_message', '' );
39
40
			if ( ! empty( $message ) ) {
41
				wc_add_notice( $message );
42
			}
43
44
			if ( isset( $wp->query_vars['lost-password'] ) ) {
45
				self::lost_password();
46
			} else {
47
				wc_get_template( 'myaccount/form-login.php' );
48
			}
49
		 } else {
50
			// Start output buffer since the html may need discarding for BW compatibility
51
			ob_start();
52
53
			// Collect notices before output
54
			$notices = wc_get_notices();
55
56
			// Output the new account page
57
			self::my_account( $atts );
58
59
			/**
60
			 * Deprecated my-account.php template handling. This code should be
61
			 * removed in a future release.
62
			 *
63
			 * If woocommerce_account_content did not run, this is an old template
64
			 * so we need to render the endpoint content again.
65
			 */
66
			if ( ! did_action( 'woocommerce_account_content' ) ) {
67
				foreach ( $wp->query_vars as $key => $value ) {
68
					if ( 'pagename' === $key ) {
69
						continue;
70
					}
71
					if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) {
72
						ob_clean(); // Clear previous buffer
73
						wc_set_notices( $notices );
74
						wc_print_notices();
75
						do_action( 'woocommerce_account_' . $key . '_endpoint', $value );
76
						break;
77
					}
78
	 			}
79
80
				_deprecated_function( 'Your theme version of my-account.php template', '2.6', 'the latest version, which supports multiple account pages and navigation, from WC 2.6.0' );
81
			}
82
83
			// Send output buffer
84
			ob_end_flush();
85
		}
86
	}
87
88
	/**
89
	 * My account page.
90
	 *
91
	 * @param array $atts
92
	 */
93
	private static function my_account( $atts ) {
94
		extract( shortcode_atts( array(
95
			'order_count' => 15 // @deprecated 2.6.0. Keep for backward compatibility.
96
		), $atts ) );
97
98
		wc_get_template( 'myaccount/my-account.php', array(
99
			'current_user' => get_user_by( 'id', get_current_user_id() ),
100
			'order_count'  => 'all' == $order_count ? -1 : $order_count,
101
		) );
102
	}
103
104
	/**
105
	 * View order page.
106
	 *
107
	 * @param int $order_id
108
	 */
109
	public static function view_order( $order_id ) {
110
		$order   = wc_get_order( $order_id );
111
112 View Code Duplication
		if ( ! current_user_can( 'view_order', $order_id ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
113
			echo '<div class="woocommerce-error">' . __( 'Invalid order.', 'woocommerce' ) . ' <a href="' . wc_get_page_permalink( 'myaccount' ).'" class="wc-forward">'. __( 'My Account', 'woocommerce' ) .'</a>' . '</div>';
114
			return;
115
		}
116
117
		// Backwards compatibility
118
		$status       = new stdClass();
119
		$status->name = wc_get_order_status_name( $order->get_status() );
120
121
		wc_get_template( 'myaccount/view-order.php', array(
122
			'status'    => $status, // @deprecated 2.2
123
			'order'     => wc_get_order( $order_id ),
124
			'order_id'  => $order_id
125
		) );
126
	}
127
128
	/**
129
	 * Edit account details page.
130
	 */
131
	public static function edit_account() {
132
		wc_get_template( 'myaccount/form-edit-account.php', array( 'user' => get_user_by( 'id', get_current_user_id() ) ) );
133
	}
134
135
	/**
136
	 * Edit address page.
137
	 *
138
	 * @param string $load_address
139
	 */
140
	public static function edit_address( $load_address = 'billing' ) {
141
		$current_user = wp_get_current_user();
142
		$load_address = sanitize_key( $load_address );
143
144
		$address = WC()->countries->get_address_fields( get_user_meta( get_current_user_id(), $load_address . '_country', true ), $load_address . '_' );
145
146
		// Enqueue scripts
147
		wp_enqueue_script( 'wc-country-select' );
148
		wp_enqueue_script( 'wc-address-i18n' );
149
150
		// Prepare values
151
		foreach ( $address as $key => $field ) {
152
153
			$value = get_user_meta( get_current_user_id(), $key, true );
154
155
			if ( ! $value ) {
156
				switch( $key ) {
157
					case 'billing_email' :
158
					case 'shipping_email' :
159
						$value = $current_user->user_email;
160
					break;
161
					case 'billing_country' :
162
					case 'shipping_country' :
163
						$value = WC()->countries->get_base_country();
164
					break;
165
					case 'billing_state' :
166
					case 'shipping_state' :
167
						$value = WC()->countries->get_base_state();
168
					break;
169
				}
170
			}
171
172
			$address[ $key ]['value'] = apply_filters( 'woocommerce_my_account_edit_address_field_value', $value, $key, $load_address );
173
		}
174
175
		wc_get_template( 'myaccount/form-edit-address.php', array(
176
			'load_address' 	=> $load_address,
177
			'address'		=> apply_filters( 'woocommerce_address_to_edit', $address )
178
		) );
179
	}
180
181
	/**
182
	 * Lost password page handling.
183
	 */
184
	public static function lost_password() {
185
		/**
186
		 * After sending the reset link, don't show the form again.
187
		 */
188
		if ( ! empty( $_GET['reset-link-sent'] ) ) {
189
			return wc_get_template( 'myaccount/lost-password-confirmation.php' );
190
191
		/**
192
		 * After reset, show confirmation message.
193
		 */
194
		} elseif ( ! empty( $_GET['reset'] ) ) {
195
			wc_add_notice( __( 'Your password has been reset.', 'woocommerce' ) . ' <a class="button" href="' . esc_url( wc_get_page_permalink( 'myaccount' ) ) . '">' . __( 'Log in', 'woocommerce' ) . '</a>' );
196
197
		/**
198
		 * Process reset key / login from email confirmation link
199
		 */
200
		} elseif ( ! empty( $_GET['show-reset-form'] ) ) {
201
			if ( isset( $_COOKIE[ 'wp-resetpass-' . COOKIEHASH ] ) && 0 < strpos( $_COOKIE[ 'wp-resetpass-' . COOKIEHASH ], ':' ) ) {
202
				list( $rp_login, $rp_key ) = array_map( 'wc_clean', explode( ':', wp_unslash( $_COOKIE[ 'wp-resetpass-' . COOKIEHASH ] ), 2 ) );
203
				$user = self::check_password_reset_key( $rp_key, $rp_login );
204
205
				// reset key / login is correct, display reset password form with hidden key / login values
206
				if ( is_object( $user ) ) {
207
					return wc_get_template( 'myaccount/form-reset-password.php', array(
208
						'key'   => $rp_key,
209
						'login' => $rp_login,
210
					) );
211
				} else {
212
					self::set_reset_password_cookie();
213
				}
214
			}
215
		}
216
217
		// Show lost password form by default
218
		wc_get_template( 'myaccount/form-lost-password.php', array(
219
			'form'  => 'lost_password',
220
		) );
221
	}
222
223
	/**
224
	 * Handles sending password retrieval email to customer.
225
	 *
226
	 * Based on retrieve_password() in core wp-login.php.
227
	 *
228
	 * @uses $wpdb WordPress Database object
229
	 * @return bool True: when finish. False: on error
230
	 */
231
	public static function retrieve_password() {
232
		global $wpdb, $wp_hasher;
233
234
		$login = trim( $_POST['user_login'] );
235
236
		if ( empty( $login ) ) {
237
238
			wc_add_notice( __( 'Enter a username or e-mail address.', 'woocommerce' ), 'error' );
239
			return false;
240
241
		} else {
242
			// Check on username first, as customers can use emails as usernames.
243
			$user_data = get_user_by( 'login', $login );
244
		}
245
246
		// If no user found, check if it login is email and lookup user based on email.
247
		if ( ! $user_data && is_email( $login ) && apply_filters( 'woocommerce_get_username_from_email', true ) ) {
248
			$user_data = get_user_by( 'email', $login );
249
		}
250
251
		do_action( 'lostpassword_post' );
252
253
		if ( ! $user_data ) {
254
			wc_add_notice( __( 'Invalid username or e-mail.', 'woocommerce' ), 'error' );
255
			return false;
256
		}
257
258
		if ( is_multisite() && ! is_user_member_of_blog( $user_data->ID, get_current_blog_id() ) ) {
259
			wc_add_notice( __( 'Invalid username or e-mail.', 'woocommerce' ), 'error' );
260
			return false;
261
		}
262
263
		// redefining user_login ensures we return the right case in the email
264
		$user_login = $user_data->user_login;
265
266
		do_action( 'retrieve_password', $user_login );
267
268
		$allow = apply_filters( 'allow_password_reset', true, $user_data->ID );
269
270
		if ( ! $allow ) {
271
272
			wc_add_notice( __( 'Password reset is not allowed for this user', 'woocommerce' ), 'error' );
273
			return false;
274
275
		} elseif ( is_wp_error( $allow ) ) {
276
277
			wc_add_notice( $allow->get_error_message(), 'error' );
278
			return false;
279
		}
280
281
		$key = wp_generate_password( 20, false );
282
283
		do_action( 'retrieve_password_key', $user_login, $key );
284
285
		// Now insert the key, hashed, into the DB.
286
		if ( empty( $wp_hasher ) ) {
287
			require_once ABSPATH . 'wp-includes/class-phpass.php';
288
			$wp_hasher = new PasswordHash( 8, true );
289
		}
290
291
		$hashed = $wp_hasher->HashPassword( $key );
292
293
		$wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user_login ) );
294
295
		// Send email notification
296
		WC()->mailer(); // load email classes
297
		do_action( 'woocommerce_reset_password_notification', $user_login, $key );
298
299
		return true;
300
	}
301
302
	/**
303
	 * Retrieves a user row based on password reset key and login.
304
	 *
305
	 * @uses $wpdb WordPress Database object
306
	 *
307
	 * @param string $key Hash to validate sending user's password
308
	 * @param string $login The user login
309
	 * @return WP_USER|bool User's database row on success, false for invalid keys
310
	 */
311
	public static function check_password_reset_key( $key, $login ) {
312
		global $wpdb, $wp_hasher;
313
314
		$key = preg_replace( '/[^a-z0-9]/i', '', $key );
315
316 View Code Duplication
		if ( empty( $key ) || ! is_string( $key ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
317
			wc_add_notice( __( 'Invalid key', 'woocommerce' ), 'error' );
318
			return false;
319
		}
320
321 View Code Duplication
		if ( empty( $login ) || ! is_string( $login ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
322
			wc_add_notice( __( 'Invalid key', 'woocommerce' ), 'error' );
323
			return false;
324
		}
325
326
		$user = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->users WHERE user_login = %s", $login ) );
327
328
		if ( ! empty( $user ) ) {
329
			if ( empty( $wp_hasher ) ) {
330
				require_once ABSPATH . 'wp-includes/class-phpass.php';
331
				$wp_hasher = new PasswordHash( 8, true );
332
			}
333
334
			$valid = $wp_hasher->CheckPassword( $key, $user->user_activation_key );
335
		}
336
337 View Code Duplication
		if ( empty( $user ) || empty( $valid ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
338
			wc_add_notice( __( 'Invalid key', 'woocommerce' ), 'error' );
339
			return false;
340
		}
341
342
		return get_userdata( $user->ID );
343
	}
344
345
	/**
346
	 * Handles resetting the user's password.
347
	 *
348
	 * @param object $user The user
349
	 * @param string $new_pass New password for the user in plaintext
350
	 */
351
	public static function reset_password( $user, $new_pass ) {
352
		do_action( 'password_reset', $user, $new_pass );
353
354
		wp_set_password( $new_pass, $user->ID );
355
		self::set_reset_password_cookie();
356
357
		wp_password_change_notification( $user );
358
	}
359
360
	/**
361
	 * Set or unset the cookie.
362
	 */
363
	public static function set_reset_password_cookie( $value = '' ) {
364
		$rp_cookie = 'wp-resetpass-' . COOKIEHASH;
365
		$rp_path   = current( explode( '?', wp_unslash( $_SERVER['REQUEST_URI'] ) ) );
366
367
		if ( $value ) {
368
			setcookie( $rp_cookie, $value, 0, $rp_path, COOKIE_DOMAIN, is_ssl(), true );
369
		} else {
370
			setcookie( $rp_cookie, ' ', time() - YEAR_IN_SECONDS, $rp_path, COOKIE_DOMAIN, is_ssl(), true );
371
		}
372
	}
373
374
	/**
375
	 * Show the add payment method page.
376
	 */
377
	public static function add_payment_method() {
378
379
		if ( ! is_user_logged_in() ) {
380
381
			wp_safe_redirect( wc_get_page_permalink( 'myaccount' ) );
382
			exit();
383
384
		} else {
385
386
			do_action( 'before_woocommerce_add_payment_method' );
387
388
			wc_print_notices();
389
390
			wc_get_template( 'myaccount/form-add-payment-method.php' );
391
392
			do_action( 'after_woocommerce_add_payment_method' );
393
394
		}
395
396
	}
397
398
}
399