diff --git a/changelog.txt b/changelog.txt index b28b26ca..e11b1744 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,6 +5,8 @@ * Bugfixes: - Fixed: CVE-2025-13220. + - Fixed: CVE-2025-13217. + - Fixed: CVE-2025-14081. = 2.11.0 December 02, 2025 = diff --git a/includes/admin/class-admin.php b/includes/admin/class-admin.php index 08a40d4d..635cf10d 100644 --- a/includes/admin/class-admin.php +++ b/includes/admin/class-admin.php @@ -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' ), ), diff --git a/includes/admin/class-secure.php b/includes/admin/class-secure.php index a8a37d61..58b90c9f 100644 --- a/includes/admin/class-secure.php +++ b/includes/admin/class-secure.php @@ -209,6 +209,13 @@ if ( ! class_exists( 'um\admin\Secure' ) ) { $scanner_content .= ''; $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', diff --git a/includes/admin/core/class-admin-metabox.php b/includes/admin/core/class-admin-metabox.php index 9332880f..aad37b29 100644 --- a/includes/admin/core/class-admin-metabox.php +++ b/includes/admin/core/class-admin-metabox.php @@ -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' ); diff --git a/includes/admin/core/class-admin-settings.php b/includes/admin/core/class-admin-settings.php index 19dedc40..bf5c6d71 100644 --- a/includes/admin/core/class-admin-settings.php +++ b/includes/admin/core/class-admin-settings.php @@ -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' ), ), diff --git a/includes/admin/templates/directory/privacy.php b/includes/admin/templates/directory/privacy.php new file mode 100644 index 00000000..01517152 --- /dev/null +++ b/includes/admin/templates/directory/privacy.php @@ -0,0 +1,48 @@ + '_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 ); +?> + +
diff --git a/includes/class-config.php b/includes/class-config.php index f77a229f..ad1834dc 100644 --- a/includes/class-config.php +++ b/includes/class-config.php @@ -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', diff --git a/includes/class-functions.php b/includes/class-functions.php index 61124c73..1959f4b7 100644 --- a/includes/class-functions.php +++ b/includes/class-functions.php @@ -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? diff --git a/includes/class-init.php b/includes/class-init.php index 0d0f976e..d9e76433 100644 --- a/includes/class-init.php +++ b/includes/class-init.php @@ -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'; diff --git a/includes/core/class-account.php b/includes/core/class-account.php index 4ba9085a..4621752d 100644 --- a/includes/core/class-account.php +++ b/includes/core/class-account.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 * diff --git a/includes/core/class-files.php b/includes/core/class-files.php index be091959..edb1f730 100644 --- a/includes/core/class-files.php +++ b/includes/core/class-files.php @@ -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(); diff --git a/includes/core/class-form.php b/includes/core/class-form.php index 8c34c24b..8a8c6e3e 100644 --- a/includes/core/class-form.php +++ b/includes/core/class-form.php @@ -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(); diff --git a/includes/core/class-member-directory-meta.php b/includes/core/class-member-directory-meta.php index 6ebcefb1..8bc883f2 100644 --- a/includes/core/class-member-directory-meta.php +++ b/includes/core/class-member-directory-meta.php @@ -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. diff --git a/includes/core/class-member-directory.php b/includes/core/class-member-directory.php index 4241720f..e5673150 100644 --- a/includes/core/class-member-directory.php +++ b/includes/core/class-member-directory.php @@ -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 diff --git a/includes/core/class-shortcodes.php b/includes/core/class-shortcodes.php index cfff4958..80f3c7fa 100644 --- a/includes/core/class-shortcodes.php +++ b/includes/core/class-shortcodes.php @@ -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' ); } diff --git a/includes/core/class-user-posts.php b/includes/core/class-user-posts.php index 469717a4..a3ad786c 100644 --- a/includes/core/class-user-posts.php +++ b/includes/core/class-user-posts.php @@ -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; diff --git a/includes/core/um-actions-ajax.php b/includes/core/um-actions-ajax.php deleted file mode 100644 index 3aabce90..00000000 --- a/includes/core/um-actions-ajax.php +++ /dev/null @@ -1,49 +0,0 @@ -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 - * - * @example - * - */ - $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'); diff --git a/includes/core/um-filters-fields.php b/includes/core/um-filters-fields.php index 98d1c299..9904db3f 100644 --- a/includes/core/um-filters-fields.php +++ b/includes/core/um-filters-fields.php @@ -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 = '