/** * bbPress User Functions * * @package bbPress * @subpackage Functions */ // Exit if accessed directly defined( 'ABSPATH' ) || exit; /** * Redirect back to $url when attempting to use the login page * * @since 2.0.0 bbPress (r2815) * * @param string $url The url * @param string $raw_url Raw url * @param object $user User object */ function bbp_redirect_login( $url = '', $raw_url = '', $user = '' ) { // Raw redirect_to was passed, so use it if ( ! empty( $raw_url ) ) { $url = $raw_url; // $url was manually set in wp-login.php to redirect to admin } elseif ( admin_url() === $url ) { $url = home_url(); // $url is empty } elseif ( empty( $url ) ) { $url = home_url(); } // Filter & return return apply_filters( 'bbp_redirect_login', $url, $raw_url, $user ); } /** * Is an anonymous topic/reply being made? * * @since 2.0.0 bbPress (r2688) * * @return bool True if anonymous is allowed and user is not logged in, false if * anonymous is not allowed or user is logged in */ function bbp_is_anonymous() { $is_anonymous = ( ! is_user_logged_in() && bbp_allow_anonymous() ); // Filter & return return (bool) apply_filters( 'bbp_is_anonymous', $is_anonymous ); } /** * Echoes the values for current poster (uses WP comment cookies) * * @since 2.0.0 bbPress (r2734) * * @param string $key Which value to echo? */ function bbp_current_anonymous_user_data( $key = '' ) { echo esc_attr( bbp_get_current_anonymous_user_data( $key ) ); } /** * Get the cookies for current poster (uses WP comment cookies). * * @since 2.0.0 bbPress (r2734) * * @param string $key Optional. Which value to get? If not given, then * an array is returned. * @return string|array Cookie(s) for current poster */ function bbp_get_current_anonymous_user_data( $key = '' ) { // Array of allowed cookie names $cookie_names = array( 'name' => 'comment_author', 'email' => 'comment_author_email', 'url' => 'comment_author_url', // Here just for the sake of them, use the above ones 'comment_author' => 'comment_author', 'comment_author_email' => 'comment_author_email', 'comment_author_url' => 'comment_author_url', ); // Get the current poster's info from the cookies $bbp_current_poster = wp_get_current_commenter(); // Sanitize the cookie key being retrieved $key = sanitize_key( $key ); // Maybe return a specific key if ( ! empty( $key ) && in_array( $key, array_keys( $cookie_names ), true ) ) { return $bbp_current_poster[ $cookie_names[ $key ] ]; } // Return all keys return $bbp_current_poster; } /** * Set the cookies for current poster (uses WP comment cookies) * * @since 2.0.0 bbPress (r2734) * * @param array $anonymous_data Optional - if it's an anonymous post. Do not * supply if supplying $author_id. Should be * sanitized (see {@link bbp_filter_anonymous_post_data()} */ function bbp_set_current_anonymous_user_data( $anonymous_data = array() ) { // Bail if empty or not an array if ( empty( $anonymous_data ) || ! is_array( $anonymous_data ) ) { return; } // Setup cookie expiration $lifetime = (int) apply_filters( 'comment_cookie_lifetime', 30000000 ); $expiry = time() + $lifetime; $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) ); // Set the cookies setcookie( 'comment_author_' . COOKIEHASH, $anonymous_data['bbp_anonymous_name'], $expiry, COOKIEPATH, COOKIE_DOMAIN, $secure ); setcookie( 'comment_author_email_' . COOKIEHASH, $anonymous_data['bbp_anonymous_email'], $expiry, COOKIEPATH, COOKIE_DOMAIN, $secure ); setcookie( 'comment_author_url_' . COOKIEHASH, $anonymous_data['bbp_anonymous_website'], $expiry, COOKIEPATH, COOKIE_DOMAIN, $secure ); } /** * Get the poster IP address * * @since 2.0.0 bbPress (r3120) * @since 2.6.0 bbPress (r5609) Added `empty()` check for unit tests * * @return string */ function bbp_current_author_ip() { // Check for remote address $remote_address = ! empty( $_SERVER['REMOTE_ADDR'] ) ? wp_unslash( $_SERVER['REMOTE_ADDR'] ) : '127.0.0.1'; // Remove any unsavory bits $retval = preg_replace( '/[^0-9a-fA-F:., ]/', '', $remote_address ); // Filter & return return apply_filters( 'bbp_current_author_ip', $retval, $remote_address ); } /** * Get the poster user agent * * @since 2.0.0 bbPress (r3446) * * @return string */ function bbp_current_author_ua() { $retval = ! empty( $_SERVER['HTTP_USER_AGENT'] ) ? mb_substr( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ), 0, 254 ) : ''; // Filter & return return apply_filters( 'bbp_current_author_ua', $retval ); } /** Edit **********************************************************************/ /** * Handles the front end user editing from POST requests * * @since 2.0.0 bbPress (r2790) * * @param string $action The requested action to compare this function to */ function bbp_edit_user_handler( $action = '' ) { // Bail if action is not `bbp-update-user` if ( 'bbp-update-user' !== $action ) { return; } // Bail if in wp-admin if ( is_admin() ) { return; } // Get the displayed user ID $user_id = bbp_get_displayed_user_id(); // Nonce check if ( ! bbp_verify_nonce_request( 'update-user_' . $user_id ) ) { bbp_add_error( 'bbp_update_user_nonce', __( 'Error: Are you sure you wanted to do that?', 'bbpress' ) ); return; } // Cap check if ( ! current_user_can( 'edit_user', $user_id ) ) { bbp_add_error( 'bbp_update_user_capability', __( 'Error: Are you sure you wanted to do that?', 'bbpress' ) ); return; } // Empty email check if ( empty( $_POST['email'] ) ) { bbp_add_error( 'bbp_user_email_empty', __( 'Error: That is not a valid email address.', 'bbpress' ), array( 'form-field' => 'email' ) ); return; } // Get the users current email address to use for comparisons $user_email = bbp_get_displayed_user_field( 'user_email', 'raw' ); // Bail if no email change if ( $user_email !== $_POST['email'] ) { // Check that new email address is valid if ( ! is_email( $_POST['email'] ) ) { bbp_add_error( 'bbp_user_email_invalid', __( 'Error: That is not a valid email address.', 'bbpress' ), array( 'form-field' => 'email' ) ); return; } // Check if email address is already in use if ( email_exists( $_POST['email'] ) ) { bbp_add_error( 'bbp_user_email_taken', __( 'Error: That email address is already in use.', 'bbpress' ), array( 'form-field' => 'email' ) ); return; } // Update the option $option = array( 'hash' => md5( $_POST['email'] . time() . wp_rand() ), 'newemail' => $_POST['email'], ); update_user_meta( $user_id, '_new_email', $option ); // Attempt to notify the user of email address change bbp_edit_user_email_send_notification( $user_id, $option ); // Set the POST email variable back to the user's email address // so `edit_user()` does not attempt to update it. This is not ideal, // but it's also what send_confirmation_on_profile_email() does. $_POST['email'] = $user_email; } // Do action based on who's profile you're editing $edit_action = bbp_is_user_home_edit() ? 'personal_options_update' : 'edit_user_profile_update'; do_action( $edit_action, $user_id ); // Prevent edit_user() from wiping out the user's Toolbar on front setting if ( ! isset( $_POST['admin_bar_front'] ) && _get_admin_bar_pref( 'front', $user_id ) ) { $_POST['admin_bar_front'] = 1; } // Bail if errors already exist if ( bbp_has_errors() ) { return; } // Handle user edit $edit_user = edit_user( $user_id ); // Error(s) editng the user, so copy them into the global if ( is_wp_error( $edit_user ) ) { bbpress()->errors = $edit_user; // Successful edit to redirect } elseif ( is_integer( $edit_user ) ) { // Maybe update super admin ability if ( is_multisite() && ! bbp_is_user_home_edit() && current_user_can( 'manage_network_options' ) && is_super_admin() ) { empty( $_POST['super_admin'] ) ? revoke_super_admin( $edit_user ) : grant_super_admin( $edit_user ); } // Redirect $args = array( 'updated' => 'true' ); $user_url = bbp_get_user_profile_edit_url( $edit_user ); $redirect = add_query_arg( $args, $user_url ); bbp_redirect( $redirect ); } } /** * Handles user email address updating from GET requests * * @since 2.6.0 bbPress (r5660) * * @param string $action */ function bbp_user_email_change_handler( $action = '' ) { // Bail if action is not `bbp-update-user-email` if ( 'bbp-update-user-email' !== $action ) { return; } // Bail if not on users own profile if ( ! bbp_is_user_home_edit() ) { return; } // Bail if not attempting to modify user email address if ( empty( $_GET['newuseremail'] ) && empty( $_GET['dismiss'] ) ) { return; } // Get the displayed user ID & option key $user_id = bbp_get_displayed_user_id(); $key = '_new_email'; $redirect_to = bbp_get_user_profile_edit_url( $user_id ); // Execute confirmed email change. if ( ! empty( $_GET['newuseremail'] ) ) { // Check for email address change option $new_email = get_user_meta( $user_id, $key, true ); // Redirect if *no* email address change exists if ( false === $new_email ) { bbp_redirect( $redirect_to ); } // Cleanup & redirect if *invalid* email address change exists if ( empty( $new_email['hash'] ) || empty( $new_email['newemail'] ) ) { delete_user_meta( $user_id, $key ); bbp_redirect( $redirect_to ); } // Compare hashes, and update user if hashes match if ( hash_equals( $new_email['hash'], $_GET['newuseremail'] ) ) { // Does another user have this email address already? if ( email_exists( $new_email['newemail'] ) ) { delete_user_meta( $user_id, $key ); bbp_add_error( 'bbp_user_email_taken', __( 'Error: That email address is already in use.', 'bbpress' ), array( 'form-field' => 'email' ) ); // Email address is good to change to } else { // Create a stdClass (for easy call to wp_update_user()) $user = new stdClass(); $user->ID = $user_id; $user->user_email = esc_html( trim( $new_email['newemail'] ) ); // Attempt to update user email $update_user = wp_update_user( $user ); // Error(s) editing the user, so copy them into the global if ( is_wp_error( $update_user ) ) { bbpress()->errors = $update_user; // All done, so redirect and show the updated message } else { // Update signups table, if signups table & entry exists // For Multisite & BuddyPress compatibility $bbp_db = bbp_db(); if ( ! empty( $bbp_db->signups ) && $bbp_db->get_var( $bbp_db->prepare( "SELECT user_login FROM {$bbp_db->signups} WHERE user_login = %s", bbp_get_displayed_user_field( 'user_login', 'raw' ) ) ) ) { $bbp_db->query( $bbp_db->prepare( "UPDATE {$bbp_db->signups} SET user_email = %s WHERE user_login = %s", $user->user_email, bbp_get_displayed_user_field( 'user_login', 'raw' ) ) ); } delete_user_meta( $user_id, $key ); bbp_redirect( add_query_arg( array( 'updated' => 'true' ), $redirect_to ) ); } } } // Delete new email address from user options } elseif ( ! empty( $_GET['dismiss'] ) && ( "{$user_id}{$key}" === $_GET['dismiss'] ) ) { if ( ! bbp_verify_nonce_request( "dismiss-{$user_id}{$key}" ) ) { bbp_add_error( 'bbp_dismiss_new_email_nonce', __( 'Error: Are you sure you wanted to do that?', 'bbpress' ) ); return; } delete_user_meta( $user_id, $key ); bbp_redirect( $redirect_to ); } } /** * Sends an email when an email address change occurs on POST requests * * @since 2.6.0 bbPress (r5660) * * @see send_confirmation_on_profile_email() */ function bbp_edit_user_email_send_notification( $user_id = 0, $args = array() ) { // Parse args $r = bbp_parse_args( $args, array( 'hash' => '', 'newemail' => '', ) ); // Bail if any relevant parameters are empty if ( empty( $user_id ) || empty( $r['hash'] ) || empty( $r['newemail'] ) ) { bbp_add_error( 'bbp_user_email_invalid_hash', __( 'Error: An error occurred while updating your email address.', 'bbpress' ), array( 'form-field' => 'email' ) ); return; } // Build the nonced URL to dismiss the pending change $user_login = bbp_get_displayed_user_field( 'user_login', 'raw' ); $user_url = bbp_get_user_profile_edit_url( $user_id ); $confirm_url = add_query_arg( array( 'action' => 'bbp-update-user-email', 'newuseremail' => $r['hash'] ), $user_url ); $email_text = __( '%1$s Someone requested a change to the email address on your account. Please click the following link to confirm this change: %2$s If you did not request this, you can safely ignore and delete this notification. This email was sent to: %3$s Regards, The %4$s Team %5$s', 'bbpress' ); /** * Filter the email text sent when a user changes emails. * * The following strings have a special meaning and will get replaced dynamically: * * %1$s - The current user's username * %2$s - The link to click on to confirm the email change * %3$s - The new email * %4$s - The name of the site * %5$s - The URL to the site * * @param string $email_text Text in the email. * @param string $r New user email that the current user has changed to. */ $content = apply_filters( 'bbp_user_email_update_content', $email_text, $r ); // Build the email message $message = sprintf( $content, $user_login, $confirm_url, $r['newemail'], get_site_option( 'site_name' ), network_home_url() ); // Build the email subject $subject = sprintf( __( '[%s] New Email Address', 'bbpress' ), wp_specialchars_decode( get_option( 'blogname' ) ) ); // Send the email wp_mail( $r['newemail'], $subject, $message ); } /** * Conditionally hook the core WordPress output actions to the end of the * default user's edit profile template * * This allows clever plugin authors to conditionally unhook the WordPress core * output actions if they don't want any unexpected junk to appear there, and * also avoids needing to pollute the templates with additional logic and actions. * * @since 2.2.0 bbPress (r4273) */ function bbp_user_edit_after() { $action = bbp_is_user_home_edit() ? 'show_user_profile' : 'edit_user_profile'; do_action( $action, get_userdata( bbp_get_displayed_user_id() ) ); } /** User Queries **************************************************************/ /** * Get the topics that a user created * * @since 2.0.0 bbPress (r2660) * @since 2.6.0 bbPress (r6618) Signature changed to accept an array of arguments * * @param array $args Optional. Arguments to pass into bbp_has_topics() * * @return bool True if user has started topics, otherwise false */ function bbp_get_user_topics_started( $args = array() ) { // Backwards compat for pre-2.6.0 if ( is_numeric( $args ) ) { $args = array( 'author' => bbp_get_user_id( $args, false, false ) ); } // Default arguments $defaults = array( 'author' => bbp_get_displayed_user_id() ); // Parse arguments $r = bbp_parse_args( $args, $defaults, 'get_user_topics_started' ); // Get the topics $query = bbp_has_topics( $r ); $user_id = $r['author']; // Filter & return return apply_filters( 'bbp_get_user_topics_started', $query, $user_id, $r, $args ); } /** * Get the replies that a user created * * @since 2.2.0 bbPress (r4225) * @since 2.6.0 bbPress (r6618) Signature changed to accept an array of arguments * * @param array $args Optional. Arguments to pass into bbp_has_replies() * * @return bool True if user has created replies, otherwise false */ function bbp_get_user_replies_created( $args = array() ) { // Backwards compat for pre-2.6.0 if ( is_numeric( $args ) ) { $args = array( 'author' => bbp_get_user_id( $args, false, false ), 'post_type' => bbp_get_reply_post_type(), 'order' => 'DESC' ); } // Default arguments $defaults = array( 'author' => bbp_get_displayed_user_id(), 'post_type' => bbp_get_reply_post_type(), 'order' => 'DESC' ); // Parse arguments $r = bbp_parse_args( $args, $defaults, 'get_user_replies_created' ); // Get the replies $query = bbp_has_replies( $r ); $user_id = $r['author']; // Filter & return return apply_filters( 'bbp_get_user_replies_created', $query, $user_id, $r, $args ); } /** * Get user IDs from nicenames * * This function is primarily used when saving object moderators * * @since 2.6.0 bbPress * * @param mixed $user_nicenames * @return array */ function bbp_get_user_ids_from_nicenames( $user_nicenames = array() ) { // Default value $retval = array(); // Only query if nicenames if ( ! empty( $user_nicenames ) ) { // Maybe explode by comma $user_nicenames = ( is_string( $user_nicenames ) && strstr( $user_nicenames, ',' ) ) ? explode( ',', $user_nicenames ) : (array) $user_nicenames; // Sanitize each nicename in the array $user_nicenames = array_map( 'sanitize_title', $user_nicenames ); // Get users $users = get_users( array( 'nicename__in' => $user_nicenames ) ); // Pluck or empty if ( ! empty( $users ) ) { $retval = wp_list_pluck( $users, 'ID' ); } } // Filter & return return (array) apply_filters( 'bbp_get_user_ids_from_nicenames', $retval, $user_nicenames ); } /** * Get user nicenames from IDs * * This function is primarily used when saving object moderators * * @since 2.6.0 bbPress * * @param mixed $user_ids * @return array */ function bbp_get_user_nicenames_from_ids( $user_ids = array() ) { // Default value $retval = array(); // Only query if nicenames if ( ! empty( $user_ids ) ) { // Get users $users = get_users( array( 'include' => $user_ids ) ); // Pluck or empty if ( ! empty( $users ) ) { $retval = wp_list_pluck( $users, 'user_nicename' ); } } // Filter & return return (array) apply_filters( 'bbp_get_user_nicenames_from_ids', $retval, $user_ids ); } /** Post Counts ***************************************************************/ /** * Return the raw database count of topics by a user * * @since 2.1.0 bbPress (r3633) * * @param int $user_id User ID to get count for * * @return int Raw DB count of topics */ function bbp_get_user_topic_count_raw( $user_id = 0 ) { $user_id = bbp_get_user_id( $user_id ); $bbp_db = bbp_db(); $statii = "'" . implode( "', '", bbp_get_public_topic_statuses() ) . "'"; $sql = "SELECT COUNT(*) FROM {$bbp_db->posts} WHERE post_author = %d AND post_type = %s AND post_status IN ({$statii})"; $query = $bbp_db->prepare( $sql, $user_id, bbp_get_topic_post_type() ); $count = (int) $bbp_db->get_var( $query ); // Filter & return return (int) apply_filters( 'bbp_get_user_topic_count_raw', $count, $user_id ); } /** * Return the raw database count of replies by a user * * @since 2.1.0 bbPress (r3633) * * @param int $user_id User ID to get count for * * @return int Raw DB count of replies */ function bbp_get_user_reply_count_raw( $user_id = 0 ) { $user_id = bbp_get_user_id( $user_id ); $bbp_db = bbp_db(); $statii = "'" . implode( "', '", bbp_get_public_reply_statuses() ) . "'"; $sql = "SELECT COUNT(*) FROM {$bbp_db->posts} WHERE post_author = %d AND post_type = %s AND post_status IN ({$statii})"; $query = $bbp_db->prepare( $sql, $user_id, bbp_get_reply_post_type() ); $count = (int) $bbp_db->get_var( $query ); // Filter & return return (int) apply_filters( 'bbp_get_user_reply_count_raw', $count, $user_id ); } /** * Bump the topic count for a user by a certain amount. * * @since 2.6.0 bbPress (r5309) * * @param int $user_id * @param int $difference */ function bbp_bump_user_topic_count( $user_id = 0, $difference = 1 ) { // Bail if no bump if ( empty( $difference ) ) { return false; } // Validate user ID $user_id = bbp_get_user_id( $user_id ); if ( empty( $user_id ) ) { return false; } // Check meta for count, or query directly if not found $count = bbp_get_user_topic_count( $user_id, true ); if ( empty( $count ) ) { $count = bbp_get_user_topic_count_raw( $user_id ); } $difference = (int) $difference; $user_topic_count = (int) ( $count + $difference ); // Add them up and filter them $new_count = (int) apply_filters( 'bbp_bump_user_topic_count', $user_topic_count, $user_id, $difference, $count ); return bbp_update_user_topic_count( $user_id, $new_count ); } /** * Bump the reply count for a user by a certain amount. * * @since 2.6.0 bbPress (r5309) * * @param int $user_id * @param int $difference */ function bbp_bump_user_reply_count( $user_id = 0, $difference = 1 ) { // Bail if no bump if ( empty( $difference ) ) { return false; } // Validate user ID $user_id = bbp_get_user_id( $user_id ); if ( empty( $user_id ) ) { return false; } // Check meta for count, or query directly if not found $count = bbp_get_user_reply_count( $user_id, true ); if ( empty( $count ) ) { $count = bbp_get_user_reply_count_raw( $user_id ); } $difference = (int) $difference; $user_reply_count = (int) ( $count + $difference ); // Add them up and filter them $new_count = (int) apply_filters( 'bbp_bump_user_reply_count', $user_reply_count, $user_id, $difference, $count ); return bbp_update_user_reply_count( $user_id, $new_count ); } /** * Helper function used to increase (by one) the count of topics for a user when * a topic is published. * * @since 2.6.0 bbPress (r5309) * * @access * @param $topic_id * @param $forum_id * @param $anonymous_data * @param $topic_author */ function bbp_increase_user_topic_count( $topic_id = 0 ) { $user_id = bbp_get_topic_author_id( $topic_id ); return bbp_bump_user_topic_count( $user_id, 1 ); } /** * Helper function used to increase (by one) the count of replies for a user when * a reply is published. * * This is a helper function, hooked to `bbp_new_reply` * * @since 2.6.0 bbPress (r5309) * * @param $topic_id * @param $forum_id * @param $anonymous_data * @param $topic_author */ function bbp_increase_user_reply_count( $reply_id = 0 ) { $user_id = bbp_get_reply_author_id( $reply_id ); return bbp_bump_user_reply_count( $user_id, 1 ); } /** * Helper function used to decrease (by one) the count of topics for a user when * a topic is unpublished. * * @since 2.6.0 bbPress (r5309) * * @param $topic_id */ function bbp_decrease_user_topic_count( $topic_id = 0 ) { $user_id = bbp_get_topic_author_id( $topic_id ); return bbp_bump_user_topic_count( $user_id, -1 ); } /** * Helper function used to increase (by one) the count of replies for a user when * a topic is unpublished. * * @since 2.6.0 bbPress (r5309) * * @param $reply_id */ function bbp_decrease_user_reply_count( $reply_id = 0 ) { $user_id = bbp_get_reply_author_id( $reply_id ); return bbp_bump_user_reply_count( $user_id, -1 ); } /** Permissions ***************************************************************/ /** * Redirect if unauthorized user is attempting to edit another user * * This is hooked to 'bbp_template_redirect' and controls the conditions under * which a user can edit another user (or themselves.) If these conditions are * met, we assume a user cannot perform this task, and look for ways they can * earn the ability to access this template. * * @since 2.1.0 bbPress (r3605) */ function bbp_check_user_edit() { // Bail if not editing a user if ( ! bbp_is_single_user_edit() ) { return; } // Default to false $redirect = true; $user_id = bbp_get_displayed_user_id(); // Allow user to edit their own profile if ( bbp_is_user_home_edit() ) { $redirect = false; // Allow if current user can edit the displayed user } elseif ( current_user_can( 'edit_user', $user_id ) ) { $redirect = false; // Allow if user can manage network users, or edit-any is enabled } elseif ( current_user_can( 'manage_network_users' ) || apply_filters( 'enable_edit_any_user_configuration', false ) ) { $redirect = false; } // Allow conclusion to be overridden $redirect = (bool) apply_filters( 'bbp_check_user_edit', $redirect, $user_id ); // Bail if not redirecting if ( false === $redirect ) { return; } // Filter redirect URL $profile_url = bbp_get_user_profile_url( $user_id ); $redirect_to = apply_filters( 'bbp_check_user_edit_redirect_to', $profile_url, $user_id ); // Redirect bbp_redirect( $redirect_to ); } /** * Check if a user is blocked, or cannot spectate the forums. * * @since 2.0.0 bbPress (r2996) */ function bbp_forum_enforce_blocked() { // Bail if not logged in or keymaster if ( ! is_user_logged_in() || bbp_is_user_keymaster() ) { return; } // Set 404 if in bbPress and user cannot spectate if ( is_bbpress() && ! current_user_can( 'spectate' ) ) { bbp_set_404(); } } /** Sanitization **************************************************************/ /** * Sanitize displayed user data, when viewing and editing any user. * * This somewhat monolithic function handles the escaping and sanitization of * user data for a bbPress profile. There are two reasons this all happens here: * * 1. bbPress took a similar approach to WordPress, and funnels all user profile * data through a central helper. This eventually calls sanitize_user_field() * which applies a few context based filters, which some third party plugins * might be relying on bbPress to play nicely with. * * 2. Early versions of bbPress 2.x templates did not escape this data meaning * a backwards compatible approach like this one was necessary to protect * existing installations that may have custom template parts. * * @since 2.6.0 bbPress (r5368) * * @param string $value * @param string $field * @param string $context * @return string */ function bbp_sanitize_displayed_user_field( $value = '', $field = '', $context = 'display' ) { // Bail if not editing or displaying (maybe we'll do more here later) if ( ! in_array( $context, array( 'edit', 'display' ), true ) ) { return $value; } // By default, no filter set (consider making this an array later) $filter = false; // Big switch statement to decide which user field we're sanitizing and how switch ( $field ) { // Description is a paragraph case 'description' : $filter = ( 'edit' === $context ) ? '' : 'wp_kses_data'; break; // Email addresses are sanitized with a specific function case 'user_email' : $filter = 'sanitize_email'; break; // Name & login fields case 'user_login' : case 'display_name' : case 'first_name' : case 'last_name' : case 'nick_name' : $filter = ( 'edit' === $context ) ? 'esc_attr' : 'esc_html'; break; // wp-includes/default-filters.php escapes this for us via esc_url() case 'user_url' : break; } // Run any applicable filters on the value if ( ! empty( $filter ) ) { $value = call_user_func( $filter, $value ); } return $value; } /** Converter *****************************************************************/ /** * Convert passwords from previous platform encryption to WordPress encryption. * * @since 2.1.0 bbPress (r3813) * @since 2.6.10 bbPress (r7244) Switched from direct query to get_user_by() */ function bbp_user_maybe_convert_pass() { // Sanitize login $login = ! empty( $_POST['log'] ) ? sanitize_user( wp_unslash( $_POST['log'] ) ) : ''; // Sanitize password $pass = ! empty( $_POST['pwd'] ) ? trim( $_POST['pwd'] ) : ''; // Bail if no username or password if ( empty( $login ) || empty( $pass ) ) { return; } // Get user by login... $user = get_user_by( 'login', $login ); // ...or get user by email if ( empty( $user ) && strpos( $login, '@' ) ) { $user = get_user_by( 'email', $login ); } // Bail if no user if ( empty( $user ) ) { return; } // Get converter class from usermeta $class = get_user_meta( $user->ID, '_bbp_class', true ); // Bail if no converter class in meta if ( empty( $class ) || ! is_string( $class ) ) { return; } // Setup the converter bbp_setup_converter(); // Try to instantiate the converter class $converter = bbp_new_converter( $class ); // Bail if no converter if ( empty( $converter ) ) { return; } // Try to call the password conversion callback method if ( ( $converter instanceof BBP_Converter_Base ) && method_exists( $converter, 'callback_pass' ) ) { $converter->callback_pass( $login, $pass ); } } Флагман Казино Зеркало – FSConsulting https://www.fsconsulting.com.co Contadores Fri, 04 Apr 2025 15:31:15 +0000 es hourly 1 https://wordpress.org/?v=6.9.1 https://www.fsconsulting.com.co/wp-content/uploads/2022/05/cropped-logosolo-32x32.png Флагман Казино Зеркало – FSConsulting https://www.fsconsulting.com.co 32 32 Джеттон Казино (Jetton Casino) — лучшее онлайн-казино 2025 🎰 https://www.fsconsulting.com.co/%d0%b4%d0%b6%d0%b5%d1%82%d1%82%d0%be%d0%bd-%d0%ba%d0%b0%d0%b7%d0%b8%d0%bd%d0%be-jetton-casino-%d0%bb%d1%83%d1%87%d1%88%d0%b5%d0%b5-%d0%be%d0%bd%d0%bb%d0%b0%d0%b9%d0%bd-%d0%ba%d0%b0%d0%b7/ https://www.fsconsulting.com.co/%d0%b4%d0%b6%d0%b5%d1%82%d1%82%d0%be%d0%bd-%d0%ba%d0%b0%d0%b7%d0%b8%d0%bd%d0%be-jetton-casino-%d0%bb%d1%83%d1%87%d1%88%d0%b5%d0%b5-%d0%be%d0%bd%d0%bb%d0%b0%d0%b9%d0%bd-%d0%ba%d0%b0%d0%b7/#respond Fri, 04 Apr 2025 15:31:15 +0000 https://www.fsconsulting.com.co/?p=27497

Джеттон Казино (Jetton Casino) — азартное приключение 2025

Добро пожаловать в Казино Jetton 2025 — современную игровую платформу, где каждый найдет для себя что-то интересное. Здесь представлены лучшие слоты, привлекательные бонусы, удобная мобильная версия и надежные методы оплаты. Если вы ищете качественный азартный контент с гарантированной безопасностью, то Jetton Casino — ваш идеальный выбор!

В этом обзоре мы рассмотрим все особенности Казино Jetton: ассортимент игр, доступные бонусы, мобильные решения, лицензирование и безопасность. А также расскажем, как зарегистрироваться, войти в личный кабинет и начать выигрывать!

Игровые автоматы и слоты в Jetton Casino

Одним из главных преимуществ Джеттон Казино является богатый выбор игровых автоматов. Здесь представлены сотни слотов от ведущих провайдеров, таких как NetEnt, Microgaming, Play’n GO, Yggdrasil и других. Игроки могут наслаждаться классическими фруктовыми слотами, современными 3D-автоматами, а также играми с прогрессивными джекпотами. Благодаря высокому RTP и разнообразию механик каждый спин может принести удачу!

В Jetton Casino можно выбрать слоты с различными бонусными функциями: фриспины, множители, респины и особые режимы. Также доступны настольные игры, включая рулетку, блэкджек и покер, а для любителей реалистичного геймплея есть лайв-казино с живыми дилерами.

Бонусы и акции для игроков

Джеттон Казино предлагает своим пользователям щедрые бонусы, начиная с момента регистрации. Новый игрок может получить приветственный пакет, включающий бонус на первый депозит и бесплатные вращения. Также доступны еженедельные акции, кэшбэки и персональные предложения для активных пользователей.

Программа лояльности в Казино Jetton позволяет получать дополнительные награды за игру. Чем больше ставок делает пользователь, тем выше его статус, а значит — тем больше бонусов и привилегий он получает. Не забывайте проверять раздел акций, чтобы не упустить выгодные предложения!

Мобильная версия и приложение

Играть в Jetton Casino можно не только на ПК, но и на мобильных устройствах. Благодаря адаптивному сайту казино отлично работает на смартфонах и планшетах, сохраняя весь функционал десктопной версии. Игроки могут свободно запускать любимые игры, пополнять баланс, выводить выигрыши и участвовать в акциях.

Для максимального удобства предусмотрено мобильное приложение Jetton Casino, которое доступно для Android и iOS. Программа позволяет быстро входить в аккаунт, избегая возможных блокировок и необходимости использовать зеркала. Если вы предпочитаете играть в движении, скачайте приложение и наслаждайтесь азартом в любое время!

Безопасность, лицензия и гарантии выплат

Джеттон Казино работает по официальной лицензии, что гарантирует прозрачность и надежность всех операций. Игровая платформа использует современные протоколы шифрования данных, обеспечивая защиту информации пользователей. Все финансовые транзакции проходят через проверенные платежные системы, что делает процесс ввода и вывода средств быстрым и безопасным.

Контроль честности слотов обеспечивается сертифицированными генераторами случайных чисел, а выплаты выигрышей осуществляются в кратчайшие сроки. Казино Jetton уделяет большое внимание ответственности в игре, предлагая пользователям инструменты самоконтроля и лимиты на депозиты.

Как зарегистрироваться и войти в аккаунт

Чтобы начать играть в Jetton Casino, необходимо пройти простую регистрацию. Для этого достаточно указать e-mail, придумать пароль и выбрать валюту счета. После подтверждения данных можно пополнить баланс и приступить к игре. Вход в аккаунт осуществляется через логин и пароль, а при необходимости можно восстановить доступ с помощью электронной почты.

Джеттон Казино предлагает удобные способы пополнения счета и вывода выигрышей, включая банковские карты, электронные кошельки и криптовалюту. Минимальный депозит позволяет начать игру с небольших ставок, а лимиты на вывод зависят от выбранного платежного метода.

]]>
https://www.fsconsulting.com.co/%d0%b4%d0%b6%d0%b5%d1%82%d1%82%d0%be%d0%bd-%d0%ba%d0%b0%d0%b7%d0%b8%d0%bd%d0%be-jetton-casino-%d0%bb%d1%83%d1%87%d1%88%d0%b5%d0%b5-%d0%be%d0%bd%d0%bb%d0%b0%d0%b9%d0%bd-%d0%ba%d0%b0%d0%b7/feed/ 0
Legacy of Dead slot machine play the slot for free in the casino Flagman https://www.fsconsulting.com.co/legacy-of-dead-slot-machine-play-the-slot-for-free-in-the-casino-flagman/ https://www.fsconsulting.com.co/legacy-of-dead-slot-machine-play-the-slot-for-free-in-the-casino-flagman/#respond Thu, 06 Mar 2025 18:03:41 +0000 https://www.fsconsulting.com.co/?p=22089

Immersion in the world Legacy of Dead – Play the popular slot for free in the casino Flagman

Welcome into a fascinating journey, where every moment can be the beginning of an incredible adventure. In this section, we offer you the opportunity to experience an amazing entertainment that lures with its original style and exciting functions. Here everyone will find something for themselves, enjoying bright moments and chances of victory.

Immersion in the world of exciting content allows you to get acquainted with the unique features and opportunities that this virtual platform gives. Explore exciting scenarios and enjoy game mechanics that attracts attention with its dynamism and diversity.

Take advantage of the chance to enjoy these entertainment in a convenient form, which opens access to exciting scenarios and pleasant surprises. Trust intuition and get ready for the exciting moments that are waiting for you ahead.

Slot machine Legacy of Dead – play for free

Virtual entertainment related to ancient topics and wealth attract the attention of many players. The project has gained particular popularity, which offers immersion in the exciting world of adventure and mysticism. The ability to try this exciting game without risk allows everyone to get acquainted with the features and functions without financial investments.

Advantages of this approach obvious: players can easily master the gameplay, study the rules and strategies without spending their own means. This gives a chance to best understand what mechanisms and bonus functions are included in the game and make a decision on further participation.

It is also worth noting that the safe testing of entertainment content allows you to avoid disappointments and improve overall experience. Registered on platforms offering such conditions, you will have the opportunity to enjoy entertainment without any obligation.

Legacy of Dead slot overview and its features

This game It offers players to go on a trip to the world of ancient civilizations, where every detail of the design and symbolism contributes to the atmosphere of adventure. Graphics and sound create an unforgettable feeling of immersion, and the mechanics of the game provide many opportunities for winning and active participation.

Features of this game include unique characters and bonus functions that make the process more interesting and dynamic. Each element in the game is carefully worked out to maintain a high degree of involvement and offer the players a fascinating and rich experience.

Where to play on Legacy of Dead without costs

If you want to experience luck and enjoy the exciting gameplay without the need to invest your own funds, there are several options that may interest you. In modern online platforms, opportunities for playing virtual loans are often provided, which allows you to enjoy the process without real financial costs.

Here are some popular ways how to access such entertainment without risk for the wallet:

  • Online platforms with demo mode: Many sites offer demonstration versions that allow you to play without the need to replenish the score. These versions often completely repeat the functionality of paid games, providing all the same opportunities for winning.
  • Promotions and bonuses: Some platforms hold shares providing free loans or rotations. Such proposals are often designed to attract new users and allow you to try out the game without personal investments.
  • Game applications with free modes: Mobile applications can be available versions with virtual rates. This is a great opportunity for entertainment on the go that does not require money expenses.

Following these recommendations, you can find the best options for a pleasant and Флагман Казино safe gaming process without the need to use your own means.

Casino Flagman: the perfect place for the game

In this entertainment institution you will find everything you need for a fascinating pastime. The place is characterized by high quality service and a wide selection of entertainment that will satisfy any preferences. Here everyone will be able to enjoy the game, regardless of the level of experience, thanks to the variety of accessible options and innovative solutions offered by users.

One of the main advantages of the establishment is the ability to try various entertainment without the need to make real rates. This gives a chance to get acquainted with gaming processes and strategies, without risking its own means. It is also worth noting a convenient interface and access to the last new products of the industry, which makes the stay in this place especially comfortable and exciting.

The situation and atmosphere of the institution contribute to the creation of a pleasant and relaxing environment. They care about each client, offering not only high -quality entertainment, but also an attentive attitude. This place will be a great choice for those who are looking not only to have fun, but also to spend time with comfort and pleasure.

]]>
https://www.fsconsulting.com.co/legacy-of-dead-slot-machine-play-the-slot-for-free-in-the-casino-flagman/feed/ 0
Лучшие мобильные казино для iPhone на iOS в 2025 году https://www.fsconsulting.com.co/%d0%bb%d1%83%d1%87%d1%88%d0%b8%d0%b5-%d0%bc%d0%be%d0%b1%d0%b8%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d0%ba%d0%b0%d0%b7%d0%b8%d0%bd%d0%be-%d0%b4%d0%bb%d1%8f-iphone-%d0%bd%d0%b0-ios-%d0%b2-2025-%d0%b3%d0%be/ https://www.fsconsulting.com.co/%d0%bb%d1%83%d1%87%d1%88%d0%b8%d0%b5-%d0%bc%d0%be%d0%b1%d0%b8%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d0%ba%d0%b0%d0%b7%d0%b8%d0%bd%d0%be-%d0%b4%d0%bb%d1%8f-iphone-%d0%bd%d0%b0-ios-%d0%b2-2025-%d0%b3%d0%be/#respond Wed, 05 Mar 2025 09:18:52 +0000 https://www.fsconsulting.com.co/?p=21964

Лучшие мобильные казино для iPhone на iOS в 2025 году

Мобильные казино на iPhone становятся все более популярными, благодаря своей удобной и интуитивно понятной платформе. В 2025 году владельцы iPhone могут наслаждаться азартными играми в любое время и в любом месте, Флагман Казино Бонус используя приложения, оптимизированные для iOS. Эти приложения предоставляют широкий выбор игр, отличные бонусы и безопасную игровую среду.

Рынок мобильных казино постоянно развивается, и важно знать, какие из них предлагают лучшие условия для игры. Мы подготовили обзор лучших казино для iPhone на iOS в 2025 году, чтобы помочь вам выбрать подходящее приложение для безопасной и увлекательной игры. Независимо от того, являетесь ли вы новичком или опытным игроком, в этой статье вы найдете советы и рекомендации, которые помогут сделать правильный выбор.

В этом списке мы рассмотрим казино, которые предлагают не только разнообразие игр, но и бонусы, поддержку игроков и безопасность. Все эти аспекты играют ключевую роль при выборе казино, ведь важно не только весело проводить время, но и быть уверенным в честности и надежности выбранной платформы.

Лучшие мобильные казино для iPhone на iOS в 2025 году

С каждым годом мобильные казино становятся все более популярными среди пользователей iPhone. В 2025 году владельцы устройств Apple могут наслаждаться не только удобством, но и качественным игровым процессом благодаря улучшенным приложениям, которые обеспечивают плавную работу на платформе iOS. В этой статье мы рассмотрим топ казино, которые предлагают лучшие возможности для игроков в 2025 году.

1. Казино X – это один из лидеров на рынке мобильных казино для iPhone. Казино X предлагает игрокам широкий выбор слотов, настольных игр и живого казино, все оптимизировано для мобильных устройств. Приложение работает стабильно, а интерфейс интуитивно понятен, что делает игру комфортной на любом устройстве Apple.

2. Казино Y – известное своими щедрыми бонусами и эксклюзивными акциями для мобильных игроков. Приложение для iPhone предлагает уникальные возможности для ставок и участия в турнирах. Все игры быстро загружаются и прекрасно отображаются на экранах iPhone, обеспечивая отличное качество графики и звук.

3. Казино Z – отличающееся надежностью и отличной службой поддержки. Это казино предлагает отличные условия для начинающих и опытных игроков. Казино Z всегда соблюдает высокие стандарты безопасности, что позволяет игрокам чувствовать себя уверенно и безопасно.

Выбирая мобильное казино для iPhone, важно обратить внимание на такие аспекты, как надежность, безопасность, Флагман Казино Бонус ассортимент игр и бонусные предложения. В 2025 году эти казино представляют собой идеальные варианты для тех, кто ищет качественное и безопасное развлечение в мобильной версии.

Обзор топовых мобильных казино для iPhone в 2025 году

1. Казино A – один из лучших вариантов для игроков на iPhone в 2025 году. Это казино впечатляет широким выбором игровых автоматов, а также настольных игр, которые идеально адаптированы для мобильных устройств. Плавная работа приложения и быстрые загрузки делают игровой процесс максимально комфортным. Казино A также привлекает игроков щедрыми бонусами и акциями, что выгодно отличает его от конкурентов.

2. Казино B – отличное мобильное казино для любителей азарта на iPhone. Здесь представлено множество популярных слотов, а также живое казино с профессиональными дилерами. Приложение работает без сбоев и поддерживает все последние обновления iOS, обеспечивая стабильную работу на самых новых моделях iPhone. Казино B также радует выгодными бонусами и отличной службой поддержки.

3. Казино C – это мобильное казино, которое выделяется своим удобным и простым интерфейсом. Игры на платформе адаптированы для удобного взаимодействия с сенсорным экраном iPhone. Кроме того, казино C предлагает большое количество промо-акций и турнирных возможностей, что привлекает игроков со всего мира. Все транзакции защищены высококачественным шифрованием, что гарантирует безопасность данных и финансов.

Каждое из этих казино предлагает уникальные возможности для мобильных игроков на iPhone в 2025 году. Выбирая подходящее казино, важно учитывать такие критерии, как ассортимент игр, безопасность и бонусные предложения, что позволяет обеспечить лучший опыт для мобильных пользователей.

Как выбрать безопасное казино для iPhone на платформе iOS

1. Лицензия и регулирование – важно, чтобы казино имело действующую лицензию от авторитетного игрового органа. Лицензированные платформы придерживаются строгих стандартов безопасности и честности игр, что гарантирует игрокам защиту их прав.

2. Защита личных данных – убедитесь, что казино использует современные методы шифрования данных, такие как SSL-сертификаты. Это предотвращает утечку личной информации и защитит финансовые транзакции от несанкционированного доступа.

3. Прозрачность и честность игр – надежное казино должно предоставлять игрокам доступ к информации о генераторах случайных чисел (RNG) и результатах аудитов. Некоторые казино также публикуют отчеты о проверке честности игр независимыми экспертами.

5. Отзывы и репутация – перед регистрацией всегда проверяйте отзывы других игроков. Платформы с положительной репутацией имеют множество положительных комментариев, а также отвечают на жалобы пользователей. Это подтверждает их надежность и безопасность.

Выбирая мобильное казино для iPhone, следует обратить внимание на эти ключевые моменты, чтобы обеспечить свою безопасность и комфорт в процессе игры.

]]>
https://www.fsconsulting.com.co/%d0%bb%d1%83%d1%87%d1%88%d0%b8%d0%b5-%d0%bc%d0%be%d0%b1%d0%b8%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d0%ba%d0%b0%d0%b7%d0%b8%d0%bd%d0%be-%d0%b4%d0%bb%d1%8f-iphone-%d0%bd%d0%b0-ios-%d0%b2-2025-%d0%b3%d0%be/feed/ 0