PluginProbe
Magic Login – Magic Link & Passwordless Authentication for WordPress – Login Without Password / 2.8
Magic Login – Magic Link & Passwordless Authentication for WordPress – Login Without Password v2.8
2.8.1 2.1 2.1.1 2.1.2 2.1.3 2.2 2.3 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.4 2.4.1 2.4.2 2.5 2.5.1 2.6 2.6.1 2.6.2 2.6.3 2.7 2.7.1 2.8 trunk All 48 releases
magic-login / includes / utils.php

utils.php in Magic Login – Magic Link & Passwordless Authentication for WordPress – Login Without Password 2.8, at includes/utils.php

820 lines 21.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Common utilities and functions
4 *
5 * @package MagicLogin
6 */
7
8 namespace MagicLogin\Utils;
9
10 use MagicLogin\Encryption;
11 use const MagicLogin\Constants\CRON_HOOK_NAME;
12 use const MagicLogin\Constants\SETTING_OPTION;
13 use const MagicLogin\Constants\TOKEN_USER_META;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Create token
21 *
22 * @param object $user \WP_User object
23 * @param string $context Context (email|email_code|sms|sms_code) @since 2.4
24 *
25 * @return string
26 */
27 function create_user_token( $user, $context = 'email' ) {
28 $settings = get_settings(); // phpcs:ignore
29 $tokens = get_user_meta( $user->ID, TOKEN_USER_META, true );
30 $tokens = is_string( $tokens ) ? array( $tokens ) : $tokens;
31 $new_token = sha1( wp_generate_password() );
32
33 switch ( $context ) {
34 case 'sms':
35 // helps to keep url link short due to 300 char limit for most of the SMS providers
36 $new_token = substr( $new_token, 0, 12 );
37 break;
38 case 'sms_code':
39 // 6-digit PIN for SMS
40 $new_token = wp_rand( 100000, 999999 );
41 break;
42 case 'email_code':
43 $charset = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
44 $charset_length = strlen( $charset ) - 1;
45 $new_token = '';
46
47 for ( $i = 0; $i < 10; $i++ ) {
48 $new_token .= $charset[ wp_rand( 0, $charset_length ) ];
49 }
50 break;
51 }
52
53 /**
54 * Filter the token
55 *
56 * @hook magic_login_create_user_token
57 *
58 * @param {string} $new_token New token
59 * @param {int} $user->ID User ID
60 * @param {string} $context Context
61 *
62 * @return {string} New value
63 * @since 2.4
64 */
65 $new_token = apply_filters( 'magic_login_create_user_token', $new_token, $user->ID, $context );
66
67 $hashed_token = hash_hmac( 'sha256', $new_token, wp_salt() );
68
69 $ip = sha1( get_client_ip() );
70 if ( defined( 'WP_CLI' ) && WP_CLI ) {
71 $ip = 'cli';
72 }
73
74 $tokens[] = [
75 'token' => $hashed_token,
76 'time' => time(),
77 'ip_hash' => $ip,
78 ];
79
80 update_user_meta( $user->ID, TOKEN_USER_META, $tokens );
81
82 if ( absint( $settings['token_ttl'] ) > 0 ) { // eternal token
83 wp_schedule_single_event( time() + ( $settings['token_ttl'] * MINUTE_IN_SECONDS ), CRON_HOOK_NAME, array( $user->ID ) );
84 }
85
86 return $new_token;
87 }
88
89
90 /**
91 * Create login link for given user
92 *
93 * @param object $user WP_User object
94 * @param string $context Context (email|email_code|sms|sms_code) @since 2.4
95 * @param string $redirect_to Redirect URL
96 *
97 * @return mixed|string
98 */
99 function create_login_link( $user, $context = 'email', $redirect_to = null ) {
100 global $magic_login_token;
101 $token = create_user_token( $user, $context );
102 $magic_login_token = $token;
103
104 $query_args = array(
105 'user_id' => $user->ID,
106 'token' => $token,
107 'magic-login' => 1,
108 );
109
110 if ( ! empty( $_POST['redirect_to'] ) ) {
111 $query_args['redirect_to'] = urlencode( wp_unslash( $_POST['redirect_to'] ) ); // phpcs:ignore
112 }
113
114 if ( ! empty( $redirect_to ) ) {
115 $query_args['redirect_to'] = rawurlencode( $redirect_to );
116 }
117
118 $login_url = esc_url_raw( add_query_arg( $query_args, wp_login_url() ) );
119
120 /**
121 * Filter the login URL
122 *
123 * @hook magic_login_create_login_link
124 *
125 * @param {string} $login_url Login URL
126 * @param {object} $user WP_User object
127 * @param {string} $context Context
128 * @param {string} $redirect_to Redirect URL
129 *
130 * @return {string} New value
131 * @since 2.4
132 */
133 $login_url = apply_filters( 'magic_login_create_login_link', $login_url, $user, $context, $redirect_to );
134
135 return $login_url;
136 }
137
138 /**
139 * Localize the frontend script with AJAX nonce data.
140 *
141 * @return void
142 */
143 function localize_frontend_script() {
144 static $localized = false;
145
146 if ( $localized ) {
147 return;
148 }
149
150 wp_localize_script(
151 'magic-login-frontend',
152 'magicLoginFrontend',
153 [
154 'ajaxNonce' => wp_create_nonce( 'magic-login-ajax-request' ),
155 ]
156 );
157
158 $localized = true;
159 }
160
161 /**
162 * Get client raw ip
163 * this should be hashed
164 *
165 * @return mixed
166 */
167 function get_client_ip() {
168 $adds = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
169
170 /**
171 * `HTTP_X_FORWARDED_FOR` removed in 1.5
172 * Filters the ip address
173 *
174 * @hook magic_login_client_ip
175 *
176 * @param {string} REMOTE_ADDR
177 *
178 * @return {string} New value.
179 * @since 1.5
180 */
181 return apply_filters( 'magic_login_client_ip', $adds );
182 }
183
184 /**
185 * Get settings with defaults
186 *
187 * @return array
188 * @since 1.0
189 */
190 function get_settings() {
191 $defaults = [
192 'is_default' => false,
193 'add_login_button' => true,
194 'token_ttl' => 5,
195 'token_validity' => 1,
196 'token_interval' => 'MINUTE',
197 'enable_brute_force_protection' => false,
198 'brute_force_bantime' => 60, // in minutes
199 'brute_force_login_attempt' => 10,
200 'brute_force_login_time' => 5, // in minutes
201 'enable_login_throttling' => false,
202 'login_throttling_limit' => 10,
203 'login_throttling_time' => 15, // in minutes
204 'enable_ip_check' => false,
205 'enable_domain_restriction' => false,
206 'allowed_domains' => '',
207 'login_email' => get_default_login_email_text(),
208 'enable_login_redirection' => false,
209 'default_redirection_url' => '',
210 'enforce_redirection_rules' => true,
211 'enable_wp_login_redirection' => false,
212 'enable_role_based_redirection' => false,
213 'role_based_redirection_rules' => [],
214 'email_subject' => __( 'Log in to {{SITENAME}}', 'magic-login' ),
215 'auto_login_links' => false,
216 'enable_ajax' => false,
217 'enable_woo_integration' => false,
218 'woo_position' => 'before',
219 'enable_woo_customer_login' => false,
220 'woo_customer_login_position' => 'before',
221 'enable_edd_checkout' => false,
222 'edd_checkout_position' => 'edd_before_purchase_form',
223 'enable_edd_login' => false,
224 'edd_login_position' => 'before',
225 'registration' => [
226 'enable' => false,
227 'mode' => 'auto', // or standard|shortcode
228 'fallback_email_field' => true, // show email field on auto registration mode as a fallback
229 'show_name_field' => true,
230 'require_name_field' => true,
231 'show_terms_field' => false,
232 'require_terms_field' => false,
233 'terms' => '',
234 'send_email' => true,
235 'email_subject' => esc_html__( 'Welcome to {{SITENAME}}', 'magic-login' ),
236 'email_content' => get_default_registration_email_text(),
237 'enable_domain_restriction' => false,
238 'allowed_domains' => '',
239 'role' => '',
240 'enable_redirection' => false,
241 'redirection_url' => '',
242 ],
243 'spam_protection' => [
244 'service' => 'recaptcha',
245 'enable_login' => false,
246 'enable_registration' => false,
247 'enable_honeypot' => false,
248 ],
249 'recaptcha' => [
250 'type' => 'v3', // which version to use
251 'v2_checkbox' => [
252 'site_key' => '',
253 'secret_key' => '',
254 ],
255 'v2_invisible' => [
256 'site_key' => '',
257 'secret_key' => '',
258 ],
259 'v3' => [
260 'site_key' => '',
261 'secret_key' => '',
262 ],
263 ],
264 'cf_turnstile' => [
265 'site_key' => '',
266 'secret_key' => '',
267 ],
268 'friendly_captcha' => [
269 'site_key' => '',
270 'secret_key' => '',
271 'endpoint' => 'global',
272 ],
273 'enable_rest_api' => false,
274 'enable_api_rate_limit' => false,
275 'rate_limit_max_requests' => 60,
276 'sms' => [
277 'enable' => false,
278 'provider' => 'twilio',
279 'twilio' => [
280 'account_sid' => '',
281 'auth_token' => '',
282 'from' => '',
283 ],
284 /**
285 * phone_only: Only send SMS if the user enters their phone number (instead of email) when logging in.
286 * sms_and_email: If the user has a phone number linked to their account, send both SMS and email.
287 * sms_or_email: Always send SMS if the user has a phone number, but do not send email notifications.
288 */
289 'sms_sending_strategy' => 'phone_only',
290 'wp_registration' => false,
291 'wp_require_phone' => false,
292 'magic_registration' => false,
293 'magic_registration_require_phone' => false,
294 'login_message' => esc_html__( 'Here is your login code: {{MAGIC_LOGIN_CODE}}. It will expire in {{EXPIRES_WITH_INTERVAL}}.', 'magic-login' ),
295 'send_registration_message' => false,
296 'registration_message' => esc_html__( 'Welcome {{FULL_NAME}}! Your account on {{SITENAME}} has been created. You can login here: {{MAGIC_LINK}}.', 'magic-login' ),
297 ],
298 ];
299
300 if ( MAGIC_LOGIN_IS_NETWORK ) {
301 $settings = get_site_option( SETTING_OPTION, [] );
302 } else {
303 $settings = get_option( SETTING_OPTION, [] );
304 }
305
306 // Merge settings with defaults, ensuring new additions and nested arrays are included
307 $settings = array_replace_recursive( $defaults, $settings );
308
309 return $settings;
310 }
311
312 /**
313 * Default login email message
314 *
315 * @return mixed|string|void
316 */
317 function get_default_login_email_text() {
318 /* translators: Do not translate USERNAME, SITENAME,EXPIRES, MAGIC_LINK, SITENAME, SITEUR, EXPIRES_WITH_INTERVAL: those are placeholders. */
319 $email_text = __(
320 'Hi {{USERNAME}},
321
322 Click and confirm that you want to log in to {{SITENAME}}. This link will expire in {{EXPIRES_WITH_INTERVAL}} and can only be used once:
323
324 <a href="{{MAGIC_LINK}}" target="_blank" rel="noreferrer noopener">Log In</a>
325
326 Need the link? {{MAGIC_LINK}}
327
328
329 You can safely ignore and delete this email if you do not want to log in.
330
331 Regards,
332 All at {{SITENAME}}
333 {{SITEURL}}',
334 'magic-login'
335 );
336
337 return $email_text;
338 }
339
340 /**
341 * Is plugin activated network wide?
342 *
343 * @param string $plugin_file file path
344 *
345 * @return bool
346 * @since 1.0
347 */
348 function is_network_wide( $plugin_file ) {
349 if ( ! is_multisite() ) {
350 return false;
351 }
352
353 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
354 require_once ABSPATH . '/wp-admin/includes/plugin.php';
355 }
356
357 return is_plugin_active_for_network( plugin_basename( $plugin_file ) );
358 }
359
360
361 /**
362 * Get login link
363 *
364 * @return mixed|string
365 */
366 function get_magic_login_url() {
367 _deprecated_function( __FUNCTION__, '2.3.4', __NAMESPACE__ . '\get_wp_login_url' );
368
369 return get_wp_login_url();
370 }
371
372 /**
373 * Get login link
374 *
375 * @return mixed|string
376 */
377 function get_wp_login_url() {
378 $url = site_url( 'wp-login.php?action=magic_login', 'login_post' );
379 /**
380 * Filter the login URL for magic login
381 *
382 * @hook magic_login_get_wp_login_url
383 * @since 2.3.4
384 */
385 $url = apply_filters( 'magic_login_get_wp_login_url', $url );
386
387 return esc_url_raw( $url );
388 }
389
390 /**
391 * Get user tokens
392 *
393 * @param int $user_id User ID
394 * @param bool $clear_expired flag for clean-up expired tokens
395 *
396 * @return array|mixed
397 */
398 function get_user_tokens( $user_id, $clear_expired = false ) {
399 $tokens = get_user_meta( $user_id, TOKEN_USER_META, true );
400 $tokens = is_array( $tokens ) ? $tokens : [];
401
402 /**
403 * Filter user tokens
404 *
405 * @hook magic_login_user_tokens
406 *
407 * @param {array} $tokens User tokens.
408 * @param {int} $user_id User ID.
409 * @param {boolean} $clear_expired Whether to clear expired tokens or not.
410 *
411 * @return {array} New value
412 * @since 2.1
413 */
414 $tokens = (array) apply_filters( 'magic_login_user_tokens', $tokens, $user_id, $clear_expired );
415
416 if ( $clear_expired ) {
417 $ttl = get_ttl_by_user( $user_id );
418
419 if ( 0 === $ttl ) { // means token lives forever till used
420 return $tokens;
421 }
422
423 foreach ( $tokens as $index => $token_data ) {
424 if ( empty( $token_data ) || ! isset( $token_data['time'] ) ) {
425 unset( $tokens[ $index ] );
426 continue;
427 }
428
429 if ( time() > absint( $token_data['time'] ) + ( $ttl * MINUTE_IN_SECONDS ) ) {
430 unset( $tokens[ $index ] );
431 }
432 }
433 update_user_meta( $user_id, TOKEN_USER_META, $tokens );
434 }
435
436 return $tokens;
437 }
438
439 /**
440 * Get default redirect url for given user
441 *
442 * @param \WP_User $user User object
443 *
444 * @return string|void
445 */
446 function get_user_default_redirect( $user ) {
447 if ( is_multisite() && ! get_active_blog_for_user( $user->ID ) && ! is_super_admin( $user->ID ) ) {
448 $redirect_to = user_admin_url();
449 } elseif ( is_multisite() && ! $user->has_cap( 'read' ) ) {
450 $redirect_to = get_dashboard_url( $user->ID );
451 } elseif ( ! $user->has_cap( 'edit_posts' ) ) {
452 $redirect_to = $user->has_cap( 'read' ) ? admin_url( 'profile.php' ) : home_url();
453 } else {
454 $redirect_to = admin_url();
455 }
456
457 return $redirect_to;
458 }
459
460 /**
461 * Delete all token meta
462 */
463 function delete_all_tokens() {
464 global $wpdb;
465
466 return $wpdb->delete( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
467 $wpdb->usermeta,
468 [
469 'meta_key' => TOKEN_USER_META, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
470 ]
471 );
472 }
473
474
475 /**
476 * Allowed intervals for TTL.
477 *
478 * @return array
479 * @since 1.2
480 */
481 function get_allowed_intervals() {
482 return [
483 'MINUTE' => esc_html__( 'Minute(s)', 'magic-login' ),
484 'HOUR' => esc_html__( 'Hour(s)', 'magic-login' ),
485 'DAY' => esc_html__( 'Day(s)', 'magic-login' ),
486 ];
487 }
488
489 /**
490 * Convert minutes to possible time format
491 *
492 * @param int $timeout_in_minutes TTL in minutes
493 *
494 * @return array
495 * @since 1.2
496 */
497 function get_ttl_with_interval( $timeout_in_minutes ) {
498 $ttl = $timeout_in_minutes;
499 $interval = 'MINUTE';
500
501 if ( $ttl > 0 ) {
502 if ( 0 === (int) ( $ttl % 1440 ) ) {
503 $ttl = $ttl / 1440;
504 $interval = 'DAY';
505 } elseif ( 0 === (int) ( $ttl % 60 ) ) {
506 $ttl = $ttl / 60;
507 $interval = 'HOUR';
508 }
509 }
510
511 return array(
512 $ttl,
513 $interval,
514 );
515 }
516
517
518 /**
519 * Get the documentation url
520 *
521 * @param string $path The path of documentation
522 * @param string $fragment URL Fragment
523 *
524 * @return string final URL
525 */
526 function get_doc_url( $path = null, $fragment = '' ) {
527 $doc_base = 'https://handyplugins.co/';
528 $utm_parameters = '?utm_source=wp_admin&utm_medium=plugin&utm_campaign=settings_page';
529
530 if ( ! empty( $path ) ) {
531 $doc_base .= ltrim( $path, '/' );
532 }
533
534 $doc_url = trailingslashit( $doc_base ) . $utm_parameters;
535
536 if ( ! empty( $fragment ) ) {
537 $doc_url .= '#' . $fragment;
538 }
539
540 return $doc_url;
541 }
542
543 /**
544 * Check whether current screen is magic login settings page or not
545 *
546 * @return bool
547 * @since 1.2.1
548 */
549 function is_magic_login_settings_screen() {
550 $current_screen = get_current_screen();
551
552 if ( ! is_a( $current_screen, '\WP_Screen' ) ) {
553 return false;
554 }
555
556 if ( false !== strpos( $current_screen->base, 'magic-login' ) ) {
557 return true;
558 }
559
560 return false;
561 }
562
563 /**
564 * Mask given string
565 *
566 * @param string $input_string String
567 * @param int $unmask_length The length of unmask
568 *
569 * @return string
570 * @since 2.2
571 */
572 function mask_string( $input_string, $unmask_length ) {
573 $output_string = substr( $input_string, 0, $unmask_length );
574
575 if ( strlen( $input_string ) > $unmask_length ) {
576 $output_string .= str_repeat( '*', strlen( $input_string ) - $unmask_length );
577 }
578
579 return $output_string;
580 }
581
582
583 /**
584 * Check if the given value is masked
585 *
586 * @param string $value The value to check
587 * @param int $mask_length The length of the mask
588 *
589 * @return bool
590 * @since 2.2
591 */
592 function is_masked_value( $value, $mask_length = 3 ) {
593 // Get the last characters of the string
594 $last_chars = substr( $value, - $mask_length );
595
596 // Check if the last characters are asterisks
597 return str_repeat( '*', $mask_length ) === $last_chars;
598 }
599
600 /**
601 * Get email placeholders by user
602 *
603 * @param \WP_User $user User object
604 * @param string $login_link Login link (@since 2.4.2)
605 *
606 * @return array
607 * @since 2.2
608 */
609 function get_email_placeholders_by_user( $user, $login_link ) {
610 global $magic_login_token;
611
612 if ( is_multisite() ) {
613 $site_name = get_network()->site_name;
614 } else {
615 $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
616 }
617
618 $settings = \MagicLogin\Utils\get_settings();
619 $ttl = get_ttl_by_user( $user->ID );
620
621 list( $token_ttl, $selected_interval ) = get_ttl_with_interval( $ttl );
622 $selected_interval_str = strtolower( $selected_interval );
623 $allowed_intervals = get_allowed_intervals();
624
625 if ( isset( $allowed_intervals[ $selected_interval ] ) ) {
626 $selected_interval_str = strtolower( $allowed_intervals[ $selected_interval ] ); // translated interval
627 }
628
629 $placeholders = [
630 '{{SITEURL}}' => home_url(),
631 '{{USERNAME}}' => $user->user_login,
632 '{{FIRST_NAME}}' => $user->first_name,
633 '{{LAST_NAME}}' => $user->last_name,
634 '{{FULL_NAME}}' => $user->first_name . ' ' . $user->last_name,
635 '{{DISPLAY_NAME}}' => $user->display_name,
636 '{{USER_EMAIL}}' => $user->user_email,
637 '{{SITENAME}}' => $site_name,
638 '{{EXPIRES}}' => $ttl,
639 '{{EXPIRES_WITH_INTERVAL}}' => $token_ttl . ' ' . $selected_interval_str,
640 '{{TOKEN_VALIDITY_COUNT}}' => get_token_validity_by_user( $user->ID ),
641 '{{MAGIC_LINK}}' => $login_link,
642 '{{MAGIC_LOGIN_CODE}}' => $magic_login_token,
643 ];
644
645 /**
646 * Filter the email placeholders
647 *
648 * @hook magic_login_email_placeholders
649 *
650 * @param {array} $placeholders Placeholders
651 *
652 * @return {array} New value
653 * @since 2.4.2
654 */
655 return apply_filters( 'magic_login_email_placeholders', $placeholders, $user->ID );
656 }
657
658 /**
659 * Get decrypted value
660 *
661 * @param string $value encrypted value
662 *
663 * @return bool|mixed|string
664 * @since 2.2
665 */
666 function get_decrypted_value( $value ) {
667 $encryption = new Encryption();
668 $decrypted_value = $encryption->decrypt( $value );
669
670 if ( false !== $decrypted_value ) {
671 return $decrypted_value;
672 }
673
674 return $value;
675 }
676
677 /**
678 * Get the token TTL by user
679 *
680 * @param int $user_id User ID
681 *
682 * @return int TTL in minutes
683 * @since 2.2
684 */
685 function get_ttl_by_user( $user_id ) {
686 $settings = \MagicLogin\Utils\get_settings();
687 $ttl = $settings['token_ttl'];
688
689 /**
690 * Filter the token TTL by user
691 *
692 * @hook magic_login_token_ttl_by_user
693 *
694 * @param {int} $ttl TTL in minutes
695 * @param {int} $user_id User ID
696 *
697 * @return {int} New value
698 * @since 2.2
699 */
700 return apply_filters( 'magic_login_token_ttl_by_user', $ttl, $user_id );
701 }
702
703
704 /**
705 * Default registration email message
706 *
707 * @return mixed|string|void
708 * @since 2.2
709 */
710 function get_default_registration_email_text() {
711 $email_text = __(
712 'Hi there,
713 <br><br>
714 Thank you for signing up to {{SITENAME}}! We are excited to have you on board.
715 <br>
716 To get started, simply use the magic link below to log in:
717 <br><br>
718 <a href="{{MAGIC_LINK}}" target="_blank" rel="noreferrer noopener">Click here to log in</a>
719 <br><br>
720 If the button above does not work, you can also copy and paste the following URL into your browser:
721 <br>
722 {{MAGIC_LINK}}
723 <br><br>
724 We hope you enjoy your experience with us. If you have any questions or need assistance, feel free to reach out.
725 <br><br>
726 Regards,<br>
727 All at {{SITENAME}}<br>
728 {{SITEURL}}',
729 'magic-login'
730 );
731
732 return $email_text;
733 }
734
735
736 /**
737 * Get user by log input
738 *
739 * @param string $input Input. It can be username, email or phone number
740 *
741 * @return false|mixed|\WP_User|null
742 * @since 2.4
743 */
744 function get_user_by_log_input( $input ) {
745 $user = get_user_by( 'login', $input );
746
747 if ( ! defined( 'MAGIC_LOGIN_USERNAME_ONLY' ) || false === MAGIC_LOGIN_USERNAME_ONLY ) {
748 if ( ! $user && strpos( $input, '@' ) ) {
749 $user = get_user_by( 'email', $input );
750 }
751 }
752
753 return $user;
754 }
755
756
757 /**
758 * Get the token validity by user
759 *
760 * @param int $user_id User ID
761 *
762 * @return int Validity in minutes
763 * @since 2.6
764 */
765 function get_token_validity_by_user( $user_id ) {
766 $settings = \MagicLogin\Utils\get_settings();
767 $validity = $settings['token_validity'];
768
769 // Check if there's a user-specific token validity setting which is available in pro version only
770 // yet it's still hookable for fellow devs who want to adjust programmatically
771
772 /**
773 * Filter the token validity by user
774 *
775 * @hook magic_login_token_validity_by_user
776 *
777 * @param int $validity Validity in minutes
778 * @param int $user_id User ID
779 *
780 * @return int New value
781 * @since 2.6
782 */
783 return apply_filters( 'magic_login_token_validity_by_user', $validity, $user_id );
784 }
785
786
787 /**
788 * Get required capability to access admin menu/settings page
789 *
790 * @since 2.6.2
791 * @return string
792 */
793 function get_settings_capability() {
794 $capability = MAGIC_LOGIN_IS_NETWORK ? 'manage_network_options' : 'manage_options';
795
796 /**
797 * Filter the capability required to access the Magic Login settings page.
798 *
799 * @hook magic_login_admin_menu_cap
800 *
801 * @param string $capability The capability required to access the settings page.
802 *
803 * @return string Altered value.
804 * @deprecated 2.6.2 Use 'magic_login_settings_capability' instead.
805 */
806 $capability = apply_filters( 'magic_login_admin_menu_cap', $capability );
807
808 /**
809 * Filter the capability required to access the Magic Login settings page.
810 *
811 * @hook magic_login_settings_capability
812 *
813 * @param string $capability The capability required to access the settings page.
814 *
815 * @return string Altered value.
816 * @since 2.6.2
817 */
818 return apply_filters( 'magic_login_settings_capability', $capability );
819 }
820