- updated hookdocs;

- reviewed `um_safe_redirect()`;
- updated readme.txt
This commit is contained in:
Mykyta Synelnikov
2023-07-18 12:06:17 +03:00
parent 6231c5cb78
commit ce4cf8fe7b
14 changed files with 182 additions and 117 deletions
+2 -2
View File
@@ -298,8 +298,8 @@ if ( ! class_exists( 'um\admin\Secure' ) ) {
array(
'id' => 'secure_allowed_redirect_hosts',
'type' => 'textarea',
'label' => __( 'Allowed hosts for redirect (one host per line)', 'ultimate-member' ),
'description' => __( 'Extend allowed hosts for redirects', 'ultimate-member' ),
'label' => __( 'Allowed hosts for safe redirect (one host per line)', 'ultimate-member' ),
'description' => __( 'Extend allowed hosts for frontend pages redirects', 'ultimate-member' ),
),
)
);
@@ -962,6 +962,9 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) {
'secure_notify_admins_banned_accounts__interval' => array(
'sanitize' => 'key',
),
'secure_allowed_redirect_hosts' => array(
'sanitize' => 'textarea',
),
)
);
+1
View File
@@ -589,6 +589,7 @@ if ( ! class_exists( 'um\Config' ) ) {
'banned_capabilities' => array( 'manage_options', 'promote_users', 'level_10' ),
'secure_notify_admins_banned_accounts' => false,
'secure_notify_admins_banned_accounts__interval' => 'instant',
'secure_allowed_redirect_hosts' => '',
);
add_filter( 'um_get_tabs_from_config', '__return_true' );
+16 -1
View File
@@ -61,7 +61,22 @@ if ( ! class_exists( 'um\core\Login' ) ) {
}
if ( empty( $args['_wpnonce'] ) || ! wp_verify_nonce( $args['_wpnonce'], 'um_login_form' ) ) {
// @todo add hookdocs
/**
* Filters URL for redirect if login form nonce isn't verified.
*
* @param {string} $error_url URL for redirect if login form nonce isn't verified.
*
* @return {string} URL for redirect.
*
* @since 2.0
* @hook um_login_invalid_nonce_redirect_url
*
* @example <caption>Change URL for redirect if login form nonce isn't verified.</caption>
* function my_um_login_invalid_nonce_redirect_url( $error_url ) {
* return '{your_custom_url}';
* }
* add_filter( 'um_login_invalid_nonce_redirect_url', 'my_um_login_invalid_nonce_redirect_url' );
*/
$url = apply_filters( 'um_login_invalid_nonce_redirect_url', add_query_arg( array( 'err' => 'invalid_nonce' ) ) );
um_safe_redirect( $url );
exit;
+17 -20
View File
@@ -79,29 +79,25 @@ if ( ! class_exists( 'um\core\Logout' ) ) {
wp_destroy_current_session();
wp_logout();
session_unset();
exit( wp_safe_redirect( home_url() ) );
wp_safe_redirect( home_url() );
exit;
} else {
/**
* UM hook
* Filters URL for redirect after logout.
*
* @type filter
* @title um_logout_redirect_url
* @description Change redirect URL after logout
* @input_vars
* [{"var":"$url","type":"string","desc":"Redirect URL"},
* {"var":"$id","type":"int","desc":"User ID"}]
* @change_log
* ["Since: 2.0"]
* @usage
* <?php add_filter( 'um_logout_redirect_url', 'function_name', 10, 2 ); ?>
* @example
* <?php
* add_filter( 'um_logout_redirect_url', 'my_logout_redirect_url', 10, 2 );
* function my_logout_redirect_url( $url, $id ) {
* // your code here
* return $url;
* @param {string} $logout_redirect_url URL for redirect after logout.
* @param {int} $user_id User ID who logged out.
*
* @return {string} Redirect URL.
*
* @since 2.0
* @hook um_logout_redirect_url
*
* @example <caption>Change URL for redirect after logout.</caption>
* function my_logout_redirect_url( $logout_redirect_url, $user_id ) {
* return '{your_custom_url}';
* }
* ?>
* add_filter( 'um_logout_redirect_url', 'my_logout_redirect_url', 10, 2 );
*/
$redirect_url = apply_filters( 'um_logout_redirect_url', um_user( 'logout_redirect_url' ), um_user( 'ID' ) );
wp_destroy_current_session();
@@ -111,7 +107,8 @@ if ( ! class_exists( 'um\core\Logout' ) ) {
}
} else {
add_filter( 'wp_safe_redirect_fallback', array( &$this, 'safe_redirect_default' ), 10, 2 );
exit( wp_safe_redirect( home_url() ) );
wp_safe_redirect( home_url() );
exit;
}
}
+1 -1
View File
@@ -239,7 +239,7 @@ if ( ! class_exists( 'um\core\Password' ) ) {
if ( isset( $_GET['hash'] ) && isset( $_GET['login'] ) ) {
$value = sprintf( '%s:%s', wp_unslash( $_GET['login'] ), wp_unslash( $_GET['hash'] ) );
$this->setcookie( $rp_cookie, $value );
// Not `um_safe_redirect()` because password-reset page is predefined page and is situated on the same host.
wp_safe_redirect( remove_query_arg( array( 'hash', 'login' ) ) );
exit;
}
+16 -1
View File
@@ -55,7 +55,22 @@ if ( ! class_exists( 'um\core\Register' ) ) {
}
if ( empty( $args['_wpnonce'] ) || ! wp_verify_nonce( $args['_wpnonce'], 'um_register_form' ) ) {
// @todo add hookdocs
/**
* Filters URL for redirect if register form nonce isn't verified.
*
* @param {string} $error_url URL for redirect if register form nonce isn't verified.
*
* @return {string} URL for redirect.
*
* @since 2.0
* @hook um_register_invalid_nonce_redirect_url
*
* @example <caption>Change URL for redirect if register form nonce isn't verified.</caption>
* function my_um_register_invalid_nonce_redirect_url( $error_url ) {
* return '{your_custom_url}';
* }
* add_filter( 'um_register_invalid_nonce_redirect_url', 'my_um_register_invalid_nonce_redirect_url' );
*/
$url = apply_filters( 'um_register_invalid_nonce_redirect_url', add_query_arg( array( 'err' => 'invalid_nonce' ) ) );
um_safe_redirect( $url );
exit;
+6
View File
@@ -140,11 +140,13 @@ function um_submit_form_errors_hook_logincheck( $submitted_data, $form_data ) {
case 'awaiting_email_confirmation':
case 'rejected':
um_reset_user();
// Not `um_safe_redirect()` because UM()->permalinks()->get_current_url() is situated on the same host.
wp_safe_redirect( add_query_arg( 'err', esc_attr( $status ), UM()->permalinks()->get_current_url() ) );
exit;
}
if ( isset( $form_data['form_id'] ) && absint( $form_data['form_id'] ) === absint( UM()->shortcodes()->core_login_form() ) && UM()->form()->errors && ! isset( $_POST[ UM()->honeypot ] ) ) {
// Not `um_safe_redirect()` because predefined login page is situated on the same host.
wp_safe_redirect( um_get_core_page( 'login' ) );
exit;
}
@@ -224,12 +226,14 @@ function um_user_login( $submitted_data ) {
// Role redirect
$after_login = um_user( 'after_login' );
if ( empty( $after_login ) ) {
// Not `um_safe_redirect()` because predefined user profile page is situated on the same host.
wp_safe_redirect( um_user_profile_url() );
exit;
}
switch ( $after_login ) {
case 'redirect_admin':
// Not `um_safe_redirect()` because is redirected to wp-admin.
wp_safe_redirect( admin_url() );
exit;
case 'redirect_url':
@@ -255,10 +259,12 @@ function um_user_login( $submitted_data ) {
um_safe_redirect( $redirect_url );
exit;
case 'refresh':
// Not `um_safe_redirect()` because UM()->permalinks()->get_current_url() is situated on the same host.
wp_safe_redirect( UM()->permalinks()->get_current_url() );
exit;
case 'redirect_profile':
default:
// Not `um_safe_redirect()` because predefined user profile page is situated on the same host.
wp_safe_redirect( um_user_profile_url() );
exit;
}
+1
View File
@@ -508,6 +508,7 @@ function um_user_edit_profile( $args, $form_data ) {
// Finally redirect to profile.
$url = um_user_profile_url( $user_id );
$url = apply_filters( 'um_update_profile_redirect_after', $url, $user_id, $args );
// Not `um_safe_redirect()` because predefined user profile page is situated on the same host.
wp_safe_redirect( um_edit_my_profile_cancel_uri( $url ) );
exit;
}
+80 -53
View File
@@ -146,32 +146,70 @@ add_action( 'um_registration_complete', 'um_send_registration_notification' );
function um_check_user_status( $user_id, $args, $form_data = null ) {
$status = um_user( 'account_status' );
/**
* UM hook
* Fires after complete UM user registration.
* Where $status can be equal to 'approved', 'checkmail' or 'pending'.
*
* @type action
* @title um_post_registration_{$status}_hook
* @description After complete UM user registration.
* @input_vars
* [{"var":"$user_id","type":"int","desc":"User ID"},
* {"var":"$args","type":"array","desc":"Form data"}]
* @change_log
* ["Since: 2.0"]
* @usage add_action( 'um_post_registration_{$status}_hook', 'function_name', 10, 2 );
* @example
* <?php
* add_action( 'um_post_registration_{$status}_hook', 'my_post_registration', 10, 2 );
* function my_post_registration( $user_id, $args ) {
* @since 1.3.x
* @since 2.6.8 Added $form_data argument.
*
* @hook um_post_registration_{$status}_hook
*
* @param {int} $user_id User ID.
* @param {array} $submitted_data Registration form submitted data.
* @param {array} $form_data Form data. Since 2.6.8
*
* @example <caption>Make a custom action after complete UM user registration when user get an approved status.</caption>
* function my_um_post_registration( $user_id, $submitted_data, $form_data ) {
* // your code here
* }
* ?>
* add_action( 'um_post_registration_approved_hook', 'my_um_post_registration', 10, 3 );
* @example <caption>Make a custom action after complete UM user registration when user requires email activation.</caption>
* function my_um_post_registration( $user_id, $submitted_data, $form_data ) {
* // your code here
* }
* add_action( 'um_post_registration_checkmail_hook', 'my_um_post_registration', 10, 3 );
* @example <caption>Make a custom action after complete UM user registration when user requires admin review.</caption>
* function my_um_post_registration( $user_id, $submitted_data, $form_data ) {
* // your code here
* }
* add_action( 'um_post_registration_pending_hook', 'my_um_post_registration', 10, 3 );
*/
do_action( "um_post_registration_{$status}_hook", $user_id, $args );
do_action( "um_post_registration_{$status}_hook", $user_id, $args, $form_data );
if ( is_null( $form_data ) || is_admin() ) {
return;
}
do_action( "track_{$status}_user_registration" );
/**
* Fires after complete UM user registration. Only for the frontend action which is run before autologin and redirects.
* Where $status can be equal to 'approved', 'checkmail' or 'pending'.
*
* @since 1.3.x
* @since 2.6.8 Added $user_id, $submitted_data, $form_data arguments.
*
* @hook track_{$status}_user_registration
*
* @param {int} $user_id User ID. Since 2.6.8
* @param {array} $submitted_data Registration form submitted data. Since 2.6.8
* @param {array} $form_data Form data. Since 2.6.8
*
* @example <caption>Make a custom action after complete UM user registration when user get an approved status.</caption>
* function my_um_post_registration( $user_id, $submitted_data, $form_data ) {
* // your code here
* }
* add_action( 'track_approved_user_registration', 'my_um_post_registration', 10, 3 );
* @example <caption>Make a custom action after complete UM user registration when user requires email activation.</caption>
* function my_um_post_registration( $user_id, $submitted_data, $form_data ) {
* // your code here
* }
* add_action( 'track_checkmail_user_registration', 'my_um_post_registration', 10, 3 );
* @example <caption>Make a custom action after complete UM user registration when user requires admin review.</caption>
* function my_um_post_registration( $user_id, $submitted_data, $form_data ) {
* // your code here
* }
* add_action( 'track_pending_user_registration', 'my_um_post_registration', 10, 3 );
*/
do_action( "track_{$status}_user_registration", $user_id, $args, $form_data );
if ( 'approved' === $status ) {
// Check if user is logged in because there can be the customized way when through 'um_registration_for_loggedin_users' hook the registration is enabled for the logged-in users (e.g. Administrator).
@@ -182,72 +220,60 @@ function um_check_user_status( $user_id, $args, $form_data = null ) {
UM()->user()->generate_profile_slug( $user_id );
/**
* UM hook
* Fires after complete UM user registration and autologin.
*
* @type action
* @title um_registration_after_auto_login
* @description After complete UM user registration and autologin.
* @input_vars
* [{"var":"$user_id","type":"int","desc":"User ID"}]
* @change_log
* ["Since: 2.0"]
* @usage add_action( 'um_registration_after_auto_login', 'function_name', 10, 1 );
* @example
* <?php
* add_action( 'um_registration_after_auto_login', 'my_registration_after_auto_login', 10, 1 );
* function my_registration_after_auto_login( $user_id ) {
* @since 1.3.65
* @hook um_registration_after_auto_login
*
* @param {int} $user_id User ID.
*
* @example <caption>Make a custom action after complete UM user registration and autologin.</caption>
* function my_um_registration_after_auto_login( $user_id ) {
* // your code here
* }
* ?>
* add_action( 'um_registration_after_auto_login', 'my_um_registration_after_auto_login' );
*/
do_action( 'um_registration_after_auto_login', $user_id );
// Priority redirect
if ( isset( $args['redirect_to'] ) ) {
um_safe_redirect( urldecode( $args['redirect_to'] ) );
exit;
}
um_fetch_user( $user_id );
if ( 'redirect_url' === um_user( 'auto_approve_act' ) && '' !== um_user( 'auto_approve_url' ) ) {
um_safe_redirect( um_user( 'auto_approve_url' ));
exit;
um_safe_redirect( um_user( 'auto_approve_url' ) );
}
if ( 'redirect_profile' === um_user( 'auto_approve_act' ) ) {
// Not `um_safe_redirect()` because predefined user profile page is situated on the same host.
wp_safe_redirect( um_user_profile_url() );
exit;
}
} else {
if ( 'redirect_url' === um_user( $status . '_action' ) && '' !== um_user( $status . '_url' ) ) {
/**
* UM hook
* Filters the redirect URL for pending user after registration.
*
* @type filter
* @title um_registration_pending_user_redirect
* @description Change redirect URL for pending user after registration
* @input_vars
* [{"var":"$url","type":"string","desc":"Redirect URL"},
* {"var":"$status","type":"string","desc":"User status"},
* {"var":"$user_id","type":"int","desc":"User ID"}]
* @change_log
* ["Since: 2.0"]
* @usage
* <?php add_filter( 'um_registration_pending_user_redirect', 'function_name', 10, 3 ); ?>
* @example
* <?php
* add_filter( 'um_registration_pending_user_redirect', 'my_registration_pending_user_redirect', 10, 3 );
* @since 2.0
* @hook um_registration_pending_user_redirect
*
* @param {string} $url Redirect URL.
* @param {string} $status User status.
* @param {int} $user_id User ID.
*
* @return {string} Redirect URL.
*
* @example <caption>Change redirect URL for pending user after registration.</caption>
* function my_registration_pending_user_redirect( $url, $status, $user_id ) {
* // your code here
* return $url;
* }
* ?>
* add_filter( 'um_registration_pending_user_redirect', 'my_registration_pending_user_redirect', 10, 3 );
*/
$redirect_url = apply_filters( 'um_registration_pending_user_redirect', um_user( $status . '_url' ), $status, um_user( 'ID' ) );
um_safe_redirect( $redirect_url );
exit;
}
if ( 'show_message' === um_user( $status . '_action' ) && '' !== um_user( $status . '_message' ) ) {
@@ -256,7 +282,7 @@ function um_check_user_status( $user_id, $args, $form_data = null ) {
// Add only priority role to URL.
$url = add_query_arg( 'um_role', esc_attr( um_user( 'role' ) ), $url );
$url = add_query_arg( 'um_form_id', esc_attr( $form_data['form_id'] ), $url );
// Not `um_safe_redirect()` because UM()->permalinks()->get_current_url() is situated on the same host.
wp_safe_redirect( $url );
exit;
}
@@ -714,6 +740,7 @@ function um_form_register_redirect() {
$page_id = UM()->options()->get( UM()->options()->get_core_page_id( 'register' ) );
$register_post = get_post( $page_id );
if ( ! empty( $register_post ) ) {
// Not `um_safe_redirect()` because predefined register page is situated on the same host.
wp_safe_redirect( get_permalink( $page_id ) );
exit();
}
+3
View File
@@ -129,6 +129,7 @@ if ( ! class_exists( 'um\frontend\Secure' ) ) {
if ( UM()->options()->get( 'lock_register_forms' ) ) {
$login_url = add_query_arg( 'notice', 'maintenance', um_get_core_page( 'login' ) );
nocache_headers();
// Not `um_safe_redirect()` because predefined login page is situated on the same host.
wp_safe_redirect( $login_url );
exit;
}
@@ -144,6 +145,7 @@ if ( ! class_exists( 'um\frontend\Secure' ) ) {
$expired_password_reset = get_user_meta( um_user( 'ID' ), 'um_secure_has_reset_password', true );
if ( ! $expired_password_reset ) {
$login_url = add_query_arg( 'notice', 'expired_password', um_get_core_page( 'login' ) );
// Not `um_safe_redirect()` because predefined login page is situated on the same host.
wp_safe_redirect( $login_url );
exit;
}
@@ -241,6 +243,7 @@ if ( ! class_exists( 'um\frontend\Secure' ) ) {
$redirect = apply_filters( 'um_secure_blocked_user_redirect_immediately', true );
if ( $redirect ) {
$login_url = add_query_arg( 'err', 'inactive', um_get_core_page( 'login' ) );
// Not `um_safe_redirect()` because predefined login page is situated on the same host.
wp_safe_redirect( $login_url );
exit;
}
+33 -37
View File
@@ -2843,16 +2843,15 @@ function um_is_amp( $check_theme_support = true ) {
}
/**
* UM safe redirect
* UM safe redirect. By default, you can be redirected only to WordPress installation Home URL. Fallback URL is wp-admin URL.
* But it can be changed through filters and extended by UM Setting "Allowed hosts for safe redirect (one host per line)" and filter `um_wp_safe_redirect_fallback`.
*
* @since 2.6.9
* @since 2.6.8
*
* @param string $url redirect URL.
*
* @return string
*/
function um_safe_redirect( $url ) {
add_filter( 'allowed_redirect_hosts', 'um_allowed_redirect_hosts', 10, 1 );
add_filter( 'allowed_redirect_hosts', 'um_allowed_redirect_hosts' );
add_filter( 'wp_safe_redirect_fallback', 'um_wp_safe_redirect_fallback', 10, 2 );
wp_safe_redirect( $url );
@@ -2862,21 +2861,19 @@ function um_safe_redirect( $url ) {
/**
* UM allowed hosts
*
* @since 2.6.9
* @since 2.6.8
*
* @param array $hosts allowed hosts.
* @param array $hosts Allowed hosts.
*
* @return array
*/
function um_allowed_redirect_hosts( $hosts ) {
$hosts = UM()->options()->get( 'secure_allowed_redirect_hosts' );
$hosts = explode( "\n", $hosts );
$hosts = array_unique( $hosts );
$secure_hosts = UM()->options()->get( 'secure_allowed_redirect_hosts' );
$secure_hosts = explode( "\n", $secure_hosts );
$secure_hosts = array_unique( $secure_hosts );
$additional_hosts = array();
foreach ( $hosts as $key => $host ) {
foreach ( $secure_hosts as $host ) {
if ( '' !== trim( $host ) ) {
$host = trim( $host );
$host = str_replace( array( 'http://', 'https://' ), '', $host );
@@ -2887,26 +2884,28 @@ function um_allowed_redirect_hosts( $hosts ) {
}
if ( strpos( $host, 'www.' ) !== false ) {
if ( ! in_array( str_replace( array( 'www.' ), '', $host ), $additional_hosts, true ) ) {
$additional_hosts[] = str_replace( array( 'www.' ), '', $host );
$strip_www = str_replace( 'www.', '', $host );
if ( ! in_array( $strip_www, $additional_hosts, true ) ) {
$additional_hosts[] = $strip_www;
}
} else {
if ( ! in_array( 'www.' . $host, $additional_hosts, true ) ) {
$additional_hosts[] = 'www.' . $host;
$added_www = 'www.' . $host;
if ( ! in_array( $added_www, $additional_hosts, true ) ) {
$additional_hosts[] = $added_www;
}
}
}
}
/**
* Filters change allowed hosts.
* Filters change allowed hosts. When `wp_safe_redirect()` function is used for the Ultimate Member frontend redirects.
*
* @since 2.6.9
* @since 2.6.8
* @hook um_allowed_redirect_hosts
*
* @param {array} $additional_hosts allowed hosts.
* @param {array} $hosts default hosts.
* @param {array} $additional_hosts Allowed hosts.
* @param {array} $hosts Default hosts.
*
* @return {array} allowed hosts.
* @return {array} Allowed hosts.
*
* @example <caption>Change allowed hosts.</caption>
* function my_um_allowed_redirect_hosts( $additional_hosts, $hosts ) {
@@ -2916,33 +2915,32 @@ function um_allowed_redirect_hosts( $hosts ) {
* add_filter( 'um_allowed_redirect_hosts', 'my_um_allowed_redirect_hosts', 10, 2 );
*/
$additional_hosts = apply_filters( 'um_allowed_redirect_hosts', $additional_hosts, $hosts );
$allowed_hosts = array_merge( $hosts, $additional_hosts );
return $allowed_hosts;
return array_merge( $hosts, $additional_hosts );
}
/**
* UM fallback redirect URL
*
* @since 2.6.9
* @since 2.6.8
*
* @param string $url fallback URL.
* @param string $status redirect status.
* @param string $url Fallback URL.
* @param string $status Redirect status.
*
* @return string
*/
function um_wp_safe_redirect_fallback( $url, $status ) {
/**
* Filters change fallback URL.
* Filters change fallback URL. When `wp_safe_redirect()` function is used for the Ultimate Member frontend redirects.
* It's `home_url()` by default.
*
* @since 2.6.9
* @since 2.6.8
* @hook um_wp_safe_redirect_fallback
*
* @param {string} $url fallback URL.
* @param {string} $status status.
* @param {string} $url UM Fallback URL.
* @param {string} $default_fallback Default fallback URL.
* @param {string} $status Redirect status.
*
* @return {string} fallback URL.
* @return {string} Fallback URL.
*
* @example <caption>Change fallback URL.</caption>
* function my_um_wp_safe_redirect_fallback( $url, $status ) {
@@ -2951,7 +2949,5 @@ function um_wp_safe_redirect_fallback( $url, $status ) {
* }
* add_filter( 'um_wp_safe_redirect_fallback', 'my_um_wp_safe_redirect_fallback', 10, 2 );
*/
$url = apply_filters( 'um_wp_safe_redirect_fallback', home_url( '/' ), $status );
return $url;
return apply_filters( 'um_wp_safe_redirect_fallback', home_url( '/' ), $url, $status );
}
+2 -1
View File
@@ -166,7 +166,7 @@ No specific extensions are needed. But we highly recommended keep active these P
IMPORTANT: PLEASE UPDATE THE PLUGIN TO AT LEAST VERSION 2.6.7 IMMEDIATELY. VERSION 2.6.7 PATCHES SECURITY PRIVILEGE ESCALATION VULNERABILITY. PLEASE SEE [THIS ARTICLE](https://docs.ultimatemember.com/article/1866-security-incident-update-and-recommended-actions) FOR MORE INFORMATION
= 2.6.8: July 14, 2023 =
= 2.6.8: July 19, 2023 =
* Enhancements:
@@ -175,6 +175,7 @@ IMPORTANT: PLEASE UPDATE THE PLUGIN TO AT LEAST VERSION 2.6.7 IMMEDIATELY. VERSI
- Added: `um_edit_profile_url` hook for force changing user profile edit URL
- Added: Additional hook attributes to 'um_reset_password_errors_hook' and 'um_reset_password_process_hook'
- Added: $form_data attribute to 'um_before_save_registration_details' hook
- Added: `um_safe_redirect()` function for handle `wp_safe_redirect()` function with new the "Allowed hosts for safe redirect" setting
- Updated: [Hooks Documentation v2](https://ultimatemember.github.io/ultimatemember/hooks/)
* Bugfixes:
+1 -1
View File
@@ -3,7 +3,7 @@
Plugin Name: Ultimate Member
Plugin URI: http://ultimatemember.com/
Description: The easiest way to create powerful online communities and beautiful user profiles with WordPress
Version: 2.6.8-rc.1
Version: 2.6.8
Author: Ultimate Member
Author URI: http://ultimatemember.com/
Text Domain: ultimate-member