PluginProbe
Listdom: AI-powered Business Directory with Classifieds Ads Listings / trunk
Listdom: AI-powered Business Directory with Classifieds Ads Listings vtrunk
6.0.0 5.9.0 5.8.1 5.8.0 5.7.0 5.6.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.2.0 1.2.1 1.3.0 1.3.1 1.4.0 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.8.0 1.9.0 All 75 releases
listdom / app / includes / user.php

user.php in Listdom: AI-powered Business Directory with Classifieds Ads Listings trunk, at app/includes/user.php

654 lines 23.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class LSD_User extends LSD_Base
4 {
5 public const META_VERIFICATION_STATUS = 'lsd_email_verification_status';
6 public const META_VERIFICATION_TOKEN = 'lsd_email_verification_token';
7
8 public static function requires_email_verification(): bool
9 {
10 $auth = LSD_Options::auth();
11 return !empty($auth['register']['email_verification']);
12 }
13
14 public static function is_email_verified(int $user_id): bool
15 {
16 if (!$user_id) return false;
17
18 if (!self::requires_email_verification()) return true;
19
20 $status = get_user_meta($user_id, self::META_VERIFICATION_STATUS, true);
21 if ($status === 'pending') return false;
22 if ($status === 'verified') return true;
23
24 // Treat users without a status as verified to preserve backward compatibility.
25 return true;
26 }
27
28 public static function verification_required_message(): string
29 {
30 $message = esc_html__('Your account is pending email verification. Please check your inbox to confirm your email address.', 'listdom');
31 return apply_filters('lsd_email_verification_required_message', $message);
32 }
33
34 public static function verification_success_message(): string
35 {
36 $message = esc_html__('Your email address has been verified. You can now log in.', 'listdom');
37 return apply_filters('lsd_email_verification_success_message', $message);
38 }
39
40 public static function verification_failed_message(): string
41 {
42 $message = esc_html__('Email verification link is invalid or has already been used.', 'listdom');
43 return apply_filters('lsd_email_verification_failed_message', $message);
44 }
45
46 public static function request_email_verification(int $user_id): string
47 {
48 if (!$user_id) return '';
49
50 $token = wp_generate_password(32, false);
51
52 update_user_meta($user_id, self::META_VERIFICATION_TOKEN, $token);
53 update_user_meta($user_id, self::META_VERIFICATION_STATUS, 'pending');
54 update_user_meta($user_id, 'lsd_email_verification_requested', current_time('mysql'));
55
56 return $token;
57 }
58
59 public static function verification_url(int $user_id, string $token): string
60 {
61 $url = add_query_arg([
62 'lsd_verify' => $user_id,
63 'lsd_key' => $token,
64 ], home_url('/'));
65
66 return apply_filters('lsd_email_verification_url', $url, $user_id, $token);
67 }
68
69 public static function maybe_send_verification_email(int $user_id)
70 {
71 if (!$user_id || !self::requires_email_verification()) return;
72
73 $status = get_user_meta($user_id, self::META_VERIFICATION_STATUS, true);
74 if ($status === 'verified') return;
75
76 $token = get_user_meta($user_id, self::META_VERIFICATION_TOKEN, true);
77
78 // When a pending token already exists, reuse it to avoid invalidating
79 // previously issued verification links (for example, when multiple
80 // registration hooks trigger this method).
81 if ($status !== 'pending' || !$token)
82 {
83 $token = self::request_email_verification($user_id);
84 if (!$token) return;
85 }
86
87 $url = self::verification_url($user_id, $token);
88
89 do_action('lsd_user_email_verification', $user_id, $token, $url);
90 }
91
92 public static function verify_email(int $user_id, string $token): bool
93 {
94 if (!$user_id) return false;
95
96 $token = sanitize_text_field($token);
97 if (!$token) return false;
98
99 $saved_token = get_user_meta($user_id, self::META_VERIFICATION_TOKEN, true);
100 if (!$saved_token) return false;
101
102 $is_valid = function_exists('hash_equals')
103 ? hash_equals($saved_token, $token)
104 : $saved_token === $token;
105
106 if (!$is_valid) return false;
107
108 delete_user_meta($user_id, self::META_VERIFICATION_TOKEN);
109 update_user_meta($user_id, self::META_VERIFICATION_STATUS, 'verified');
110 update_user_meta($user_id, 'lsd_email_verification_time', current_time('mysql'));
111
112 do_action('lsd_user_email_verified', $user_id);
113
114 return true;
115 }
116
117 public static function create(string $email)
118 {
119 $email = sanitize_email($email);
120
121 $exists = email_exists($email);
122 if ($exists) return $exists;
123 else
124 {
125 $results = register_new_user($email, $email);
126 if (!is_wp_error($results)) return $results;
127 }
128
129 return 0;
130 }
131
132 public static function register($user_login, $user_email, $password = null)
133 {
134 // Password
135 if (!$password) $password = wp_generate_password();
136
137 // Errors
138 $errors = new WP_Error();
139 $sanitized_user_login = sanitize_user($user_login);
140
141 /**
142 * Filters the email address of a user being registered.
143 *
144 * @param string $user_email The email address of the new user.
145 * @since 2.1.0
146 *
147 */
148 $user_email = apply_filters('user_registration_email', $user_email);
149
150 // Check the username.
151 if ('' === $sanitized_user_login) $errors->add('empty_username', wp_kses_post(__('<strong>Error</strong>: Please enter a username.', 'listdom')));
152 else if (!validate_username($user_login))
153 {
154 $errors->add('invalid_username', wp_kses_post(__('<strong>Error</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.', 'listdom')));
155 $sanitized_user_login = '';
156 }
157 else if (username_exists($sanitized_user_login)) $errors->add('username_exists', wp_kses_post(__('<strong>Error</strong>: This username is already registered. Please choose another one.', 'listdom')));
158 else
159 {
160 /** This filter is documented in wp-includes/user.php */
161 $illegal_user_logins = (array) apply_filters('illegal_user_logins', []);
162 if (in_array(strtolower($sanitized_user_login), array_map('strtolower', $illegal_user_logins), true)) $errors->add('invalid_username', wp_kses_post(__('<strong>Error</strong>: Sorry, that username is not allowed.', 'listdom')));
163 }
164
165 // Check the email address.
166 if ('' === $user_email) $errors->add('empty_email', wp_kses_post(__('<strong>Error</strong>: Please type your email address.', 'listdom')));
167 else if (!is_email($user_email))
168 {
169 $errors->add('invalid_email', wp_kses_post(__('<strong>Error</strong>: The email address isn&#8217;t correct.', 'listdom')));
170 $user_email = '';
171 }
172 else if (email_exists($user_email)) $errors->add('email_exists', wp_kses_post(__('<strong>Error</strong>: This email is already registered. Please choose another one.', 'listdom')));
173
174 /**
175 * Fires when submitting registration form data, before the user is created.
176 *
177 * @param string $sanitized_user_login The submitted username after being sanitized.
178 * @param string $user_email The submitted email.
179 * @param WP_Error $errors Contains any errors with submitted username and email,
180 * e.g., an empty field, an invalid username or email,
181 * or an existing username or email.
182 * @since 2.1.0
183 *
184 */
185 do_action('register_post', $sanitized_user_login, $user_email, $errors);
186
187 /**
188 * Filters the errors encountered when a new user is being registered.
189 *
190 * The filtered WP_Error object may, for example, contain errors for an invalid
191 * or existing username or email address. A WP_Error object should always be returned,
192 * but may or may not contain errors.
193 *
194 * If any errors are present in $errors, this will abort the user's registration.
195 *
196 * @param WP_Error $errors A WP_Error object containing any errors encountered
197 * during registration.
198 * @param string $sanitized_user_login User's username after it has been sanitized.
199 * @param string $user_email User's email.
200 * @since 2.1.0
201 *
202 */
203 $errors = apply_filters('registration_errors', $errors, $sanitized_user_login, $user_email);
204
205 // Return Errors
206 if ($errors->has_errors()) return $errors;
207
208 $user_id = wp_create_user($sanitized_user_login, $password, $user_email);
209 if (!$user_id || is_wp_error($user_id))
210 {
211 $errors->add('registerfail', sprintf(
212 /* translators: %s: Support email address. */
213 wp_kses_post(__('<strong>Error</strong>: Couldn&#8217;t register you&hellip; please contact the <a href="mailto:%s">site admin</a>!', 'listdom')),
214 get_option('admin_email')
215 ));
216
217 return $errors;
218 }
219
220 update_user_option($user_id, 'default_password_nag', true, true); // Set up the password change nag.
221
222 /**
223 * Fires after a new user registration has been recorded.
224 *
225 * @param int $user_id ID of the newly registered user.
226 * @since 4.4.0
227 *
228 */
229 do_action('register_new_user', $user_id);
230
231 self::maybe_send_verification_email($user_id);
232
233 return $user_id;
234 }
235
236 public static function login($user_id)
237 {
238 wp_clear_auth_cookie();
239 wp_set_current_user($user_id);
240 wp_set_auth_cookie($user_id);
241 }
242
243 /**
244 * @param int $listing_id
245 * @param string $email
246 * @param string $password
247 * @param string $fullname
248 * @return int|mixed|WP_Error|null
249 */
250 public static function listing(int $listing_id, string $email, string $password = '', string $fullname = '')
251 {
252 if (!is_email($email)) return null;
253
254 $user_id = null;
255 $exists = email_exists($email);
256
257 if ($exists) $user_id = $exists;
258 else
259 {
260 $password = trim($password) ? sanitize_text_field($password) : wp_generate_password();
261 $registered = LSD_User::register($email, $email, $password);
262
263 if (!is_wp_error($registered))
264 {
265 $user_id = $registered;
266 if (trim($fullname))
267 {
268 [$first_name, $last_name] = LSD_Main::get_name_parts(sanitize_text_field($fullname));
269
270 // Update User
271 wp_update_user([
272 'ID' => $user_id,
273 'first_name' => $first_name,
274 'last_name' => $last_name,
275 'display_name' => trim($first_name . ' ' . $last_name),
276 ]);
277 }
278 }
279 }
280
281 // Assign Listing to new Owner
282 LSD_Main::assign($listing_id, $user_id);
283
284 // User ID
285 return $user_id;
286 }
287
288 public static function get_user_avatar($id_or_email = null, $size = 96)
289 {
290 if (!$id_or_email)
291 {
292 $user_id = get_current_user_id();
293 if (!$user_id) return '';
294
295 $id_or_email = $user_id;
296 }
297
298 $user_id = 0;
299
300 if (is_numeric($id_or_email)) $user_id = (int) $id_or_email;
301 else if (is_object($id_or_email) && isset($id_or_email->ID)) $user_id = (int) $id_or_email->ID;
302 else if (is_string($id_or_email) && is_email($id_or_email))
303 {
304 $user = get_user_by('email', $id_or_email);
305 if ($user) $user_id = $user->ID;
306 }
307
308 if ($user_id)
309 {
310 $profile_image_id = get_user_meta($user_id, 'lsd_profile_image', true);
311 if ($profile_image_id)
312 {
313 $profile_image_url = wp_get_attachment_url($profile_image_id);
314 if ($profile_image_url) return '<img src="' . esc_url($profile_image_url) . '" class="avatar avatar-' . esc_attr($size) . ' photo" height="' . esc_attr($size) . '" width="' . esc_attr($size) . '" alt="' . esc_attr__('Profile Image', 'listdom') . '">';
315 }
316 }
317
318 return get_avatar($id_or_email, $size);
319 }
320
321 public static function get_user_image_url(int $user_id, string $meta_key): string
322 {
323 if (!$user_id || !$meta_key) return '';
324
325 $attachment_id = (int) get_user_meta($user_id, $meta_key, true);
326 if (!$attachment_id) return '';
327
328 $url = wp_get_attachment_url($attachment_id);
329 if ($url) return $url;
330
331 return '';
332 }
333
334 public static function get_user_info($username = ''): array
335 {
336 if ($username)
337 {
338 if (is_numeric($username)) $user = get_user_by('ID', $username);
339 else $user = get_user_by('login', $username);
340 }
341 else
342 {
343 $user_id = get_current_user_id();
344 if (!$user_id) return [];
345
346 $user = get_userdata($user_id);
347 }
348
349 if (!($user instanceof WP_User)) return [];
350
351 $meta_keys = [
352 'lsd_profile_image' => 'profile_image',
353 'lsd_hero_image' => 'hero_image',
354 'lsd_job_title' => 'job_title',
355 'description' => 'bio',
356 'lsd_phone' => 'phone',
357 'lsd_mobile' => 'mobile',
358 'lsd_website' => 'website',
359 'lsd_fax' => 'fax',
360 'lsd_facebook' => 'facebook',
361 'lsd_twitter' => 'twitter',
362 'lsd_pinterest' => 'pinterest',
363 'lsd_linkedin' => 'linkedin',
364 'lsd_instagram' => 'instagram',
365 'lsd_whatsapp' => 'whatsapp',
366 'lsd_youtube' => 'youtube',
367 'lsd_tiktok' => 'tiktok',
368 'lsd_telegram' => 'telegram',
369 ];
370
371 $user_meta = [];
372 foreach ($meta_keys as $meta_key => $field_name)
373 {
374 $user_meta[$field_name] = get_user_meta($user->ID, $meta_key, true) ?: '';
375 }
376
377 return array_merge([
378 'ID' => $user->ID,
379 'display_name' => $user->display_name,
380 'email' => $user->user_email,
381 'description' => $user->description,
382 'first_name' => $user->first_name ?: '',
383 'last_name' => $user->last_name ?: '',
384 ], $user_meta);
385 }
386
387 public static function profile_edit_fields_defaults(): array
388 {
389 $social = [];
390 foreach (self::profile_social_networks() as $network) $social[$network] = 1;
391
392 return [
393 'job_title' => 1,
394 'bio' => 1,
395 'profile_image' => 1,
396 'hero_image' => 1,
397 'email' => 1,
398 'phone' => 1,
399 'mobile' => 1,
400 'website' => 1,
401 'fax' => 1,
402 'social' => $social,
403 ];
404 }
405
406 public static function profile_social_networks(): array
407 {
408 $networks = [
409 'facebook',
410 'twitter',
411 'pinterest',
412 'linkedin',
413 'instagram',
414 'whatsapp',
415 'youtube',
416 'tiktok',
417 'telegram',
418 ];
419
420 $socials = LSD_Options::socials();
421 foreach ($socials as $network => $options)
422 {
423 if (!is_string($network) || trim($network) === '') continue;
424 if (!in_array($network, $networks, true)) $networks[] = $network;
425 }
426
427 return $networks;
428 }
429
430 public static function profile_edit_fields(): array
431 {
432 $auth = LSD_Options::auth();
433 $fields = isset($auth['profile']['fields']) && is_array($auth['profile']['fields']) ? $auth['profile']['fields'] : [];
434 $defaults = self::profile_edit_fields_defaults();
435
436 $fields = self::parse_args($fields, $defaults);
437 foreach (['job_title', 'bio', 'profile_image', 'hero_image', 'email', 'phone', 'mobile', 'website', 'fax'] as $field)
438 {
439 $fields[$field] = isset($fields[$field]) && (int) $fields[$field] === 0 ? 0 : 1;
440 }
441
442 $social_defaults = $defaults['social'];
443 $legacy_socials = isset($fields['socials']) && (int) $fields['socials'] === 0 ? 0 : 1;
444 $social = isset($fields['social']) && is_array($fields['social']) ? $fields['social'] : [];
445 $social = self::parse_args($social, $social_defaults);
446
447 foreach ($social_defaults as $network => $enabled)
448 {
449 $social[$network] = ($legacy_socials === 0 || (isset($social[$network]) && (int) $social[$network] === 0)) ? 0 : 1;
450 }
451
452 $fields['social'] = $social;
453 unset($fields['socials']);
454
455 return $fields;
456 }
457
458 public static function is_profile_edit_field_enabled(string $field): bool
459 {
460 $fields = self::profile_edit_fields();
461 if (!array_key_exists($field, $fields)) return true;
462
463 return (int) $fields[$field] === 1;
464 }
465
466 public static function is_profile_social_field_enabled(string $network): bool
467 {
468 $fields = self::profile_edit_fields();
469
470 if (!isset($fields['social']) || !is_array($fields['social'])) return true;
471 if (!array_key_exists($network, $fields['social'])) return true;
472
473 return (int) $fields['social'][$network] === 1;
474 }
475
476 public static function has_enabled_profile_social_fields(): bool
477 {
478 $fields = self::profile_edit_fields();
479 if (!isset($fields['social']) || !is_array($fields['social'])) return true;
480
481 foreach ($fields['social'] as $enabled)
482 {
483 if ((int) $enabled === 1) return true;
484 }
485
486 return false;
487 }
488
489 public static function send_forgot_password_email(WP_User $user): bool
490 {
491 // Generate password reset key and link
492 $reset_key = get_password_reset_key($user);
493 if (is_wp_error($reset_key)) return false;
494
495 $reset_link = self::password_reset_url($user->user_login, $reset_key);
496
497 if (is_multisite()) $site_name = get_network()->site_name;
498 else $site_name = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
499
500 // Send password reset email
501 $message = esc_html__('Someone has requested a password reset for the following account:', 'listdom') . "\r\n\r\n";
502 $message .= sprintf(
503 /* translators: %s: Site name. */
504 esc_html__('Site Name: %s', 'listdom'),
505 $site_name
506 ) . "\r\n\r\n";
507 $message .= sprintf(
508 /* translators: %s: Username for the account. */
509 esc_html__('Username: %s', 'listdom'),
510 $user->user_login
511 ) . "\r\n\r\n";
512 $message .= esc_html__('If this was a mistake, just ignore this email and nothing will happen.', 'listdom') . "\r\n\r\n";
513 $message .= esc_html__('To reset your password, visit the following address:', 'listdom') . "\r\n\r\n";
514 $message .= '<' . $reset_link . ">\r\n";
515
516 $title = sprintf(
517 /* translators: %s: Site name. */
518 esc_html__('[%s] Password Reset', 'listdom'),
519 $site_name
520 );
521
522 $title = apply_filters('retrieve_password_title', $title, $user->user_login, $user);
523
524 $default_message = $message;
525 $message = apply_filters('retrieve_password_message', $message, $reset_key, $user->user_login, $user);
526
527 // Match WordPress behavior: a falsey filtered message suppresses the email.
528 if (!$message) return true;
529
530 $html_message = self::password_reset_email_html($message, $default_message, $reset_link);
531
532 return wp_mail($user->user_email, wp_specialchars_decode($title), $html_message, ['Content-Type: text/html; charset=UTF-8']);
533 }
534
535 public static function password_reset_email_html(string $message, string $default_message, string $reset_link): string
536 {
537 if (self::password_reset_email_contains_html($message)) return $message;
538
539 $html_message = wpautop(esc_html($message));
540 $link = self::password_reset_email_link($message, $default_message, $reset_link);
541
542 if (!$link) return $html_message;
543
544 $html_message .= '<p><a href="' . esc_url($link) . '">' . esc_html__('Reset your password', 'listdom') . '</a></p>';
545 $html_message .= '<p><a href="' . esc_url($link) . '">' . esc_html($link) . '</a></p>';
546
547 return $html_message;
548 }
549
550 public static function password_reset_email_link(string $message, string $default_message, string $reset_link): string
551 {
552 if ($message === $default_message) return $reset_link;
553
554 $prefix = strstr($default_message, '<' . $reset_link . '>', true);
555 if (is_string($prefix) && $prefix !== '')
556 {
557 $start = strpos($message, $prefix);
558 if ($start !== false)
559 {
560 $line = substr($message, $start + strlen($prefix));
561 $line = preg_split("/\r\n|\n|\r/", $line, 2)[0] ?? '';
562 $urls = wp_extract_urls($line);
563
564 if (count($urls) === 1) return $urls[0];
565 }
566 }
567
568 return '';
569 }
570
571 public static function password_reset_email_contains_html(string $message): bool
572 {
573 $without_plaintext_tokens = preg_replace([
574 '/<https?:\/\/[^>\s]+>/i',
575 '/<[A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,}>/i',
576 ], '', $message);
577
578 if (!is_string($without_plaintext_tokens)) $without_plaintext_tokens = $message;
579
580 return $without_plaintext_tokens !== wp_strip_all_tags($without_plaintext_tokens);
581 }
582
583 public static function password_reset_url(string $user_login, string $reset_key): string
584 {
585 $auth = LSD_Options::auth();
586
587 $custom_form_enabled = !empty($auth['auth']['forgot_password_form']) && empty($auth['auth']['hide_forgot_password_form']);
588 $custom_page_id = isset($auth['auth']['forgot_password_page']) ? absint($auth['auth']['forgot_password_page']) : 0;
589
590 if ($custom_form_enabled && $custom_page_id)
591 {
592 $page = get_post($custom_page_id);
593
594 if ($page instanceof WP_Post && $page->post_type === 'page' && $page->post_status === 'publish')
595 {
596 $url = get_permalink($page);
597
598 if (is_string($url) && trim($url))
599 {
600 return add_query_arg([
601 'tab' => 'lostpassword',
602 'action' => 'rp',
603 'key' => $reset_key,
604 'login' => $user_login,
605 ], $url);
606 }
607 }
608 }
609
610 return network_site_url("wp-login.php?action=rp&key=$reset_key&login=" . rawurlencode($user_login), 'login');
611 }
612
613 public static function profile_link(int $id): string
614 {
615 $auth = LSD_Options::auth();
616
617 $profile_page_id = isset($auth['profile']['page']) && $auth['profile']['page'] ? $auth['profile']['page'] : 0;
618 $profile_page = $profile_page_id ? get_post($profile_page_id) : null;
619
620 // Listdom Profile Page
621 if ($profile_page instanceof WP_Post && $profile_page->post_status === 'publish')
622 {
623 // User
624 $user = get_user($id);
625
626 if ($user && isset($user->user_login))
627 {
628 // URL Structure
629 $structure = get_option('permalink_structure');
630
631 // URL
632 $url = get_permalink($profile_page);
633
634 // Main
635 $main = new LSD_Main();
636
637 $slug = $user->user_login;
638 if (is_email($slug)) $slug = $user->ID;
639
640 // Generate URL
641 if ($structure) return trim($url, '/ ') . '/' . sanitize_title($slug);
642 else return $main->add_qs_var('user', sanitize_title($slug), $url);
643 }
644 }
645
646 return get_author_posts_url($id);
647 }
648
649 public static function roles(bool $only_keys = false): array
650 {
651 return LSD_Roles::supported($only_keys);
652 }
653 }
654