mirror of
https://github.com/10h30/ultimatemember.git
synced 2026-06-05 15:09:37 +09:00
Merge branch 'development/2.9.x' into fix/CVE-2025-13220
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
* Bugfixes:
|
||||
|
||||
- Fixed: CVE-2025-13220.
|
||||
- Fixed: CVE-2025-13217.
|
||||
- Fixed: CVE-2025-14081.
|
||||
|
||||
= 2.11.0 December 02, 2025 =
|
||||
|
||||
|
||||
@@ -284,6 +284,12 @@ if ( ! class_exists( 'um\admin\Admin' ) ) {
|
||||
'_um_mode' => array(
|
||||
'sanitize' => 'key',
|
||||
),
|
||||
'_um_privacy' => array(
|
||||
'sanitize' => 'absint',
|
||||
),
|
||||
'_um_privacy_roles' => array(
|
||||
'sanitize' => array( $this, 'sanitize_existed_role' ),
|
||||
),
|
||||
'_um_view_types' => array(
|
||||
'sanitize' => array( $this, 'sanitize_md_view_types' ),
|
||||
),
|
||||
|
||||
@@ -209,6 +209,13 @@ if ( ! class_exists( 'um\admin\Secure' ) ) {
|
||||
$scanner_content .= '</span>';
|
||||
|
||||
$secure_fields = array(
|
||||
array(
|
||||
'id' => 'ajax_nopriv_rate_limit',
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Rate Limit', 'ultimate-member' ),
|
||||
'checkbox_label' => __( 'Enable Rate Limiting', 'ultimate-member' ),
|
||||
'description' => __( 'This prevents brute-force enumeration attempts in guest AJAX requests.', 'ultimate-member' ),
|
||||
),
|
||||
array(
|
||||
'id' => 'banned_capabilities',
|
||||
'type' => 'multi_checkbox',
|
||||
|
||||
@@ -728,7 +728,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Metabox' ) ) {
|
||||
* @param $object
|
||||
* @param $box
|
||||
*/
|
||||
function load_metabox_directory( $object, $box ) {
|
||||
public function load_metabox_directory( $object, $box ) {
|
||||
$box['id'] = str_replace( 'um-admin-form-', '', $box['id'] );
|
||||
|
||||
preg_match('#\{.*?\}#s', $box['id'], $matches );
|
||||
@@ -857,6 +857,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Metabox' ) ) {
|
||||
public function add_metabox_directory() {
|
||||
add_meta_box( 'submitdiv', __( 'Publish', 'ultimate-member' ), array( &$this, 'custom_submitdiv' ), 'um_directory', 'side', 'high' );
|
||||
add_meta_box( 'um-admin-form-general', __( 'General Options', 'ultimate-member' ), array( &$this, 'load_metabox_directory' ), 'um_directory', 'normal', 'default' );
|
||||
add_meta_box( 'um-admin-form-privacy', __( 'Privacy Options', 'ultimate-member' ), array( &$this, 'load_metabox_directory' ), 'um_directory', 'normal', 'default' );
|
||||
add_meta_box( 'um-admin-form-sorting', __( 'Sorting', 'ultimate-member' ), array( &$this, 'load_metabox_directory' ), 'um_directory', 'normal', 'default' );
|
||||
add_meta_box( 'um-admin-form-profile', __( 'Profile Card', 'ultimate-member' ), array( &$this, 'load_metabox_directory' ), 'um_directory', 'normal', 'default' );
|
||||
add_meta_box( 'um-admin-form-search', __( 'Search Options', 'ultimate-member' ), array( &$this, 'load_metabox_directory' ), 'um_directory', 'normal', 'default' );
|
||||
|
||||
@@ -1032,6 +1032,9 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) {
|
||||
'display_login_form_notice' => array(
|
||||
'sanitize' => 'bool',
|
||||
),
|
||||
'ajax_nopriv_rate_limit' => array(
|
||||
'sanitize' => 'bool',
|
||||
),
|
||||
'banned_capabilities' => array(
|
||||
'sanitize' => array( UM()->admin(), 'sanitize_wp_capabilities_assoc' ),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
array(
|
||||
'id' => '_um_privacy',
|
||||
'type' => 'select',
|
||||
'label' => __( 'Who can see this member directory', 'ultimate-member' ),
|
||||
'description' => __( 'Select which users can view this member directory. Minimum recommended visibility is `Members only`. Please pay attention that visible for guests (anyone) member directory can have sensitive information.', 'ultimate-member' ),
|
||||
'options' => array(
|
||||
0 => __( 'Anyone', 'ultimate-member' ),
|
||||
1 => __( 'Guests only', 'ultimate-member' ),
|
||||
2 => __( 'Members only', 'ultimate-member' ),
|
||||
3 => __( 'Only specific roles', 'ultimate-member' ),
|
||||
),
|
||||
'value' => UM()->query()->get_meta_value( '_um_privacy', null, 2 ),
|
||||
),
|
||||
array(
|
||||
'id' => '_um_privacy_roles',
|
||||
'type' => 'select',
|
||||
'multi' => true,
|
||||
'label' => __( 'Allowed roles', 'ultimate-member' ),
|
||||
'description' => __( 'Select the the user roles allowed to view this member directory.', 'ultimate-member' ),
|
||||
'options' => UM()->roles()->get_roles(),
|
||||
'placeholder' => __( 'Choose user roles...', 'ultimate-member' ),
|
||||
'conditional' => array( '_um_privacy', '=', '3' ),
|
||||
'value' => UM()->query()->get_meta_value( '_um_privacy_roles', null, 'na' ),
|
||||
),
|
||||
);
|
||||
|
||||
$fields = apply_filters( 'um_admin_extend_directory_options_privacy', $fields );
|
||||
?>
|
||||
|
||||
<div class="um-admin-metabox">
|
||||
<?php
|
||||
UM()->admin_forms(
|
||||
array(
|
||||
'class' => 'um-member-directory-privacy um-half-column',
|
||||
'prefix_id' => 'um_metadata',
|
||||
'fields' => $fields,
|
||||
)
|
||||
)->render_form();
|
||||
?>
|
||||
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
@@ -141,6 +141,8 @@ if ( ! class_exists( 'um\Config' ) ) {
|
||||
'_um_core' => 'members',
|
||||
'_um_template' => 'members',
|
||||
'_um_mode' => 'directory',
|
||||
'_um_privacy' => 2,
|
||||
'_um_privacy_roles' => array(),
|
||||
'_um_view_types' => array( 'grid' ),
|
||||
'_um_default_view' => 'grid',
|
||||
'_um_roles' => array(),
|
||||
@@ -729,6 +731,7 @@ if ( ! class_exists( 'um\Config' ) ) {
|
||||
'lock_register_forms' => false,
|
||||
'display_login_form_notice' => false,
|
||||
'secure_ban_admins_accounts' => false,
|
||||
'ajax_nopriv_rate_limit' => true,
|
||||
'banned_capabilities' => array( 'manage_options', 'promote_users', 'level_10' ),
|
||||
'secure_notify_admins_banned_accounts' => false,
|
||||
'secure_notify_admins_banned_accounts__interval' => 'instant',
|
||||
|
||||
@@ -47,6 +47,37 @@ if ( ! class_exists( 'UM_Functions' ) ) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user rate limit has been reached based on the provided context
|
||||
*
|
||||
* @param string $context The context for which the rate limit check is being performed
|
||||
* @param int|null $requests_num Number requests per minute. Default 10.
|
||||
*
|
||||
* @return bool True if the rate limit has been reached, false otherwise
|
||||
*/
|
||||
public function is_rate_limited( $context, $requests_num = 10 ) {
|
||||
if ( is_user_logged_in() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! UM()->options()->get( 'ajax_nopriv_rate_limit' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$transient_name = 'um_ajax_nopriv_rate_limit_' . md5( $context . '_' . $_SERVER['REMOTE_ADDR'] );
|
||||
$rate_limit = get_transient( $transient_name );
|
||||
$requests_num = apply_filters( 'um_ajax_rate_limit_requests_num', $requests_num, $context ); // 10 requests per minute per IP
|
||||
|
||||
if ( false === $rate_limit ) {
|
||||
set_transient( $transient_name, 1, MINUTE_IN_SECONDS );
|
||||
} elseif ( $rate_limit < $requests_num ) {
|
||||
set_transient( $transient_name, $rate_limit + 1, MINUTE_IN_SECONDS );
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* What type of request is this?
|
||||
|
||||
@@ -1375,7 +1375,6 @@ if ( ! class_exists( 'UM' ) ) {
|
||||
require_once 'core/um-actions-form.php';
|
||||
require_once 'core/um-actions-access.php';
|
||||
require_once 'core/um-actions-wpadmin.php';
|
||||
require_once 'core/um-actions-ajax.php';
|
||||
require_once 'core/um-actions-login.php';
|
||||
require_once 'core/um-actions-register.php';
|
||||
require_once 'core/um-actions-profile.php';
|
||||
|
||||
@@ -473,23 +473,27 @@ if ( ! class_exists( 'um\core\Account' ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $fields
|
||||
* @param $shortcode_args
|
||||
* @return mixed
|
||||
*/
|
||||
function filter_fields_by_attrs( $fields, $shortcode_args ) {
|
||||
private function filter_fields_by_attrs( $fields, $shortcode_args ) {
|
||||
foreach ( $fields as $k => $field ) {
|
||||
if ( isset( $shortcode_args[ $field['metakey'] ] ) && 0 == $shortcode_args[ $field['metakey'] ] ) {
|
||||
unset( $fields[ $k ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// required user permission 'required_perm' - it's field attribute predefined in the field data in code.
|
||||
if ( isset( $data['required_perm'] ) && ! UM()->roles()->um_user_can( $data['required_perm'] ) ) {
|
||||
unset( $fields[ $k ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Init displayed fields for security check
|
||||
*
|
||||
|
||||
@@ -266,6 +266,10 @@ if ( ! class_exists( 'um\core\Files' ) ) {
|
||||
public function ajax_remove_file() {
|
||||
UM()->check_ajax_nonce();
|
||||
|
||||
if ( UM()->is_rate_limited( 'remove_file' ) ) {
|
||||
wp_send_json_error( __( 'Too many requests', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
if ( empty( $_POST['src'] ) ) {
|
||||
wp_send_json_error( __( 'Wrong path', 'ultimate-member' ) );
|
||||
}
|
||||
@@ -314,6 +318,11 @@ if ( ! class_exists( 'um\core\Files' ) ) {
|
||||
*/
|
||||
public function ajax_resize_image() {
|
||||
UM()->check_ajax_nonce();
|
||||
|
||||
if ( UM()->is_rate_limited( 'resize_image' ) ) {
|
||||
wp_send_json_error( __( 'Too many requests', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification -- verified by the `check_ajax_nonce()`
|
||||
if ( ! isset( $_REQUEST['src'], $_REQUEST['coord'], $_REQUEST['key'] ) ) {
|
||||
wp_send_json_error( esc_js( __( 'Invalid parameters', 'ultimate-member' ) ) );
|
||||
@@ -447,6 +456,10 @@ if ( ! class_exists( 'um\core\Files' ) ) {
|
||||
* @throws Exception
|
||||
*/
|
||||
public function ajax_image_upload() {
|
||||
if ( UM()->is_rate_limited( 'upload_image' ) ) {
|
||||
wp_send_json_error( __( 'Too many requests', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
$ret['error'] = null;
|
||||
$ret = array();
|
||||
|
||||
@@ -645,6 +658,10 @@ if ( ! class_exists( 'um\core\Files' ) ) {
|
||||
* @throws Exception
|
||||
*/
|
||||
public function ajax_file_upload() {
|
||||
if ( UM()->is_rate_limited( 'upload_file' ) ) {
|
||||
wp_send_json_error( __( 'Too many requests', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
$ret['error'] = null;
|
||||
$ret = array();
|
||||
|
||||
|
||||
@@ -116,6 +116,10 @@ if ( ! class_exists( 'um\core\Form' ) ) {
|
||||
public function ajax_select_options() {
|
||||
UM()->check_ajax_nonce();
|
||||
|
||||
if ( UM()->is_rate_limited( 'select_options' ) ) {
|
||||
wp_send_json_error( __( 'Too many requests', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification
|
||||
|
||||
$arr_options = array();
|
||||
|
||||
@@ -595,6 +595,10 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
|
||||
public function ajax_get_members() {
|
||||
UM()->check_ajax_nonce();
|
||||
|
||||
if ( UM()->is_rate_limited( 'member_directory' ) ) {
|
||||
wp_send_json_error( __( 'Too many requests', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
@@ -609,6 +613,10 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
|
||||
}
|
||||
// phpcs:enable WordPress.Security.NonceVerification -- verified via `UM()->check_ajax_nonce();`.
|
||||
|
||||
if ( ! $this->can_view_directory( $directory_id ) ) {
|
||||
wp_send_json_error( __( 'You cannot see this member directory', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
$directory_data = UM()->query()->post_data( $directory_id );
|
||||
|
||||
// Predefined result for user without capabilities to see other members.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace um\core;
|
||||
|
||||
use Exception;
|
||||
use WP_Post;
|
||||
use WP_User_Query;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
@@ -123,11 +124,79 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
|
||||
public function __construct() {
|
||||
add_filter( 'init', array( &$this, 'init_variables' ) );
|
||||
|
||||
add_action( 'wp_insert_post', array( &$this, 'set_token' ), 10, 3 );
|
||||
add_action( 'template_redirect', array( &$this, 'access_members' ), 555 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set member directory token as soon as it's created.
|
||||
*
|
||||
* @param int $post_ID
|
||||
* @param WP_Post $post
|
||||
* @param bool $update
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_token( $post_ID, $post, $update ) {
|
||||
if ( 'um_directory' === $post->post_type && ! $update ) {
|
||||
$this->set_directory_hash( $post_ID );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user can view the member directory.
|
||||
*
|
||||
* @param int $directory_id
|
||||
* @param int|null $user_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view_directory( $directory_id, $user_id = null ) {
|
||||
if ( is_null( $user_id ) && is_user_logged_in() ) {
|
||||
$user_id = get_current_user_id();
|
||||
}
|
||||
|
||||
$can_view = false;
|
||||
$privacy = get_post_meta( $directory_id, '_um_privacy', true );
|
||||
if ( '' === $privacy ) {
|
||||
$can_view = true;
|
||||
} else {
|
||||
$privacy = absint( $privacy );
|
||||
|
||||
switch ( $privacy ) {
|
||||
case 0:
|
||||
$can_view = true;
|
||||
break;
|
||||
case 1:
|
||||
if ( ! is_user_logged_in() ) {
|
||||
$can_view = true;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if ( is_user_logged_in() ) {
|
||||
$can_view = true;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if ( is_user_logged_in() ) {
|
||||
$privacy_roles = get_post_meta( $directory_id, '_um_privacy_roles', true );
|
||||
$privacy_roles = ! empty( $privacy_roles ) && is_array( $privacy_roles ) ? $privacy_roles : array();
|
||||
|
||||
$current_user_roles = um_user( 'roles' );
|
||||
if ( ! empty( $current_user_roles ) && count( array_intersect( $current_user_roles, $privacy_roles ) ) > 0 ) {
|
||||
$can_view = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'um_directory_user_can_view', $can_view, $directory_id, $user_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WordPress core searching fields in wp_users query.
|
||||
*
|
||||
* @since 2.6.10
|
||||
* @version 2.10.2
|
||||
* @param array|null $qv WP_User_Query variables.
|
||||
@@ -202,29 +271,104 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
function get_directory_by_hash( $hash ) {
|
||||
public function get_directory_by_hash( $hash ) {
|
||||
global $wpdb;
|
||||
|
||||
$directory_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE SUBSTRING( MD5( ID ), 11, 5 ) = %s", $hash ) );
|
||||
|
||||
$directory_id = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT post_id
|
||||
FROM {$wpdb->postmeta}
|
||||
WHERE meta_key = '_um_directory_token' AND
|
||||
meta_value = %s
|
||||
LIMIT 1",
|
||||
$hash
|
||||
)
|
||||
);
|
||||
if ( empty( $directory_id ) ) {
|
||||
return false;
|
||||
// Fallback, use old value.
|
||||
$directory_id = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT ID
|
||||
FROM {$wpdb->posts}
|
||||
WHERE SUBSTRING( MD5( ID ), 11, 5 ) = %s",
|
||||
$hash
|
||||
)
|
||||
);
|
||||
if ( empty( $directory_id ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $directory_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a secure random token for each directory
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function set_directory_hash( $id ) {
|
||||
$unique_hash = wp_generate_password( 5, false );
|
||||
$result = update_post_meta( $id, '_um_directory_token', $unique_hash );
|
||||
if ( false === $result ) {
|
||||
return false;
|
||||
}
|
||||
return $unique_hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
function get_directory_hash( $id ) {
|
||||
$hash = substr( md5( $id ), 10, 5 );
|
||||
public function get_directory_hash( $id ) {
|
||||
$hash = get_post_meta( $id, '_um_directory_token', true );
|
||||
if ( '' === $hash ) {
|
||||
// Set the hash if empty.
|
||||
$hash = $this->set_directory_hash( $id );
|
||||
}
|
||||
if ( empty( $hash ) ) {
|
||||
// Fallback, use old value.
|
||||
$hash = substr( md5( $id ), 10, 5 );
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a secure random token for each user card
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function set_user_hash( $id ) {
|
||||
$unique_hash = wp_generate_password( 5, false );
|
||||
$result = update_user_meta( $id, '_um_card_anchor_token', $unique_hash );
|
||||
if ( false === $result ) {
|
||||
return false;
|
||||
}
|
||||
return $unique_hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function get_user_hash( $id ) {
|
||||
$hash = get_user_meta( $id, '_um_card_anchor_token', true );
|
||||
if ( '' === $hash ) {
|
||||
// Set the hash if empty.
|
||||
$hash = $this->set_user_hash( $id );
|
||||
}
|
||||
if ( empty( $hash ) ) {
|
||||
// Fallback, use old value.
|
||||
$hash = substr( md5( $id ), 10, 5 );
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get view Type template
|
||||
@@ -673,7 +817,7 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
|
||||
*/
|
||||
$attrs = apply_filters( 'um_search_fields', $attrs, $field_key, $directory_data['form_id'] );
|
||||
|
||||
$unique_hash = substr( md5( $directory_data['form_id'] ), 10, 5 );
|
||||
$unique_hash = $this->get_directory_hash( $directory_data['form_id'] );
|
||||
|
||||
ob_start();
|
||||
|
||||
@@ -2545,16 +2689,15 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
|
||||
$hook_after_user_name = ob_get_clean();
|
||||
|
||||
$data_array = array(
|
||||
'card_anchor' => esc_html( substr( md5( $user_id ), 10, 5 ) ),
|
||||
'id' => absint( $user_id ),
|
||||
'role' => esc_html( um_user( 'role' ) ),
|
||||
'account_status' => esc_html( UM()->common()->users()->get_status( $user_id ) ),
|
||||
'account_status_name' => esc_html( UM()->common()->users()->get_status( $user_id, 'formatted' ) ),
|
||||
'card_anchor' => esc_html( $this->get_user_hash( $user_id ) ),
|
||||
'role' => is_user_logged_in() ? esc_html( um_user( 'role' ) ) : 'undefined', // make the role hidden for the nopriv requests.
|
||||
'account_status' => is_user_logged_in() ? esc_html( UM()->common()->users()->get_status( $user_id ) ) : 'undefined', // make the status hidden for the nopriv requests.
|
||||
'account_status_name' => is_user_logged_in() ? esc_html( UM()->common()->users()->get_status( $user_id, 'formatted' ) ) : __( 'Undefined', 'ultimate-member' ), // make the status hidden for the nopriv requests.
|
||||
'cover_photo' => wp_kses( um_user( 'cover_photo', $this->cover_size ), UM()->get_allowed_html( 'templates' ) ),
|
||||
'display_name' => esc_html( um_user( 'display_name' ) ),
|
||||
'profile_url' => esc_url( um_user_profile_url() ),
|
||||
'can_edit' => (bool) $can_edit,
|
||||
'edit_profile_url' => esc_url( um_edit_profile_url() ),
|
||||
'edit_profile_url' => $can_edit ? esc_url( um_edit_profile_url() ) : '',
|
||||
'avatar' => wp_kses( get_avatar( $user_id, $this->avatar_size ), UM()->get_allowed_html( 'templates' ) ),
|
||||
'display_name_html' => wp_kses( um_user( 'display_name', 'html' ), UM()->get_allowed_html( 'templates' ) ),
|
||||
'dropdown_actions' => $dropdown_actions,
|
||||
@@ -2788,13 +2931,16 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main Query function for getting members via AJAX
|
||||
*/
|
||||
function ajax_get_members() {
|
||||
public function ajax_get_members() {
|
||||
UM()->check_ajax_nonce();
|
||||
|
||||
if ( UM()->is_rate_limited( 'member_directory' ) ) {
|
||||
wp_send_json_error( __( 'Too many requests', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $_POST['directory_id'] ) ) {
|
||||
@@ -2807,6 +2953,10 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
|
||||
wp_send_json_error( __( 'Wrong member directory data', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
if ( ! $this->can_view_directory( $directory_id ) ) {
|
||||
wp_send_json_error( __( 'You cannot see this member directory', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
$directory_data = UM()->query()->post_data( $directory_id );
|
||||
|
||||
//predefined result for user without capabilities to see other members
|
||||
|
||||
@@ -775,6 +775,9 @@ if ( ! class_exists( 'um\core\Shortcodes' ) ) {
|
||||
}
|
||||
|
||||
if ( 'directory' === $args['mode'] ) {
|
||||
if ( ! UM()->member_directory()->can_view_directory( $this->form_id ) ) {
|
||||
return ''; // Checking for privacy settings of the member directory
|
||||
}
|
||||
wp_enqueue_script( 'um_members' );
|
||||
wp_enqueue_style( 'um_members' );
|
||||
}
|
||||
|
||||
@@ -105,6 +105,10 @@ if ( ! class_exists( 'um\core\User_posts' ) ) {
|
||||
function load_posts() {
|
||||
UM()->check_ajax_nonce();
|
||||
|
||||
if ( UM()->is_rate_limited( 'paginate_posts' ) ) {
|
||||
wp_send_json_error( __( 'Too many requests', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
$author = ! empty( $_POST['author'] ) ? absint( $_POST['author'] ) : get_current_user_id();
|
||||
$page = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 0;
|
||||
|
||||
@@ -153,6 +157,10 @@ if ( ! class_exists( 'um\core\User_posts' ) ) {
|
||||
function load_comments() {
|
||||
UM()->check_ajax_nonce();
|
||||
|
||||
if ( UM()->is_rate_limited( 'paginate_comments' ) ) {
|
||||
wp_send_json_error( __( 'Too many requests', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
$user_id = ! empty( $_POST['user_id'] ) ? absint( $_POST['user_id'] ) : get_current_user_id();
|
||||
$page = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 0;
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||||
|
||||
/**
|
||||
* Run check if username exists
|
||||
* @uses action hooks: wp_ajax_nopriv_ultimatemember_check_username_exists, wp_ajax_ultimatemember_check_username_exists
|
||||
* @return boolean
|
||||
*/
|
||||
function ultimatemember_check_username_exists() {
|
||||
UM()->check_ajax_nonce();
|
||||
|
||||
$username = isset( $_REQUEST['username'] ) ? sanitize_user( $_REQUEST['username'] ) : '';
|
||||
$exists = username_exists( $username );
|
||||
|
||||
/**
|
||||
* UM hook
|
||||
*
|
||||
* @type filter
|
||||
* @title um_validate_username_exists
|
||||
* @description Change username exists validation
|
||||
* @input_vars
|
||||
* [{"var":"$exists","type":"bool","desc":"Exists?"},
|
||||
* {"var":"$username","type":"string","desc":"Username"}]
|
||||
* @change_log
|
||||
* ["Since: 2.0"]
|
||||
* @usage
|
||||
* <?php add_filter( 'um_validate_username_exists', 'function_name', 10, 2 ); ?>
|
||||
* @example
|
||||
* <?php
|
||||
* add_filter( 'um_validate_username_exists', 'my_validate_username_exists', 10, 2 );
|
||||
* function my_account_pre_updating_profile( $exists, $username ) {
|
||||
* // your code here
|
||||
* return $exists;
|
||||
* }
|
||||
* ?>
|
||||
*/
|
||||
$exists = apply_filters( 'um_validate_username_exists', $exists, $username );
|
||||
|
||||
if ( $exists ) {
|
||||
echo 1;
|
||||
} else {
|
||||
echo 0;
|
||||
}
|
||||
|
||||
die();
|
||||
}
|
||||
add_action('wp_ajax_nopriv_ultimatemember_check_username_exists', 'ultimatemember_check_username_exists');
|
||||
add_action('wp_ajax_ultimatemember_check_username_exists', 'ultimatemember_check_username_exists');
|
||||
@@ -76,15 +76,15 @@ function um_profile_field_filter_hook__youtube_video( $value, $data ) {
|
||||
}
|
||||
$value = ( strstr( $value, 'http' ) || strstr( $value, '://' ) ) ? um_youtube_id_from_url( $value ) : $value;
|
||||
if ( false !== $value ) {
|
||||
$value = 'https://www.youtube.com/embed/' . $value;
|
||||
$value = '<div class="um-youtube">'
|
||||
. '<iframe width="600" height="450" src="https://www.youtube.com/embed/' . $value . '" frameborder="0" allowfullscreen></iframe>'
|
||||
. '<iframe width="600" height="450" src="' . esc_url( $value ) . '" frameborder="0" allowfullscreen></iframe>'
|
||||
. '</div>';
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
add_filter( 'um_profile_field_filter_hook__youtube_video', 'um_profile_field_filter_hook__youtube_video', 99, 2 );
|
||||
|
||||
|
||||
/**
|
||||
* Outputs a spotify iframe
|
||||
*
|
||||
@@ -127,8 +127,9 @@ function um_profile_field_filter_hook__vimeo_video( $value, $data ) {
|
||||
}
|
||||
|
||||
$value = ! is_numeric( $value ) ? (int) substr( parse_url( $value, PHP_URL_PATH ), 1 ) : $value;
|
||||
$value = 'https://player.vimeo.com/video/' . $value;
|
||||
$value = '<div class="um-vimeo">
|
||||
<iframe src="https://player.vimeo.com/video/' . $value . '" width="600" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
|
||||
<iframe src="' . esc_url( $value ) . '" width="600" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
|
||||
</div>';
|
||||
return $value;
|
||||
}
|
||||
@@ -199,14 +200,14 @@ function um_profile_field_filter_hook__googlemap( $value, $data ) {
|
||||
if ( ! $value ) {
|
||||
return '';
|
||||
}
|
||||
$value = 'https://maps.google.it/maps?q=' . urlencode( $value ) . '&output=embed';
|
||||
$value = '<div class="um-googlemap">
|
||||
<iframe width="600" height="450" frameborder="0" style="border:0" src="https://maps.google.it/maps?q=' . urlencode( $value ) . '&output=embed"></iframe>
|
||||
<iframe width="600" height="450" frameborder="0" style="border:0" src="' . esc_url( $value ) . '"></iframe>
|
||||
</div>';
|
||||
return $value;
|
||||
}
|
||||
add_filter( 'um_profile_field_filter_hook__googlemap', 'um_profile_field_filter_hook__googlemap', 99, 2 );
|
||||
|
||||
|
||||
/**
|
||||
* User's registration date
|
||||
*
|
||||
|
||||
@@ -172,6 +172,9 @@ IMPORTANT: PLEASE UPDATE THE PLUGIN TO AT LEAST VERSION 2.6.7 IMMEDIATELY. VERSI
|
||||
**Bugfixes**
|
||||
|
||||
* Fixed: CVE-2025-13220.
|
||||
* Fixed: CVE-2025-13217.
|
||||
* Fixed: CVE-2025-14081.
|
||||
* Fixed: CVE-2025-12492.
|
||||
|
||||
= 2.11.0 2025-12-02 =
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Page: "Members"
|
||||
*
|
||||
* @version 2.6.1
|
||||
* @version 2.11.1
|
||||
*
|
||||
* @var array $args
|
||||
* @var bool $cover_photos
|
||||
@@ -23,8 +23,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$unique_hash = substr( md5( $args['form_id'] ), 10, 5 ); ?>
|
||||
|
||||
$unique_hash = UM()->member_directory()->get_directory_hash( $args['form_id'] );
|
||||
?>
|
||||
<script type="text/template" id="tmpl-um-member-grid-<?php echo esc_attr( $unique_hash ) ?>">
|
||||
<div class="um-members um-members-grid">
|
||||
<div class="um-gutter-sizer"></div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Page: "Members"
|
||||
*
|
||||
* @version 2.6.1
|
||||
* @version 2.11.1
|
||||
*
|
||||
* @var array $args
|
||||
* @var bool $cover_photos
|
||||
@@ -19,10 +19,12 @@
|
||||
* @var array $reveal_fields
|
||||
* @var string $no_users
|
||||
*/
|
||||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||||
|
||||
$unique_hash = substr( md5( $args['form_id'] ), 10, 5 ); ?>
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$unique_hash = UM()->member_directory()->get_directory_hash( $args['form_id'] );
|
||||
?>
|
||||
<script type="text/template" id="tmpl-um-member-list-<?php echo esc_attr( $unique_hash ) ?>">
|
||||
<div class="um-members um-members-list">
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Page: "Members"
|
||||
*
|
||||
* @version 2.6.8
|
||||
* @version 2.11.1
|
||||
*
|
||||
* @var array $args
|
||||
*/
|
||||
@@ -23,7 +23,7 @@ foreach ( UM()->config()->core_directory_meta['members'] as $k => $v ) {
|
||||
$def_args[ $key ] = $v;
|
||||
}
|
||||
|
||||
$unique_hash = substr( md5( $args['form_id'] ), 10, 5 );
|
||||
$unique_hash = UM()->member_directory()->get_directory_hash( $args['form_id'] );
|
||||
|
||||
$args = array_merge( $def_args, $args );
|
||||
|
||||
@@ -237,8 +237,8 @@ if ( ( ( $search && $show_search ) || ( $filters && $show_filters && count( $sea
|
||||
$postid = ! empty( $post->ID ) ? $post->ID : '';
|
||||
?>
|
||||
|
||||
<div class="um <?php echo esc_attr( $this->get_class( $mode ) ); ?> um-<?php echo esc_attr( substr( md5( $form_id ), 10, 5 ) ); ?>"
|
||||
data-hash="<?php echo esc_attr( substr( md5( $form_id ), 10, 5 ) ) ?>" data-base-post="<?php echo esc_attr( $postid ) ?>"
|
||||
<div class="um <?php echo esc_attr( $this->get_class( $mode ) ); ?> um-<?php echo esc_attr( $unique_hash ); ?>"
|
||||
data-hash="<?php echo esc_attr( $unique_hash ) ?>" data-base-post="<?php echo esc_attr( $postid ) ?>"
|
||||
data-must-search="<?php echo esc_attr( $must_search ); ?>" data-searched="<?php echo $not_searched ? '0' : '1'; ?>"
|
||||
data-view_type="<?php echo esc_attr( $current_view ) ?>" data-page="<?php echo esc_attr( $current_page ) ?>"
|
||||
data-sorting="<?php echo esc_attr( $sort_from_url ) ?>">
|
||||
@@ -313,7 +313,7 @@ $postid = ! empty( $post->ID ) ? $post->ID : '';
|
||||
<?php $items = array();
|
||||
|
||||
foreach ( $sorting_options as $value => $title ) {
|
||||
$items[] = '<a href="javascript:void(0);" data-directory-hash="' . esc_attr( substr( md5( $form_id ), 10, 5 ) ) . '" class="um-sortyng-by-' . esc_attr( $value ) . '" data-value="' . esc_attr( $value ) . '" data-selected="' . ( ( $sort_from_url == $value ) ? '1' : '0' ) . '" data-default="' . ( ( $default_sorting == $value ) ? '1' : '0' ) . '">' . $title . '</a>'; ?>
|
||||
$items[] = '<a href="javascript:void(0);" data-directory-hash="' . esc_attr( $unique_hash ) . '" class="um-sortyng-by-' . esc_attr( $value ) . '" data-value="' . esc_attr( $value ) . '" data-selected="' . ( ( $sort_from_url == $value ) ? '1' : '0' ) . '" data-default="' . ( ( $default_sorting == $value ) ? '1' : '0' ) . '">' . $title . '</a>'; ?>
|
||||
<?php }
|
||||
|
||||
UM()->member_directory()->dropdown_menu( '.um-member-directory-sorting-a', 'click', $items ); ?>
|
||||
|
||||
Reference in New Issue
Block a user