From 2086ddb53f42ac7a7d2e736f1d07446c571c9b1b Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Fri, 15 Nov 2024 02:06:03 +0200 Subject: [PATCH] * added `um_image_upload_validation` hook for 3rd-party validation during upload images; --- changelog.txt | 15 ++ includes/core/class-files.php | 253 +++++++++++++++++++--------------- readme.txt | 4 + ultimate-member.php | 2 +- 4 files changed, 161 insertions(+), 113 deletions(-) diff --git a/changelog.txt b/changelog.txt index 49946b34..098cb6bc 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,20 @@ == Changelog == += 2.9.1 November 14, 2024 = + +* Enhancements: + + - Added: `um_image_upload_validation` hook for 3rd-party validation during upload images + +* Bugfixes: + + - Fixed: "Load textdomain just in time" issue + - Fixed: Capabilities checking in the wp-admin > Users list table + - Fixed: File/image upload on the role specific profile form + - Fixed: Issues when the form's custom fields meta has a wrong format + - Fixed: Validation of the "Registration Default Role" slug + - Fixed: Allowed query variables via registered REST API class only when REST_REQUEST is defined + = 2.9.0 November 12, 2024 = * Enhancements: diff --git a/includes/core/class-files.php b/includes/core/class-files.php index 0101a957..60b2e5e2 100644 --- a/includes/core/class-files.php +++ b/includes/core/class-files.php @@ -450,143 +450,172 @@ if ( ! class_exists( 'um\core\Files' ) ) { $ret['error'] = null; $ret = array(); + if ( empty( $_POST['key'] ) ) { + $ret['error'] = esc_html__( 'Invalid image key', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + + $id = sanitize_text_field( $_POST['key'] ); + $user_id = empty( $_POST['user_id'] ) ? null : absint( $_POST['user_id'] ); + /** - * Filters image upload checking nonce. + * Filters the custom validation marker for 3rd-party uploader. * - * @param {bool} $verify_nonce Verify nonce marker. Default true. + * @param {bool} $custom_validation Custom validation marker. Is null by default. Keep null for UM core validation. + * @param {string} $id Uploader field key. + * @param {int} $user_id User ID. * - * @return {bool} Verify nonce marker. + * @return {bool} Custom validation marker. * - * @since 1.3.x - * @hook um_image_upload_nonce + * @since 2.9.1 + * @hook um_image_upload_validation * - * @example Disable checking nonce on image upload. + * @example Custom validation. * function my_image_upload_nonce( $verify_nonce ) { * // your code here - * $verify_nonce = false; - * return $verify_nonce; + * $ret['error'] = esc_html__( 'Error code', 'ultimate-member' ); + * wp_send_json_error( $ret ); + * return true; * } - * add_filter( 'um_image_upload_nonce', 'my_image_upload_nonce' ); + * add_filter( 'um_image_upload_validation', 'my_um_image_upload_validation', 10, 3 ); */ - $um_image_upload_nonce = apply_filters( 'um_image_upload_nonce', true ); - if ( $um_image_upload_nonce ) { - $timestamp = absint( $_POST['timestamp'] ); - $nonce = sanitize_text_field( $_POST['_wpnonce'] ); - if ( ! wp_verify_nonce( $nonce, "um_upload_nonce-{$timestamp}" ) && is_user_logged_in() ) { - // This nonce is not valid. - $ret['error'] = esc_html__( 'Invalid nonce', 'ultimate-member' ); - wp_send_json_error( $ret ); + $custom_validation = apply_filters( 'um_image_upload_validation', null, $id, $user_id ); + if ( is_null( $custom_validation ) ) { + /** + * Filters image upload checking nonce. + * + * @param {bool} $verify_nonce Verify nonce marker. Default true. + * + * @return {bool} Verify nonce marker. + * + * @since 1.3.x + * @hook um_image_upload_nonce + * + * @example Disable checking nonce on image upload. + * function my_image_upload_nonce( $verify_nonce ) { + * // your code here + * $verify_nonce = false; + * return $verify_nonce; + * } + * add_filter( 'um_image_upload_nonce', 'my_image_upload_nonce' ); + */ + $um_image_upload_nonce = apply_filters( 'um_image_upload_nonce', true ); + if ( $um_image_upload_nonce ) { + $timestamp = absint( $_POST['timestamp'] ); + $nonce = sanitize_text_field( $_POST['_wpnonce'] ); + if ( ! wp_verify_nonce( $nonce, "um_upload_nonce-{$timestamp}" ) && is_user_logged_in() ) { + // This nonce is not valid. + $ret['error'] = esc_html__( 'Invalid nonce', 'ultimate-member' ); + wp_send_json_error( $ret ); + } } - } - $user_id = empty( $_POST['user_id'] ) ? null : absint( $_POST['user_id'] ); - if ( $user_id && is_user_logged_in() && ! UM()->roles()->um_current_user_can( 'edit', $user_id ) ) { - $ret['error'] = esc_html__( 'You have no permission to edit this user', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - - if ( $user_id && ! is_user_logged_in() ) { - $ret['error'] = esc_html__( 'Please login to edit this user', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - - $form_id = absint( $_POST['set_id'] ); - $mode = sanitize_key( $_POST['set_mode'] ); - - UM()->fields()->set_id = $form_id; - UM()->fields()->set_mode = $mode; - - if ( ! is_user_logged_in() && 'profile' === $mode ) { - $ret['error'] = esc_html__( 'You have no permission to edit user profile', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - - if ( null !== $user_id && 'register' === $mode ) { - $ret['error'] = esc_html__( 'User has to be empty on registration', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - - $form_post = get_post( $form_id ); - // Invalid post ID. Maybe post doesn't exist. - if ( empty( $form_post ) ) { - $ret['error'] = esc_html__( 'Invalid form ID', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - - if ( 'um_form' !== $form_post->post_type ) { - $ret['error'] = esc_html__( 'Invalid form post type', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - - $form_status = get_post_status( $form_id ); - if ( 'publish' !== $form_status ) { - $ret['error'] = esc_html__( 'Invalid form status', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - - $post_data = UM()->query()->post_data( $form_id ); - if ( ! array_key_exists( 'mode', $post_data ) || $mode !== $post_data['mode'] ) { - $ret['error'] = esc_html__( 'Invalid form type', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - - // For profiles only. - if ( 'profile' === $mode && ! empty( $post_data['use_custom_settings'] ) && ! empty( $post_data['role'] ) ) { - // Option "Apply custom settings to this form". Option "Make this profile form role-specific". - // Show the first Profile Form with role selected, don't show profile forms below the page with other role-specific setting. - $current_user_roles = UM()->roles()->get_all_user_roles( $user_id ); - if ( empty( $current_user_roles ) ) { - $ret['error'] = esc_html__( 'You have no permission to edit this user through this form', 'ultimate-member' ); + if ( $user_id && is_user_logged_in() && ! UM()->roles()->um_current_user_can( 'edit', $user_id ) ) { + $ret['error'] = esc_html__( 'You have no permission to edit this user', 'ultimate-member' ); wp_send_json_error( $ret ); } - $post_data['role'] = maybe_unserialize( $post_data['role'] ); + if ( $user_id && ! is_user_logged_in() ) { + $ret['error'] = esc_html__( 'Please login to edit this user', 'ultimate-member' ); + wp_send_json_error( $ret ); + } - if ( is_array( $post_data['role'] ) ) { - if ( ! count( array_intersect( $post_data['role'], $current_user_roles ) ) ) { + $form_id = absint( $_POST['set_id'] ); + $mode = sanitize_key( $_POST['set_mode'] ); + + UM()->fields()->set_id = $form_id; + UM()->fields()->set_mode = $mode; + + if ( ! is_user_logged_in() && 'profile' === $mode ) { + $ret['error'] = esc_html__( 'You have no permission to edit user profile', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + + if ( null !== $user_id && 'register' === $mode ) { + $ret['error'] = esc_html__( 'User has to be empty on registration', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + + $form_post = get_post( $form_id ); + // Invalid post ID. Maybe post doesn't exist. + if ( empty( $form_post ) ) { + $ret['error'] = esc_html__( 'Invalid form ID', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + + if ( 'um_form' !== $form_post->post_type ) { + $ret['error'] = esc_html__( 'Invalid form post type', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + + $form_status = get_post_status( $form_id ); + if ( 'publish' !== $form_status ) { + $ret['error'] = esc_html__( 'Invalid form status', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + + $post_data = UM()->query()->post_data( $form_id ); + if ( ! array_key_exists( 'mode', $post_data ) || $mode !== $post_data['mode'] ) { + $ret['error'] = esc_html__( 'Invalid form type', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + + // For profiles only. + if ( 'profile' === $mode && ! empty( $post_data['use_custom_settings'] ) && ! empty( $post_data['role'] ) ) { + // Option "Apply custom settings to this form". Option "Make this profile form role-specific". + // Show the first Profile Form with role selected, don't show profile forms below the page with other role-specific setting. + $current_user_roles = UM()->roles()->get_all_user_roles( $user_id ); + if ( empty( $current_user_roles ) ) { $ret['error'] = esc_html__( 'You have no permission to edit this user through this form', 'ultimate-member' ); wp_send_json_error( $ret ); } - } elseif ( ! in_array( $post_data['role'], $current_user_roles, true ) ) { - $ret['error'] = esc_html__( 'You have no permission to edit this user through this form', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - } - $id = sanitize_text_field( $_POST['key'] ); + $post_data['role'] = maybe_unserialize( $post_data['role'] ); - if ( ! array_key_exists( 'custom_fields', $post_data ) || empty( $post_data['custom_fields'] ) ) { - $ret['error'] = esc_html__( 'Invalid form fields', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - - $custom_fields = maybe_unserialize( $post_data['custom_fields'] ); - if ( ! is_array( $custom_fields ) || ! array_key_exists( $id, $custom_fields ) ) { - if ( ! ( 'profile' === $mode && in_array( $id, array( 'cover_photo', 'profile_photo' ), true ) ) ) { - $ret['error'] = esc_html__( 'Invalid field metakey', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - } - - if ( 'profile' === $mode ) { - if ( in_array( $id, array( 'cover_photo', 'profile_photo' ), true ) ) { - if ( 'profile_photo' === $id ) { - $disable_photo_uploader = empty( $post_data['use_custom_settings'] ) ? UM()->options()->get( 'disable_profile_photo_upload' ) : $post_data['disable_photo_upload']; - if ( $disable_photo_uploader ) { - $ret['error'] = esc_html__( 'You have no permission to edit this field', 'ultimate-member' ); - wp_send_json_error( $ret ); - } - } else { - $cover_enabled_uploader = empty( $post_data['use_custom_settings'] ) ? UM()->options()->get( 'profile_cover_enabled' ) : $post_data['cover_enabled']; - if ( ! $cover_enabled_uploader ) { - $ret['error'] = esc_html__( 'You have no permission to edit this field', 'ultimate-member' ); + if ( is_array( $post_data['role'] ) ) { + if ( ! count( array_intersect( $post_data['role'], $current_user_roles ) ) ) { + $ret['error'] = esc_html__( 'You have no permission to edit this user through this form', 'ultimate-member' ); wp_send_json_error( $ret ); } + } elseif ( ! in_array( $post_data['role'], $current_user_roles, true ) ) { + $ret['error'] = esc_html__( 'You have no permission to edit this user through this form', 'ultimate-member' ); + wp_send_json_error( $ret ); } - } elseif ( ! um_can_edit_field( $custom_fields[ $id ] ) ) { - $ret['error'] = esc_html__( 'You have no permission to edit this field', 'ultimate-member' ); + } + + if ( ! array_key_exists( 'custom_fields', $post_data ) || empty( $post_data['custom_fields'] ) ) { + $ret['error'] = esc_html__( 'Invalid form fields', 'ultimate-member' ); wp_send_json_error( $ret ); } + + $custom_fields = maybe_unserialize( $post_data['custom_fields'] ); + if ( ! is_array( $custom_fields ) || ! array_key_exists( $id, $custom_fields ) ) { + if ( ! ( 'profile' === $mode && in_array( $id, array( 'cover_photo', 'profile_photo' ), true ) ) ) { + $ret['error'] = esc_html__( 'Invalid field metakey', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + } + + if ( 'profile' === $mode ) { + if ( in_array( $id, array( 'cover_photo', 'profile_photo' ), true ) ) { + if ( 'profile_photo' === $id ) { + $disable_photo_uploader = empty( $post_data['use_custom_settings'] ) ? UM()->options()->get( 'disable_profile_photo_upload' ) : $post_data['disable_photo_upload']; + if ( $disable_photo_uploader ) { + $ret['error'] = esc_html__( 'You have no permission to edit this field', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + } else { + $cover_enabled_uploader = empty( $post_data['use_custom_settings'] ) ? UM()->options()->get( 'profile_cover_enabled' ) : $post_data['cover_enabled']; + if ( ! $cover_enabled_uploader ) { + $ret['error'] = esc_html__( 'You have no permission to edit this field', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + } + } elseif ( ! um_can_edit_field( $custom_fields[ $id ] ) ) { + $ret['error'] = esc_html__( 'You have no permission to edit this field', 'ultimate-member' ); + wp_send_json_error( $ret ); + } + } } if ( isset( $_FILES[ $id ]['name'] ) ) { diff --git a/readme.txt b/readme.txt index 991966f0..b9c4d036 100644 --- a/readme.txt +++ b/readme.txt @@ -168,6 +168,10 @@ IMPORTANT: PLEASE UPDATE THE PLUGIN TO AT LEAST VERSION 2.6.7 IMMEDIATELY. VERSI = 2.9.1 2024-11-14 = +**Enhancements** + +* Added: `um_image_upload_validation` hook for 3rd-party validation during upload images + **Bugfixes** * Fixed: "Load textdomain just in time" issue diff --git a/ultimate-member.php b/ultimate-member.php index f04473f1..61deebde 100644 --- a/ultimate-member.php +++ b/ultimate-member.php @@ -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.9.1-alpha + * Version: 2.9.1 * Author: Ultimate Member * Author URI: http://ultimatemember.com/ * Text Domain: ultimate-member