|
1
|
|
|
<?php |
|
|
|
|
|
|
2
|
|
|
/** |
|
3
|
|
|
* Email Access |
|
4
|
|
|
* |
|
5
|
|
|
* @package Give |
|
6
|
|
|
* @subpackage Classes/Give_Email_Access |
|
7
|
|
|
* @copyright Copyright (c) 2016, WordImpress |
|
8
|
|
|
* @license https://opensource.org/licenses/gpl-license GNU Public License |
|
9
|
|
|
* @since 1.4 |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
// Exit if accessed directly. |
|
13
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
|
14
|
|
|
exit; |
|
15
|
|
|
} |
|
16
|
|
|
|
|
17
|
|
|
/** |
|
18
|
|
|
* Give_Email_Access class |
|
19
|
|
|
* |
|
20
|
|
|
* This class handles email access, allowing donors access to their donation w/o logging in; |
|
21
|
|
|
* |
|
22
|
|
|
* Based on the work from Matt Gibbs - https://github.com/FacetWP/edd-no-logins |
|
23
|
|
|
* |
|
24
|
|
|
* @since 1.0 |
|
25
|
|
|
*/ |
|
26
|
|
|
class Give_Email_Access { |
|
27
|
|
|
|
|
28
|
|
|
/** |
|
29
|
|
|
* Token exists |
|
30
|
|
|
* |
|
31
|
|
|
* @since 1.0 |
|
32
|
|
|
* @access public |
|
33
|
|
|
* |
|
34
|
|
|
* @var bool |
|
35
|
|
|
*/ |
|
36
|
|
|
public $token_exists = false; |
|
37
|
|
|
|
|
38
|
|
|
/** |
|
39
|
|
|
* Token email |
|
40
|
|
|
* |
|
41
|
|
|
* @since 1.0 |
|
42
|
|
|
* @access public |
|
43
|
|
|
* |
|
44
|
|
|
* @var bool |
|
45
|
|
|
*/ |
|
46
|
|
|
public $token_email = false; |
|
47
|
|
|
|
|
48
|
|
|
/** |
|
49
|
|
|
* Token |
|
50
|
|
|
* |
|
51
|
|
|
* @since 1.0 |
|
52
|
|
|
* @access public |
|
53
|
|
|
* |
|
54
|
|
|
* @var bool |
|
55
|
|
|
*/ |
|
56
|
|
|
public $token = false; |
|
57
|
|
|
|
|
58
|
|
|
/** |
|
59
|
|
|
* Error |
|
60
|
|
|
* |
|
61
|
|
|
* @since 1.0 |
|
62
|
|
|
* @access public |
|
63
|
|
|
* |
|
64
|
|
|
* @var string |
|
65
|
|
|
*/ |
|
66
|
|
|
public $error = ''; |
|
67
|
|
|
|
|
68
|
|
|
/** |
|
69
|
|
|
* Verify throttle |
|
70
|
|
|
* |
|
71
|
|
|
* @since 1.0 |
|
72
|
|
|
* @access private |
|
73
|
|
|
* |
|
74
|
|
|
* @var |
|
75
|
|
|
*/ |
|
76
|
|
|
private $verify_throttle; |
|
77
|
|
|
|
|
78
|
|
|
/** |
|
79
|
|
|
* Verify expiration |
|
80
|
|
|
* |
|
81
|
|
|
* @since 1.0 |
|
82
|
|
|
* @access private |
|
83
|
|
|
* |
|
84
|
|
|
* @var string |
|
85
|
|
|
*/ |
|
86
|
|
|
private $token_expiration; |
|
87
|
|
|
|
|
88
|
|
|
/** |
|
89
|
|
|
* Class Constructor |
|
90
|
|
|
* |
|
91
|
|
|
* Set up the Give Email Access Class. |
|
92
|
|
|
* |
|
93
|
|
|
* @since 1.0 |
|
94
|
|
|
* @access public |
|
95
|
|
|
*/ |
|
96
|
|
|
public function __construct() { |
|
97
|
|
|
|
|
98
|
|
|
// get it started |
|
99
|
|
|
add_action( 'init', array( $this, 'init' ) ); |
|
100
|
|
|
} |
|
101
|
|
|
|
|
102
|
|
|
/** |
|
103
|
|
|
* Init |
|
104
|
|
|
* |
|
105
|
|
|
* Register defaults and filters |
|
106
|
|
|
* |
|
107
|
|
|
* @since 1.0 |
|
108
|
|
|
* @access public |
|
109
|
|
|
* |
|
110
|
|
|
* @return void |
|
111
|
|
|
*/ |
|
112
|
|
|
public function init() { |
|
113
|
|
|
|
|
114
|
|
|
/** |
|
115
|
|
|
* Do NOT pass go if: |
|
116
|
|
|
* |
|
117
|
|
|
* a. User is logged in |
|
118
|
|
|
* b. Email access setting is not enabled |
|
119
|
|
|
* c. You're in the admin |
|
120
|
|
|
*/ |
|
121
|
|
|
if ( |
|
122
|
|
|
is_user_logged_in() |
|
123
|
|
|
|| ! give_is_setting_enabled( give_get_option( 'email_access' ) ) |
|
124
|
|
|
|| is_admin() |
|
125
|
|
|
) { |
|
126
|
|
|
return; |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
// Are db columns setup? |
|
130
|
|
|
$is_setup = give_get_option( 'email_access_installed' ); |
|
131
|
|
|
if ( empty( $is_setup ) ) { |
|
132
|
|
|
$this->create_columns(); |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
// Timeouts. |
|
136
|
|
|
$this->verify_throttle = apply_filters( 'give_nl_verify_throttle', 300 ); |
|
137
|
|
|
$this->token_expiration = apply_filters( 'give_nl_token_expiration', 7200 ); |
|
138
|
|
|
|
|
139
|
|
|
// Setup login. |
|
140
|
|
|
$this->check_for_token(); |
|
141
|
|
|
|
|
142
|
|
|
if ( $this->token_exists ) { |
|
143
|
|
|
add_filter( 'give_can_view_receipt', '__return_true' ); |
|
144
|
|
|
add_filter( 'give_user_pending_verification', '__return_false' ); |
|
145
|
|
|
add_filter( 'give_get_users_donations_args', array( $this, 'users_donations_args' ) ); |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
/** |
|
151
|
|
|
* Prevent email spamming. |
|
152
|
|
|
* |
|
153
|
|
|
* @since 1.0 |
|
154
|
|
|
* @access public |
|
155
|
|
|
* |
|
156
|
|
|
* @param $customer_id string Customer id. |
|
157
|
|
|
* |
|
158
|
|
|
* @return bool |
|
159
|
|
|
*/ |
|
160
|
|
|
public function can_send_email( $customer_id ) { |
|
161
|
|
|
/* @var WPDB $wpdb */ |
|
162
|
|
|
global $wpdb; |
|
|
|
|
|
|
163
|
|
|
|
|
164
|
|
|
// Prevent multiple emails within X minutes |
|
165
|
|
|
$throttle = date( 'Y-m-d H:i:s', time() - $this->verify_throttle ); |
|
166
|
|
|
|
|
167
|
|
|
// Does a user row exist? |
|
168
|
|
|
$exists = (int) $wpdb->get_var( |
|
169
|
|
|
$wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}give_customers WHERE id = %d", $customer_id ) |
|
170
|
|
|
); |
|
171
|
|
|
|
|
172
|
|
|
if ( 0 < $exists ) { |
|
173
|
|
|
$row_id = (int) $wpdb->get_var( |
|
174
|
|
|
$wpdb->prepare( "SELECT id FROM {$wpdb->prefix}give_customers WHERE id = %d AND (verify_throttle < %s OR verify_key = '') LIMIT 1", $customer_id, $throttle ) |
|
175
|
|
|
); |
|
176
|
|
|
|
|
177
|
|
|
if ( $row_id < 1 ) { |
|
178
|
|
|
give_set_error( 'give_email_access_attempts_exhausted', __( 'Please wait a few minutes before requesting a new email access link.', 'give' ) ); |
|
179
|
|
|
|
|
180
|
|
|
return false; |
|
181
|
|
|
} |
|
182
|
|
|
} |
|
183
|
|
|
|
|
184
|
|
|
return true; |
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
/** |
|
188
|
|
|
* Send the user's token |
|
189
|
|
|
* |
|
190
|
|
|
* @since 1.0 |
|
191
|
|
|
* @access public |
|
192
|
|
|
* |
|
193
|
|
|
* @param $customer_id string Customer id. |
|
194
|
|
|
* @param $email string Customer email. |
|
195
|
|
|
* |
|
196
|
|
|
* @return void |
|
197
|
|
|
*/ |
|
198
|
|
|
public function send_email( $customer_id, $email ) { |
|
199
|
|
|
|
|
200
|
|
|
$verify_key = wp_generate_password( 20, false ); |
|
201
|
|
|
|
|
202
|
|
|
// Generate a new verify key |
|
203
|
|
|
$this->set_verify_key( $customer_id, $email, $verify_key ); |
|
204
|
|
|
|
|
205
|
|
|
// Get the donation history page |
|
206
|
|
|
$page_id = give_get_option( 'history_page' ); |
|
207
|
|
|
|
|
208
|
|
|
$access_url = add_query_arg( array( |
|
209
|
|
|
'give_nl' => $verify_key, |
|
210
|
|
|
), get_permalink( $page_id ) ); |
|
211
|
|
|
|
|
212
|
|
|
// Nice subject and message. |
|
213
|
|
|
$subject = apply_filters( 'give_email_access_token_subject', sprintf( __( 'Your Access Link to %s', 'give' ), get_bloginfo( 'name' ) ) ); |
|
214
|
|
|
|
|
215
|
|
|
$message = __( 'You or someone in your organization requested an access link be sent to this email address. This is a temporary access link for you to view your donation information. Click on the link below to view:', 'give' ) . "\n\n"; |
|
216
|
|
|
$message .= '<a href="' . esc_url( $access_url ) . '" target="_blank">' . __( 'Access Donation Details »', 'give' ) . '</a>' . "\n\n"; |
|
217
|
|
|
$message .= "\n\n"; |
|
218
|
|
|
$message .= __( 'Sincerely,', 'give' ) . "\n"; |
|
219
|
|
|
$message .= get_bloginfo( 'name' ) . "\n"; |
|
220
|
|
|
|
|
221
|
|
|
$message = apply_filters( 'give_email_access_token_message', $message ); |
|
222
|
|
|
|
|
223
|
|
|
// Send the email. |
|
224
|
|
|
Give()->emails->__set( 'heading', apply_filters( 'give_email_access_token_heading', __( 'Your Access Link', 'give' ) ) ); |
|
225
|
|
|
Give()->emails->send( $email, $subject, $message ); |
|
226
|
|
|
|
|
227
|
|
|
} |
|
228
|
|
|
|
|
229
|
|
|
/** |
|
230
|
|
|
* Has the user authenticated? |
|
231
|
|
|
* |
|
232
|
|
|
* @since 1.0 |
|
233
|
|
|
* @access public |
|
234
|
|
|
* |
|
235
|
|
|
* @return bool |
|
|
|
|
|
|
236
|
|
|
*/ |
|
237
|
|
|
public function check_for_token() { |
|
238
|
|
|
|
|
239
|
|
|
$token = isset( $_GET['give_nl'] ) ? $_GET['give_nl'] : ''; |
|
240
|
|
|
|
|
241
|
|
|
// Check for cookie. |
|
242
|
|
|
if ( empty( $token ) ) { |
|
243
|
|
|
$token = isset( $_COOKIE['give_nl'] ) ? $_COOKIE['give_nl'] : ''; |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
// Must have a token. |
|
247
|
|
|
if ( ! empty( $token ) ) { |
|
248
|
|
|
|
|
249
|
|
|
if ( ! $this->is_valid_token( $token ) ) { |
|
250
|
|
|
if ( ! $this->is_valid_verify_key( $token ) ) { |
|
251
|
|
|
return; |
|
252
|
|
|
} |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
$this->token_exists = true; |
|
256
|
|
|
// Set cookie. |
|
257
|
|
|
$lifetime = current_time( 'timestamp' ) + Give()->session->set_expiration_time(); |
|
258
|
|
|
@setcookie( 'give_nl', $token, $lifetime, COOKIEPATH, COOKIE_DOMAIN, false ); |
|
259
|
|
|
|
|
260
|
|
|
return true; |
|
261
|
|
|
} |
|
262
|
|
|
} |
|
263
|
|
|
|
|
264
|
|
|
/** |
|
265
|
|
|
* Is this a valid token? |
|
266
|
|
|
* |
|
267
|
|
|
* @since 1.0 |
|
268
|
|
|
* @access public |
|
269
|
|
|
* |
|
270
|
|
|
* @param $token string The token. |
|
271
|
|
|
* |
|
272
|
|
|
* @return bool |
|
273
|
|
|
*/ |
|
274
|
|
|
public function is_valid_token( $token ) { |
|
275
|
|
|
|
|
276
|
|
|
global $wpdb; |
|
|
|
|
|
|
277
|
|
|
|
|
278
|
|
|
// Make sure token isn't expired. |
|
279
|
|
|
$expires = date( 'Y-m-d H:i:s', time() - $this->token_expiration ); |
|
280
|
|
|
|
|
281
|
|
|
$email = $wpdb->get_var( |
|
282
|
|
|
$wpdb->prepare( "SELECT email FROM {$wpdb->prefix}give_customers WHERE token = %s AND verify_throttle >= %s LIMIT 1", $token, $expires ) |
|
283
|
|
|
); |
|
284
|
|
|
|
|
285
|
|
|
if ( ! empty( $email ) ) { |
|
286
|
|
|
$this->token_email = $email; |
|
287
|
|
|
$this->token = $token; |
|
288
|
|
|
return true; |
|
289
|
|
|
} |
|
290
|
|
|
|
|
291
|
|
|
// Set error only if email access form isn't being submitted |
|
292
|
|
|
if ( ! isset( $_POST['give_email'] ) && ! isset( $_POST['_wpnonce'] ) ) { |
|
293
|
|
|
give_set_error( 'give_email_token_expired', apply_filters( 'give_email_token_expired_message', __( 'Your access token has expired. Please request a new one below:', 'give' ) ) ); |
|
294
|
|
|
} |
|
295
|
|
|
|
|
296
|
|
|
return false; |
|
297
|
|
|
|
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
/** |
|
301
|
|
|
* Add the verify key to DB |
|
302
|
|
|
* |
|
303
|
|
|
* @since 1.0 |
|
304
|
|
|
* @access public |
|
305
|
|
|
* |
|
306
|
|
|
* @param $customer_id string Customer id. |
|
307
|
|
|
* @param $email string Customer email. |
|
308
|
|
|
* @param $verify_key string The verification key. |
|
309
|
|
|
* |
|
310
|
|
|
* @return void |
|
311
|
|
|
*/ |
|
312
|
|
|
public function set_verify_key( $customer_id, $email, $verify_key ) { |
|
|
|
|
|
|
313
|
|
|
global $wpdb; |
|
|
|
|
|
|
314
|
|
|
|
|
315
|
|
|
$now = date( 'Y-m-d H:i:s' ); |
|
316
|
|
|
|
|
317
|
|
|
// Insert or update? |
|
318
|
|
|
$row_id = (int) $wpdb->get_var( |
|
319
|
|
|
$wpdb->prepare( "SELECT id FROM {$wpdb->prefix}give_customers WHERE id = %d LIMIT 1", $customer_id ) |
|
320
|
|
|
); |
|
321
|
|
|
|
|
322
|
|
|
// Update. |
|
323
|
|
|
if ( ! empty( $row_id ) ) { |
|
324
|
|
|
$wpdb->query( |
|
325
|
|
|
$wpdb->prepare( "UPDATE {$wpdb->prefix}give_customers SET verify_key = %s, verify_throttle = %s WHERE id = %d LIMIT 1", $verify_key, $now, $row_id ) |
|
326
|
|
|
); |
|
327
|
|
|
} // Insert. |
|
328
|
|
|
else { |
|
329
|
|
|
$wpdb->query( |
|
330
|
|
|
$wpdb->prepare( "INSERT INTO {$wpdb->prefix}give_customers ( verify_key, verify_throttle) VALUES (%s, %s)", $verify_key, $now ) |
|
331
|
|
|
); |
|
332
|
|
|
} |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
|
/** |
|
336
|
|
|
* Is this a valid verify key? |
|
337
|
|
|
* |
|
338
|
|
|
* @since 1.0 |
|
339
|
|
|
* @access public |
|
340
|
|
|
* |
|
341
|
|
|
* @param $token string The token. |
|
342
|
|
|
* |
|
343
|
|
|
* @return bool |
|
344
|
|
|
*/ |
|
345
|
|
|
public function is_valid_verify_key( $token ) { |
|
346
|
|
|
/* @var WPDB $wpdb */ |
|
347
|
|
|
global $wpdb; |
|
|
|
|
|
|
348
|
|
|
|
|
349
|
|
|
// See if the verify_key exists. |
|
350
|
|
|
$row = $wpdb->get_row( |
|
351
|
|
|
$wpdb->prepare( "SELECT id, email FROM {$wpdb->prefix}give_customers WHERE verify_key = %s LIMIT 1", $token ) |
|
352
|
|
|
); |
|
353
|
|
|
|
|
354
|
|
|
$now = date( 'Y-m-d H:i:s' ); |
|
355
|
|
|
|
|
356
|
|
|
// Set token and remove verify key. |
|
357
|
|
|
if ( ! empty( $row ) ) { |
|
358
|
|
|
$wpdb->query( |
|
359
|
|
|
$wpdb->prepare( "UPDATE {$wpdb->prefix}give_customers SET verify_key = '', token = %s, verify_throttle = %s WHERE id = %d LIMIT 1", $token, $now, $row->id ) |
|
360
|
|
|
); |
|
361
|
|
|
|
|
362
|
|
|
$this->token_email = $row->email; |
|
363
|
|
|
$this->token = $token; |
|
364
|
|
|
|
|
365
|
|
|
return true; |
|
366
|
|
|
} |
|
367
|
|
|
|
|
368
|
|
|
return false; |
|
369
|
|
|
} |
|
370
|
|
|
|
|
371
|
|
|
/** |
|
372
|
|
|
* Users donations args |
|
373
|
|
|
* |
|
374
|
|
|
* Force Give to find donations by email, not user ID. |
|
375
|
|
|
* |
|
376
|
|
|
* @since 1.0 |
|
377
|
|
|
* @access public |
|
378
|
|
|
* |
|
379
|
|
|
* @param $args array User Donations arguments. |
|
380
|
|
|
* |
|
381
|
|
|
* @return mixed |
|
382
|
|
|
*/ |
|
383
|
|
|
public function users_donations_args( $args ) { |
|
384
|
|
|
$args['user'] = $this->token_email; |
|
385
|
|
|
|
|
386
|
|
|
return $args; |
|
387
|
|
|
} |
|
388
|
|
|
|
|
389
|
|
|
/** |
|
390
|
|
|
* Create בolumns |
|
391
|
|
|
* |
|
392
|
|
|
* Create the necessary columns for email access |
|
393
|
|
|
* |
|
394
|
|
|
* @since 1.0 |
|
395
|
|
|
* @access public |
|
396
|
|
|
* |
|
397
|
|
|
* @return void |
|
398
|
|
|
*/ |
|
399
|
|
|
public function create_columns() { |
|
400
|
|
|
|
|
401
|
|
|
global $wpdb; |
|
|
|
|
|
|
402
|
|
|
|
|
403
|
|
|
// Create columns in customers table |
|
404
|
|
|
$query = $wpdb->query( "ALTER TABLE {$wpdb->prefix}give_customers ADD `token` VARCHAR(255) CHARACTER SET utf8 NOT NULL, ADD `verify_key` VARCHAR(255) CHARACTER SET utf8 NOT NULL AFTER `token`, ADD `verify_throttle` DATETIME NOT NULL AFTER `verify_key`" ); |
|
405
|
|
|
|
|
406
|
|
|
// Columns added properly |
|
407
|
|
|
if ( $query ) { |
|
408
|
|
|
give_update_option( 'email_access_installed', 1 ); |
|
409
|
|
|
} |
|
410
|
|
|
|
|
411
|
|
|
} |
|
412
|
|
|
|
|
413
|
|
|
} |
|
414
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.