check_ajax_nonce(); // phpcs:disable WordPress.Security.NonceVerification if ( ! isset( $_REQUEST['hook'] ) ) { die( esc_html__( 'Invalid hook', 'ultimate-member' ) ); } if ( isset( $_REQUEST['user_id'] ) ) { $user_id = absint( $_REQUEST['user_id'] ); } if ( ! isset( $user_id ) || ! UM()->roles()->um_current_user_can( 'edit', $user_id ) ) { die( esc_html__( 'You can not edit this user.', 'ultimate-member' ) ); } $hook = sanitize_key( $_REQUEST['hook'] ); /** * Fires on AJAX muted action. * * @since 1.3.x * @hook um_run_ajax_function__{$hook} * * @param {array} $request Request. * * @example Make any custom action on AJAX muted action. * function my_run_ajax_function( $request ) { * // your code here * } * add_action( 'um_run_ajax_function__{$hook}', 'my_run_ajax_function', 10, 1 ); */ do_action( "um_run_ajax_function__{$hook}", $_REQUEST ); // phpcs:enable WordPress.Security.NonceVerification } /** * */ public function ajax_select_options() { UM()->check_ajax_nonce(); // phpcs:disable WordPress.Security.NonceVerification $arr_options = array(); $arr_options['status'] = 'success'; $arr_options['post'] = $_POST; // Callback validation if ( empty( $_POST['child_callback'] ) ) { $arr_options['status'] = 'error'; $arr_options['message'] = __( 'Wrong callback.', 'ultimate-member' ); wp_send_json( $arr_options ); } $ajax_source_func = sanitize_text_field( $_POST['child_callback'] ); if ( ! function_exists( $ajax_source_func ) ) { $arr_options['status'] = 'error'; $arr_options['message'] = __( 'Wrong callback.', 'ultimate-member' ); wp_send_json( $arr_options ); } $allowed_callbacks = UM()->options()->get( 'allowed_choice_callbacks' ); if ( empty( $allowed_callbacks ) ) { $arr_options['status'] = 'error'; $arr_options['message'] = __( 'This is not possible for security reasons.', 'ultimate-member' ); wp_send_json( $arr_options ); } $allowed_callbacks = array_map( 'rtrim', explode( "\n", wp_unslash( $allowed_callbacks ) ) ); if ( ! in_array( $ajax_source_func, $allowed_callbacks, true ) ) { $arr_options['status'] = 'error'; $arr_options['message'] = __( 'This is not possible for security reasons.', 'ultimate-member' ); wp_send_json( $arr_options ); } if ( UM()->fields()->is_source_blacklisted( $ajax_source_func ) ) { $arr_options['status'] = 'error'; $arr_options['message'] = __( 'This is not possible for security reasons.', 'ultimate-member' ); wp_send_json( $arr_options ); } if ( isset( $_POST['form_id'] ) ) { UM()->fields()->set_id = absint( $_POST['form_id'] ); } UM()->fields()->set_mode = 'profile'; $form_fields = UM()->fields()->get_fields(); $arr_options['fields'] = $form_fields; if ( isset( $arr_options['post']['members_directory'] ) && 'yes' === $arr_options['post']['members_directory'] ) { global $wpdb; $values_array = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT meta_value FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value != ''", $arr_options['post']['child_name'] ) ); if ( ! empty( $values_array ) ) { $parent_dropdown = isset( $arr_options['post']['parent_option_name'] ) ? $arr_options['post']['parent_option_name'] : ''; $arr_options['items'] = call_user_func( $ajax_source_func, $parent_dropdown ); if ( array_keys( $arr_options['items'] ) !== range( 0, count( $arr_options['items'] ) - 1 ) ) { // array with dropdown items is associative $arr_options['items'] = array_intersect_key( array_map( 'trim', $arr_options['items'] ), array_flip( $values_array ) ); } else { // array with dropdown items has sequential numeric keys, starting from 0 and there are intersected values with $values_array $arr_options['items'] = array_intersect( $arr_options['items'], $values_array ); } } else { $arr_options['items'] = array(); } wp_send_json( $arr_options ); } else { /** * UM hook * * @type filter * @title um_ajax_select_options__debug_mode * @description Activate debug mode for AJAX select options * @input_vars * [{"var":"$debug_mode","type":"bool","desc":"Enable Debug mode"}] * @change_log * ["Since: 2.0"] * @usage * * @example * */ $debug = apply_filters( 'um_ajax_select_options__debug_mode', false ); if ( $debug ) { $arr_options['debug'] = array( $_POST, $form_fields, ); } if ( ! empty( $_POST['child_callback'] ) && isset( $form_fields[ $_POST['child_name'] ] ) ) { // If the requested callback function is added in the form or added in the field option, execute it with call_user_func. if ( isset( $form_fields[ $_POST['child_name'] ]['custom_dropdown_options_source'] ) && ! empty( $form_fields[ $_POST['child_name'] ]['custom_dropdown_options_source'] ) && $form_fields[ $_POST['child_name'] ]['custom_dropdown_options_source'] === $ajax_source_func ) { $arr_options['field'] = $form_fields[ $_POST['child_name'] ]; $arr_options['items'] = call_user_func( $ajax_source_func, $arr_options['field']['parent_dropdown_relationship'] ); } else { $arr_options['status'] = 'error'; $arr_options['message'] = __( 'This is not possible for security reasons.', 'ultimate-member' ); } } // phpcs:enable WordPress.Security.NonceVerification wp_send_json( $arr_options ); } } /** * Count the form errors. * @return integer */ public function count_errors() { $errors = $this->errors; if ( $errors && is_array( $errors ) ) { return count( $errors ); } return 0; } /** * Appends field errors * * @param string $key * @param string $error */ public function add_error( $key, $error ) { if ( ! isset( $this->errors[ $key ] ) ) { /** * UM hook * * @type filter * @title um_submit_form_error * @description Change error text on submit form * @input_vars * [{"var":"$error","type":"string","desc":"Error String"}, * {"var":"$key","type":"string","desc":"Error Key"}] * @change_log * ["Since: 2.0"] * @usage * * @example * */ $this->errors[ $key ] = apply_filters( 'um_submit_form_error', $error, $key ); } } /** * Appends field notices * @param string $key * @param string $notice */ public function add_notice( $key, $notice ) { if ( ! isset( $this->notices[ $key ] ) ) { /** * UM hook * * @type filter * @title um_submit_form_notice * @description Change notice text on submit form * @input_vars * [{"var":"$notice","type":"string","desc":"notice String"}, * {"var":"$key","type":"string","desc":"notice Key"}] * @change_log * ["Since: 2.0"] * @usage * * @example * */ $this->notices[ $key ] = apply_filters( 'um_submit_form_notice', $notice, $key ); } } /** * If a form has errors * * @param string $key * @return boolean */ public function has_error( $key ) { if ( isset( $this->errors[ $key ] ) ) { return true; } return false; } /** * If a form has notices/info * * @param string $key * @return boolean */ public function has_notice( $key ) { if ( isset( $this->notices[ $key ] ) ) { return true; } return false; } /** * Return the errors as a WordPress Error object * * @return \WP_Error */ function get_wp_error() { $wp_error = new \WP_Error(); if ( $this->count_errors() > 0 ) { foreach ( $this->errors as $key => $value ) { $wp_error->add( $key, $value ); } } return $wp_error; } /** * Declare all fields */ public function field_declare() { if ( isset( UM()->builtin()->custom_fields ) ) { $this->all_fields = UM()->builtin()->custom_fields; } else { $this->all_fields = null; } } /** * Remove banned wp_usermeta keys from submitted data. * * @since 2.6.5 * @param array $submitted * @return array */ public function clean_submitted_data( $submitted ) { foreach ( $submitted as $metakey => $value ) { if ( UM()->user()->is_metakey_banned( $metakey/*, 'submission'*/ ) ) { unset( $submitted[ $metakey ] ); } } return $submitted; } /** * Validate form on submit */ public function form_init() { if ( isset( $_SERVER['REQUEST_METHOD'] ) ) { $http_post = ( 'POST' === $_SERVER['REQUEST_METHOD'] ); } else { $http_post = 'POST'; } // Handles Register, Profile and Login forms. if ( $http_post && ! is_admin() && isset( $_POST['form_id'] ) && is_numeric( $_POST['form_id'] ) ) { $this->form_id = absint( $_POST['form_id'] ); if ( 'um_form' !== get_post_type( $this->form_id ) ) { return; } $this->form_status = get_post_status( $this->form_id ); if ( 'publish' !== $this->form_status ) { return; } // Verified that form_id is right and UM form is published. Then get form data. $this->form_data = UM()->query()->post_data( $this->form_id ); // Checking the form custom fields. Form without custom fields is invalid. if ( ! array_key_exists( 'mode', $this->form_data ) ) { return; } // Checking the form custom fields. Form without custom fields is invalid. if ( ! array_key_exists( 'custom_fields', $this->form_data ) ) { return; } $custom_fields = maybe_unserialize( $this->form_data['custom_fields'] ); if ( ! is_array( $custom_fields ) || empty( $custom_fields ) ) { return; } $ignore_keys = array(); $arr_restricted_fields = array(); if ( 'profile' === $this->form_data['mode'] ) { $arr_restricted_fields = UM()->fields()->get_restricted_fields_for_edit(); } $field_types_without_metakey = UM()->builtin()->get_fields_without_metakey(); foreach ( $custom_fields as $cf_k => $cf_data ) { if ( ! array_key_exists( 'type', $cf_data ) || in_array( $cf_data['type'], $field_types_without_metakey, true ) ) { unset( $custom_fields[ $cf_k ] ); } if ( array_key_exists( 'type', $cf_data ) && 'password' === $cf_data['type'] ) { $ignore_keys[] = $cf_k; $ignore_keys[] = 'confirm_' . $cf_k; } if ( 'profile' === $this->form_data['mode'] ) { if ( ! empty( $cf_data['edit_forbidden'] ) ) { $ignore_keys[] = $cf_k; } if ( ! um_can_edit_field( $cf_data ) || ! um_can_view_field( $cf_data ) ) { $ignore_keys[] = $cf_k; } } if ( ! array_key_exists( 'metakey', $cf_data ) || empty( $cf_data['metakey'] ) ) { unset( $custom_fields[ $cf_k ] ); } if ( isset( $cf_data['required_opt'] ) ) { $opt = $cf_data['required_opt']; if ( UM()->options()->get( $opt[0] ) !== $opt[1] ) { $ignore_keys[] = $cf_k; } } } $cf_metakeys = array_column( $custom_fields, 'metakey' ); $all_cf_metakeys = $cf_metakeys; // The '_um_last_login' cannot be updated through UM form. $cf_metakeys = array_values( array_diff( $cf_metakeys, array( 'role_select', 'role_radio', 'role', '_um_last_login', 'user_pass', 'user_password', 'confirm_user_password' ) ) ); if ( ! empty( $ignore_keys ) ) { $cf_metakeys = array_values( array_diff( $cf_metakeys, $ignore_keys ) ); } // Remove restricted fields when edit profile. if ( 'profile' === $this->form_data['mode'] ) { // Column names from wp_users table. $cf_metakeys = array_values( array_diff( $cf_metakeys, array( 'user_login' ) ) ); // Hidden for edit fields $cf_metakeys = array_values( array_diff( $cf_metakeys, $arr_restricted_fields ) ); $cf_metakeys[] = 'profile_photo'; $cf_metakeys[] = 'cover_photo'; if ( ! empty( $this->form_data['use_custom_settings'] ) && ! empty( $this->form_data['show_bio'] ) ) { $cf_metakeys[] = UM()->profile()->get_show_bio_key( $this->form_data ); } else { if ( UM()->options()->get( 'profile_show_bio' ) ) { $cf_metakeys[] = UM()->profile()->get_show_bio_key( $this->form_data ); } } } // Add required usermeta for register. if ( 'register' === $this->form_data['mode'] ) { $cf_metakeys[] = 'form_id'; } /** * Filters whitelisted usermeta keys that can be stored inside DB after UM Form submission. * * @param {array} $whitelisted_metakeys Whitelisted usermeta keys. * @param {array} $form_data UM form data. * * @return {array} Whitelisted usermeta keys. * * @since 2.6.7 * @hook um_whitelisted_metakeys * * @example Extends whitelisted usermeta keys. * function my_um_whitelisted_metakeys( $metakeys, $form_data ) { * $metakeys[] = 'some_key'; * return $metakeys; * } * add_filter( 'um_whitelisted_metakeys', 'my_um_whitelisted_metakeys', 10, 2 ); */ $cf_metakeys = apply_filters( 'um_whitelisted_metakeys', $cf_metakeys, $this->form_data ); // Important variable to prevent save unnecessary data to wp_usermeta. $this->usermeta_whitelist = $cf_metakeys; /** * Fires before UM login, registration or profile form submission. * * @since 1.3.x * @hook um_before_submit_form_post * * @param {array} $post $_POST submission array. Deprecated since 2.0. * @param {object} $this UM()->form() class instance. Since 2.6.7 * * @example Make any custom action before UM login, registration or profile form submission. * function my_custom_before_submit_form_post( $um_form_obj ) { * // your code here * } * add_action( 'um_before_submit_form_post', 'my_custom_before_submit_form_post' ); */ do_action( 'um_before_submit_form_post', $this ); /* save entire form as global */ /** * Filters $_POST submitted data by the UM login, registration or profile form. * * @param {array} $_post Submitted data. Already un-slashed by `wp_unslash()`. * * @return {array} Submitted data. * * @since 1.3.x * @hook um_submit_post_form * * @example Extends $_POST data. * function my_submit_post_form( $_post ) { * $_post['some_key'] = 'some value'; * return $_post; * } * add_filter( 'um_submit_post_form', 'my_submit_post_form' ); */ $this->post_form = apply_filters( 'um_submit_post_form', wp_unslash( $_POST ) ); // Validate form submission by honeypot. if ( isset( $this->post_form[ UM()->honeypot ] ) && '' !== $this->post_form[ UM()->honeypot ] ) { // High level escape if hacking. wp_die( esc_html__( 'Hello, spam bot!', 'ultimate-member' ) ); } $this->post_form = $this->beautify( $this->post_form ); // Validate and filter 'role' submitted data to avoid handling roles with admin privileges. // Remove role from post_form at first if role ! empty and there aren't custom fields with role name $maybe_set_default_role = true; if ( array_key_exists( 'role', $this->post_form ) ) { if ( 'login' === $this->form_data['mode'] ) { unset( $this->post_form['role'] ); } else { $form_has_role_field = count( array_intersect( $all_cf_metakeys, array( 'role_select', 'role_radio' ) ) ) > 0; if ( ! $form_has_role_field ) { unset( $this->post_form['role'] ); } else { $custom_field_roles = $this->custom_field_roles( $this->form_data['custom_fields'] ); if ( ! empty( $custom_field_roles ) && ! empty( $this->post_form['role'] ) ) { if ( is_array( $this->post_form['role'] ) ) { $role = current( $this->post_form['role'] ); $role = sanitize_key( $role ); } else { $role = sanitize_key( $this->post_form['role'] ); } global $wp_roles; $exclude_roles = array_diff( array_keys( $wp_roles->roles ), UM()->roles()->get_editable_user_roles() ); if ( ! empty( $role ) && ( ! in_array( $role, $custom_field_roles, true ) || in_array( $role, $exclude_roles, true ) ) ) { // High level escape if hacking. wp_die( esc_html__( 'This is not possible for security reasons.', 'ultimate-member' ) ); } $this->post_form['role'] = $role; $maybe_set_default_role = false; // Force adding `role` metakey if there is a role-type field on the form. It's required to User Profile. $this->usermeta_whitelist[] = 'role'; } } } } $this->post_form = $this->sanitize( $this->post_form ); $this->post_form['submitted'] = $this->post_form; // Set default role from settings on registration form. It has been made after defined 'submitted' because predefined role isn't a submitted field. if ( $maybe_set_default_role && 'register' === $this->form_data['mode'] ) { $role = $this->assigned_role( $this->form_id ); $this->post_form['role'] = $role; } /** * Filters $_POST submitted data by the UM login, registration or profile form. * It's un-slashed by `wp_unslash()`, beautified and sanitized. `role` attribute is filtered by possible role. * `submitted` key is added by code and contains summary of submission. * * Internal Ultimate Member callbacks (Priority -> Callback name -> Excerpt): * 9 - `um_submit_form_data_trim_fields()` maybe over-functionality and can be removed. * 10 - `um_submit_form_data_role_fields()` important for conditional logic based on role fields in form. * * @param {array} $_post Submitted data. * @param {string} $mode Form mode. login||register||profile * @param {array} $all_cf_metakeys Form's metakeys. Since 2.6.7. * * @return {array} Submitted data. * * @since 1.3.x * @hook um_submit_form_data * * @example Extends UM form submitted data. * function my_submit_form_data( $_post, $mode, $all_cf_metakeys ) { * $_post['some_key'] = 'some value'; * return $_post; * } * add_filter( 'um_submit_form_data', 'my_submit_form_data', 10, 3 ); */ $this->post_form = apply_filters( 'um_submit_form_data', $this->post_form, $this->form_data['mode'], $all_cf_metakeys ); /* Continue based on form mode - pre-validation */ /** * Fires for validation UM login, registration or profile form submission. * * Internal Ultimate Member callbacks (Priority -> Callback name -> Excerpt): * 10 - `um_submit_form_errors_hook()` All form validation handlers. * 20 - `um_recaptcha_validate()` reCAPTCHA form validation handlers. um-recaptcha extension. * * @since 1.3.x * @hook um_submit_form_errors_hook * * @param {array} $post $_POST Submission array. * @param {array} $form_data UM form data. Since 2.6.7 * * @example Make any common validation action here. * function my_custom_before_submit_form_post( $post, $form_data ) { * // your code here * } * add_action( 'um_submit_form_errors_hook', 'my_custom_submit_form_errors_hook', 10, 2 ); */ do_action( 'um_submit_form_errors_hook', $this->post_form, $this->form_data ); /* Continue based on form mode - store data. */ /** * Fires for make main actions on UM login, registration or profile form submission. * Where $mode equals login, registration or profile * * Internal Ultimate Member callbacks (Priority -> Callback name -> Excerpt): * ### um_submit_form_login: * * 1 - `UM()->login()->verify_nonce()` Verify nonce. * * 10 - `um_submit_form_login()` Login form main handler. * ### um_submit_form_register: * * 1 - `UM()->register()->verify_nonce()` Verify nonce. * * 9 - `UM()->agreement_validation()` GDPR Agreement. * * 9 - `UM()->terms_conditions()->agreement_validation()` Terms & Conditions Agreement. * * 10 - `um_submit_form_register()` Register form main handler. * ### um_submit_form_profile: * * 10 - `um_submit_form_profile()` Profile form main handler. * * @since 1.3.x * @hook um_submit_form_{$mode} * * @param {array} $post $_POST Submission array. * @param {array} $form_data UM form data. Since 2.6.7 * * @example Make any custom action on profile submission. * function my_custom_submit_form_profile( $post, $form_data ) { * // your code here * } * add_action( 'um_submit_form_profile', 'my_custom_submit_form_profile', 10, 2 ); */ do_action( "um_submit_form_{$this->form_data['mode']}", $this->post_form, $this->form_data ); } } /** * Beautify form data * * @param array $form * * @return array $form */ public function beautify( $form ) { if ( isset( $form['form_id'] ) ) { $this->form_suffix = '-' . $form['form_id']; $this->processing = absint( $form['form_id'] ); foreach ( $form as $key => $value ) { if ( strstr( $key, $this->form_suffix ) ) { $a_key = str_replace( $this->form_suffix, '', $key ); $form[ $a_key ] = $value; unset( $form[ $key ] ); } } } return $form; } /** * Use PHP tidy extension if it's active for getting clean HTML without unclosed tags. * * @param string $html_fragment Textarea with active HTML option field value. * @param array $field_data Ultimate Member form field data. * * @return string|\tidy */ private static function maybe_apply_tidy( $html_fragment, $field_data ) { // Break if extension isn't active in php.ini if ( ! function_exists( 'tidy_parse_string' ) ) { return $html_fragment; } $tidy_config = array( 'clean' => true, 'output-xhtml' => true, 'show-body-only' => true, 'wrap' => 0, ); /** * Filters PHP tidy extension config. * Get more info here https://www.php.net/manual/en/tidy.parsestring.php * * @param {array} $tidy_config Config. * @param {array} $field_data UM Form Field Data. * * @return {array} Config. * * @since 2.8.9 * @hook um_tidy_config * * @example Customize tidy config based on field data. * function my_um_tidy_config( $tidy_config, $field_data ) { * // your code here * if ( 'custom_metakey' === $field_data['metakey'] ) { * $tidy_config['clean'] = false; * } * return $tidy_config; * } * add_filter( 'um_tidy_config', 'my_um_tidy_config', 10, 2 ); */ $tidy_config = apply_filters( 'um_tidy_config', $tidy_config, $field_data ); // since PHP8.0 $tidy_config, 'UTF8' variables are nullable https://www.php.net/manual/en/tidy.parsestring.php $tidy = tidy_parse_string( $html_fragment, $tidy_config, 'UTF8' ); $result = $tidy->cleanRepair(); if ( $result ) { return $tidy; } return $html_fragment; } /** * Beautify form data * * @param array $form * * @return array $form */ public function sanitize( $form ) { if ( isset( $form['form_id'] ) ) { if ( isset( $this->form_data['custom_fields'] ) ) { $custom_fields = maybe_unserialize( $this->form_data['custom_fields'] ); if ( is_array( $custom_fields ) ) { foreach ( $custom_fields as $k => $field ) { if ( isset( $field['type'] ) ) { if ( isset( $form[ $k ] ) ) { switch ( $field['type'] ) { default: $form[ $k ] = apply_filters( 'um_sanitize_form_field', $form[ $k ], $field ); break; case 'number': $form[ $k ] = '' !== $form[ $k ] ? (int) $form[ $k ] : ''; break; case 'textarea': if ( ! empty( $field['html'] ) || ( UM()->profile()->get_show_bio_key( $form ) === $k && UM()->options()->get( 'profile_show_html_bio' ) ) ) { $form[ $k ] = html_entity_decode( $form[ $k ] ); // required because WP_Editor send sometimes encoded content. $form[ $k ] = self::maybe_apply_tidy( $form[ $k ], $field ); $allowed_html = UM()->get_allowed_html( 'templates' ); if ( empty( $allowed_html['iframe'] ) ) { $allowed_html['iframe'] = array( 'allow' => true, 'frameborder' => true, 'loading' => true, 'name' => true, 'referrerpolicy' => true, 'sandbox' => true, 'src' => true, 'srcdoc' => true, 'title' => true, 'width' => true, 'height' => true, 'allowfullscreen' => true, ); } $form[ $k ] = wp_kses( $form[ $k ], $allowed_html ); add_filter( 'wp_kses_allowed_html', array( &$this, 'wp_kses_user_desc' ), 10, 2 ); } else { $form[ $k ] = sanitize_textarea_field( $form[ $k ] ); } break; case 'oembed': case 'url': $f = UM()->builtin()->get_a_field( $k ); if ( is_array( $f ) && array_key_exists( 'match', $f ) && array_key_exists( 'advanced', $f ) && 'social' === $f['advanced'] ) { $v = $form[ $k ]; // Make a proper social link if ( ! empty( $v ) ) { $replace_match = is_array( $f['match'] ) ? $f['match'][0] : $f['match']; $need_replace = false; if ( is_array( $f['match'] ) ) { $need_replace = true; foreach ( $f['match'] as $arr_match ) { if ( strstr( $v, $arr_match ) ) { $need_replace = false; } } } if ( ! is_array( $f['match'] ) || $need_replace ) { if ( ! strstr( $v, $replace_match ) ) { $domain = trim( strtr( $replace_match, array( 'https://' => '', 'http://' => '', ) ), ' /' ); if ( ! strstr( $v, $domain ) ) { $v = $replace_match . $v; } else { $v = 'https://' . trim( strtr( $v, array( 'https://' => '', 'http://' => '', ) ), ' /' ); } } } } $form[ $k ] = esc_url_raw( $v ); } else { $form[ $k ] = esc_url_raw( $form[ $k ] ); } break; case 'password': $form[ $k ] = trim( $form[ $k ] ); if ( array_key_exists( 'confirm_' . $k, $form ) ) { $form[ 'confirm_' . $k ] = trim( $form[ 'confirm_' . $k ] ); } break; case 'text': case 'select': case 'image': case 'file': case 'date': case 'time': case 'rating': case 'googlemap': case 'youtube_video': case 'vimeo_video': case 'soundcloud_track': case 'spotify': case 'tel': $form[ $k ] = sanitize_text_field( $form[ $k ] ); break; case 'multiselect': case 'radio': case 'checkbox': $form[ $k ] = is_array( $form[ $k ] ) ? array_map( 'sanitize_text_field', $form[ $k ] ) : sanitize_text_field( $form[ $k ] ); break; } } } } } } $show_bio = false; $bio_html = false; $global_setting = UM()->options()->get( 'profile_show_html_bio' ); if ( ! empty( $form_data['use_custom_settings'] ) ) { if ( ! empty( $form_data['show_bio'] ) ) { $show_bio = true; $bio_html = ! empty( $global_setting ); } } else { $global_show_bio = UM()->options()->get( 'profile_show_bio' ); if ( ! empty( $global_show_bio ) ) { $show_bio = true; $bio_html = ! empty( $global_setting ); } } $description_key = UM()->profile()->get_show_bio_key( $this->form_data ); if ( $show_bio && ! empty( $form[ $description_key ] ) ) { $field_exists = false; if ( ! empty( $this->form_data['custom_fields'] ) ) { $custom_fields = maybe_unserialize( $this->form_data['custom_fields'] ); if ( array_key_exists( $description_key, $custom_fields ) ) { $field_exists = true; if ( ! empty( $custom_fields[ $description_key ]['html'] ) && $bio_html ) { $form[ $description_key ] = html_entity_decode( $form[ $description_key ] ); // required because WP_Editor send sometimes encoded content. $form[ $description_key ] = self::maybe_apply_tidy( $form[ $description_key ], $custom_fields[ $description_key ] ); $allowed_html = UM()->get_allowed_html( 'templates' ); if ( empty( $allowed_html['iframe'] ) ) { $allowed_html['iframe'] = array( 'allow' => true, 'frameborder' => true, 'loading' => true, 'name' => true, 'referrerpolicy' => true, 'sandbox' => true, 'src' => true, 'srcdoc' => true, 'title' => true, 'width' => true, 'height' => true, 'allowfullscreen' => true, ); } $form[ $description_key ] = wp_kses( $form[ $description_key ], $allowed_html ); add_filter( 'wp_kses_allowed_html', array( &$this, 'wp_kses_user_desc' ), 10, 2 ); } else { $form[ $description_key ] = sanitize_textarea_field( $form[ $description_key ] ); } } } if ( ! $field_exists ) { if ( $bio_html ) { $allowed_html = UM()->get_allowed_html( 'templates' ); if ( empty( $allowed_html['iframe'] ) ) { $allowed_html['iframe'] = array( 'allow' => true, 'frameborder' => true, 'loading' => true, 'name' => true, 'referrerpolicy' => true, 'sandbox' => true, 'src' => true, 'srcdoc' => true, 'title' => true, 'width' => true, 'height' => true, 'allowfullscreen' => true, ); } $form[ $description_key ] = wp_kses( $form[ $description_key ], $allowed_html ); add_filter( 'wp_kses_allowed_html', array( &$this, 'wp_kses_user_desc' ), 10, 2 ); } else { $form[ $description_key ] = sanitize_textarea_field( $form[ $description_key ] ); } } } } return $form; } public function wp_kses_user_desc( $tags, $context ) { if ( 'user_description' === $context || 'pre_user_description' === $context ) { $allowed_html = UM()->get_allowed_html( 'templates' ); if ( empty( $allowed_html['iframe'] ) ) { $allowed_html['iframe'] = array( 'allow' => true, 'frameborder' => true, 'loading' => true, 'name' => true, 'referrerpolicy' => true, 'sandbox' => true, 'src' => true, 'srcdoc' => true, 'title' => true, 'width' => true, 'height' => true, 'allowfullscreen' => true, ); } $tags = $allowed_html; } return $tags; } /** * Display form type as Title * @param string $mode * @param integer $post_id * @return string $output */ public function display_form_type( $mode, $post_id ) { $output = null; switch ( $mode ) { case 'login': $output = __( 'Login', 'ultimate-member' ); break; case 'profile': $output = __( 'Profile', 'ultimate-member' ); break; case 'register': $output = __( 'Register', 'ultimate-member' ); break; } return $output; } /** * Assigned roles to a form * @param integer $post_id * @return string $role */ public function assigned_role( $post_id ) { $global_role = get_option( 'default_role' ); // WP Global settings $um_global_role = UM()->options()->get( 'register_role' ); // UM Settings Global settings $existing_roles = array_keys( wp_roles()->roles ); if ( ! empty( $um_global_role ) && in_array( $um_global_role, $existing_roles, true ) ) { $global_role = $um_global_role; // Form Global settings } $mode = $this->form_type( $post_id ); /** * @todo WPML integration to get role from original if it's empty */ $use_custom = get_post_meta( $post_id, "_um_{$mode}_use_custom_settings", true ); if ( $use_custom ) { // Custom Form settings $role = get_post_meta( $post_id, "_um_{$mode}_role", true ); } if ( empty( $role ) || ! in_array( $role, $existing_roles, true ) ) { // custom role is default, return default role's slug $role = $global_role; } return $role; } /** * Get form type * @param integer $post_id * @return string */ public function form_type( $post_id ) { $mode = get_post_meta( $post_id, '_um_mode', true ); return $mode; } /** * Get custom field roles * * @param string $custom_fields serialized * @return bool|array roles */ public function custom_field_roles( $custom_fields ) { $fields = maybe_unserialize( $custom_fields ); if ( ! is_array( $fields ) ) { return false; } // role field global $wp_roles; $exclude_roles = array_diff( array_keys( $wp_roles->roles ), UM()->roles()->get_editable_user_roles() ); $roles = UM()->roles()->get_roles( false, $exclude_roles ); $roles = array_map( function( $item ) { return html_entity_decode( $item, ENT_QUOTES ); }, $roles ); foreach ( $fields as $field_key => $field_settings ) { if ( strstr( $field_key, 'role_' ) && array_key_exists( 'options', $field_settings ) && is_array( $field_settings['options'] ) ) { if ( isset( $this->post_form['mode'] ) && 'profile' === $this->post_form['mode'] ) { // It's for a legacy case `array_key_exists( 'editable', $field_settings )`. if ( ( array_key_exists( 'editable', $field_settings ) && empty( $field_settings['editable'] ) ) || ! um_can_edit_field( $field_settings ) ) { continue; } } if ( ! um_can_view_field( $field_settings ) ) { continue; } $intersected_options = array(); foreach ( $field_settings['options'] as $key => $title ) { if ( false !== $search_key = array_search( $title, $roles ) ) { $intersected_options[ $search_key ] = $title; } elseif ( isset( $roles[ $key ] ) ) { $intersected_options[ $key ] = $title; } } // getting roles only from the first role fields return array_keys( $intersected_options ); } } return false; } } }