false, 'name' => false, 'description' => '', 'can_delete' => true, ), 'xprofile_insert_field_group' ); // Bail if no group name. if ( empty( $r['name'] ) ) { return false; } // Create new field group object, maybe using an existing ID. $field_group = new BP_XProfile_Group( $r['field_group_id'] ); $field_group->name = $r['name']; $field_group->description = $r['description']; $field_group->can_delete = $r['can_delete']; return $field_group->save(); } /** * Get a specific profile field group. * * @since 1.0.0 * * @param int $field_group_id Field group ID to fetch. * @return false|BP_XProfile_Group */ function xprofile_get_field_group( $field_group_id = 0 ) { // Try to get a specific field group by ID. $field_group = new BP_XProfile_Group( $field_group_id ); // Bail if group was not found. if ( empty( $field_group->id ) ) { return false; } // Return field group. return $field_group; } /** * Delete a specific profile field group. * * @since 1.0.0 * * @param int $field_group_id Field group ID to delete. * @return boolean */ function xprofile_delete_field_group( $field_group_id = 0 ) { // Try to get a specific field group by ID. $field_group = xprofile_get_field_group( $field_group_id ); // Bail if group was not found. if ( false === $field_group ) { return false; } // Return the results of trying to delete the field group. return $field_group->delete(); } /** * Update the position of a specific profile field group. * * @since 1.0.0 * * @param int $field_group_id Field group ID to update. * @param int $position Field group position to update to. * @return boolean */ function xprofile_update_field_group_position( $field_group_id = 0, $position = 0 ) { return BP_XProfile_Group::update_position( $field_group_id, $position ); } /*** Field Management *********************************************************/ /** * Get details of all xprofile field types. * * @since 2.0.0 * * @return array Key/value pairs (field type => class name). */ function bp_xprofile_get_field_types() { $fields = array( 'checkbox' => 'BP_XProfile_Field_Type_Checkbox', 'datebox' => 'BP_XProfile_Field_Type_Datebox', 'multiselectbox' => 'BP_XProfile_Field_Type_Multiselectbox', 'number' => 'BP_XProfile_Field_Type_Number', 'url' => 'BP_XProfile_Field_Type_URL', 'radio' => 'BP_XProfile_Field_Type_Radiobutton', 'selectbox' => 'BP_XProfile_Field_Type_Selectbox', 'textarea' => 'BP_XProfile_Field_Type_Textarea', 'textbox' => 'BP_XProfile_Field_Type_Textbox', 'telephone' => 'BP_XProfile_Field_Type_Telephone', 'wp-biography' => 'BP_XProfile_Field_Type_WordPress_Biography', 'wp-textbox' => 'BP_XProfile_Field_Type_WordPress_Textbox', 'checkbox_acceptance' => 'BP_XProfile_Field_Type_Checkbox_Acceptance', ); /** * Filters the list of all xprofile field types. * * If you've added a custom field type in a plugin, register it with this filter. * * @since 2.0.0 * * @param array $fields Array of field type/class name pairings. */ return apply_filters( 'bp_xprofile_get_field_types', $fields ); } /** * Creates the specified field type object; used for validation and templating. * * @since 2.0.0 * * @param string $type Type of profile field to create. See {@link bp_xprofile_get_field_types()} for default core values. * @return object $value If field type unknown, returns BP_XProfile_Field_Type_Textarea. * Otherwise returns an instance of the relevant child class of BP_XProfile_Field_Type. */ function bp_xprofile_create_field_type( $type ) { $field = bp_xprofile_get_field_types(); $class = isset( $field[$type] ) ? $field[$type] : ''; /** * To handle (missing) field types, fallback to a placeholder field object if a type is unknown. */ if ( $class && class_exists( $class ) ) { return new $class; } else { return new BP_XProfile_Field_Type_Placeholder; } } /** * Insert or update an xprofile field. * * @since 1.1.0 * * @param array|string $args { * Array of arguments. * @type int $field_id Optional. Pass the ID of an existing field to edit that field. * @type int $field_group_id ID of the associated field group. * @type int $parent_id Optional. ID of the parent field. * @type string $type Field type. Checked against a list of allowed field_types. * @type string $name Name of the new field. * @type string $description Optional. Descriptive text for the field. * @type bool $is_required Optional. Whether users must provide a value for the field. Default: false. * @type bool $can_delete Optional. Whether admins can delete this field in the Dashboard interface. * Generally this is false only for the Name field, which is required throughout BP. * Default: true. * @type string $order_by Optional. For field types that support options (such as 'radio'), this flag * determines whether the sort order of the options will be 'default' * (order created) or 'custom'. * @type bool $is_default_option Optional. For the 'option' field type, setting this value to true means that * it'll be the default value for the parent field when the user has not yet * overridden. Default: true. * @type int $option_order Optional. For the 'option' field type, this determines the order in which the * options appear. * } * @return bool|int False on failure, ID of new field on success. */ function xprofile_insert_field( $args = '' ) { $r = bp_parse_args( $args, array( 'field_id' => null, 'field_group_id' => null, 'parent_id' => null, 'type' => '', 'name' => '', 'description' => '', 'is_required' => false, 'can_delete' => true, 'order_by' => '', 'is_default_option' => false, 'option_order' => null, 'field_order' => null, ) ); // Field_group_id is required. if ( empty( $r['field_group_id'] ) ) { return false; } // Check this is a non-empty, valid field type. if ( ! in_array( $r['type'], (array) buddypress()->profile->field_types ) ) { return false; } // Instantiate a new field object. if ( ! empty( $r['field_id'] ) ) { $field = xprofile_get_field( $r['field_id'], null, false ); } else { $field = new BP_XProfile_Field; } $field->group_id = $r['field_group_id']; $field->type = $r['type']; // The 'name' field cannot be empty. if ( ! empty( $r['name'] ) ) { $field->name = $r['name']; } $field->description = $r['description']; $field->order_by = $r['order_by']; $field->parent_id = (int) $r['parent_id']; $field->field_order = (int) $r['field_order']; $field->option_order = (int) $r['option_order']; $field->is_required = (bool) $r['is_required']; $field->can_delete = (bool) $r['can_delete']; $field->is_default_option = (bool) $r['is_default_option']; return $field->save(); } /** * Get a profile field object. * * @since 1.1.0 * @since 2.8.0 Added `$user_id` and `$get_data` parameters. * * @param int|object $field ID of the field or object representing field data. * @param int|null $user_id Optional. ID of the user associated with the field. * Ignored if `$get_data` is false. If `$get_data` is * true, but no `$user_id` is provided, defaults to * logged-in user ID. * @param bool $get_data Whether to fetch data for the specified `$user_id`. * @return BP_XProfile_Field|null Field object if found, otherwise null. */ function xprofile_get_field( $field, $user_id = null, $get_data = true ) { if ( $field instanceof BP_XProfile_Field ) { $_field = $field; } elseif ( is_object( $field ) ) { $_field = new BP_XProfile_Field(); $_field->fill_data( $field ); } else { $_field = BP_XProfile_Field::get_instance( $field, $user_id, $get_data ); } if ( ! $_field ) { return null; } return $_field; } /** * Get a profile Field Type object. * * @since 8.0.0 * * @param int $field_id ID of the field. * @return BP_XProfile_Field_Type|null Field Type object if found, otherwise null. */ function bp_xprofile_get_field_type( $field_id ) { $field_type = null; $field = xprofile_get_field( $field_id, null, false ); if ( $field instanceof BP_XProfile_Field ) { $field_type = $field->type_obj; } return $field_type; } /** * Delete a profile field object. * * @since 1.1.0 * * @param int|object $field_id ID of the field or object representing field data. * @return bool Whether or not the field was deleted. */ function xprofile_delete_field( $field_id ) { $field = new BP_XProfile_Field( $field_id ); return $field->delete(); } /*** Field Data Management *****************************************************/ /** * Fetches profile data for a specific field for the user. * * When the field value is serialized, this function unserializes and filters * each item in the array. * * @since 1.0.0 * * @param mixed $field The ID of the field, or the $name of the field. * @param int $user_id The ID of the user. * @param string $multi_format How should array data be returned? 'comma' if you want a * comma-separated string; 'array' if you want an array. * @return mixed The profile field data. */ function xprofile_get_field_data( $field, $user_id = 0, $multi_format = 'array' ) { if ( empty( $user_id ) ) { $user_id = bp_displayed_user_id(); } if ( empty( $user_id ) ) { return false; } if ( is_numeric( $field ) ) { $field_id = $field; } else { $field_id = xprofile_get_field_id_from_name( $field ); } if ( empty( $field_id ) ) { return false; } $values = maybe_unserialize( BP_XProfile_ProfileData::get_value_byid( $field_id, $user_id ) ); if ( is_array( $values ) ) { $data = array(); foreach( (array) $values as $value ) { /** * Filters the field data value for a specific field for the user. * * @since 1.0.0 * * @param string $value Value saved for the field. * @param int $field_id ID of the field being displayed. * @param int $user_id ID of the user being displayed. */ $data[] = apply_filters( 'xprofile_get_field_data', $value, $field_id, $user_id ); } if ( 'comma' == $multi_format ) { $data = implode( ', ', $data ); } } else { /** This filter is documented in bp-xprofile/bp-xprofile-functions.php */ $data = apply_filters( 'xprofile_get_field_data', $values, $field_id, $user_id ); } return $data; } /** * A simple function to set profile data for a specific field for a specific user. * * @since 1.0.0 * * @param int|string $field The ID of the field, or the $name of the field. * @param int $user_id The ID of the user. * @param mixed $value The value for the field you want to set for the user. * @param bool $is_required Whether or not the field is required. * @return bool True on success, false on failure. */ function xprofile_set_field_data( $field, $user_id, $value, $is_required = false ) { if ( is_numeric( $field ) ) { $field_id = $field; } else { $field_id = xprofile_get_field_id_from_name( $field ); } if ( empty( $field_id ) ) { return false; } $field = xprofile_get_field( $field_id ); $field_type = BP_XProfile_Field::get_type( $field_id ); $field_type_obj = bp_xprofile_create_field_type( $field_type ); /** * Filter the raw submitted profile field value. * * Use this filter to modify the values submitted by users before * doing field-type-specific validation. * * @since 2.1.0 * * @param mixed $value Value passed to xprofile_set_field_data(). * @param BP_XProfile_Field $field Field object. * @param BP_XProfile_Field_Type $field_type_obj Field type object. */ $value = apply_filters( 'bp_xprofile_set_field_data_pre_validate', $value, $field, $field_type_obj ); // Special-case support for integer 0 for the number field type. if ( $is_required && ! is_integer( $value ) && $value !== '0' && ( empty( $value ) || ! is_array( $value ) && ! strlen( trim( $value ) ) ) ) { return false; } /** * Certain types of fields (checkboxes, multiselects) may come through empty. * Save as empty array so this isn't overwritten by the default on next edit. * * Special-case support for integer 0 for the number field type */ if ( empty( $value ) && ! is_integer( $value ) && $value !== '0' && $field_type_obj->accepts_null_value ) { $value = array(); } // If the value is empty, then delete any field data that exists, unless the field is of a type // where null values are semantically meaningful. if ( empty( $value ) && ! is_integer( $value ) && $value !== '0' && ! $field_type_obj->accepts_null_value ) { xprofile_delete_field_data( $field_id, $user_id ); return true; } // For certain fields, only certain parameters are acceptable, so add them to the list of allowed values. if ( $field_type_obj->supports_options ) { $field_type_obj->set_allowed_values( wp_list_pluck( $field->get_children(), 'name' ) ); } // Check the value is in an accepted format for this form field. if ( ! $field_type_obj->is_valid( $value ) ) { return false; } $field_args = compact( 'field_type_obj', 'field', 'user_id', 'value', 'is_required' ); /** * Return a WP_Error object or true to use your custom way of saving field values. * * @since 8.0.0 * * @param boolean Whether to shortcircuit the $bp->profile->table_name_data table. * @param array $field_args { * An array of arguments. * * @type object $field_type_obj Field type object. * @type BP_XProfile_Field $field Field object. * @type integer $user_id The user ID. * @type mixed $value Value passed to xprofile_set_field_data(). * @type boolean $is_required Whether or not the field is required. * } */ $retval = apply_filters( 'bp_xprofile_set_field_data_pre_save', false, $field_args ); if ( is_wp_error( $retval ) ) { return false; } if ( false === $retval ) { $field = new BP_XProfile_ProfileData(); $field->field_id = $field_id; $field->user_id = $user_id; // Gets un/reserialized via xprofile_sanitize_data_value_before_save(). $field->value = maybe_serialize( $value ); $retval = $field->save(); } return $retval; } /** * Set the visibility level for this field. * * @since 1.6.0 * * @param int $field_id The ID of the xprofile field. * @param int $user_id The ID of the user to whom the data belongs. * @param string $visibility_level What the visibility setting should be. * @return bool True on success */ function xprofile_set_field_visibility_level( $field_id = 0, $user_id = 0, $visibility_level = '' ) { if ( empty( $field_id ) || empty( $user_id ) || empty( $visibility_level ) ) { return false; } // Check against a list of registered visibility levels. $allowed_values = bp_xprofile_get_visibility_levels(); if ( !array_key_exists( $visibility_level, $allowed_values ) ) { return false; } // Stored in an array in usermeta. $current_visibility_levels = bp_get_user_meta( $user_id, 'bp_xprofile_visibility_levels', true ); if ( !$current_visibility_levels ) { $current_visibility_levels = array(); } $current_visibility_levels[$field_id] = $visibility_level; return bp_update_user_meta( $user_id, 'bp_xprofile_visibility_levels', $current_visibility_levels ); } /** * Get the visibility level for a field. * * @since 2.0.0 * * @param int $field_id The ID of the xprofile field. * @param int $user_id The ID of the user to whom the data belongs. * @return string */ function xprofile_get_field_visibility_level( $field_id = 0, $user_id = 0 ) { $current_level = ''; if ( empty( $field_id ) || empty( $user_id ) ) { return $current_level; } $current_levels = bp_get_user_meta( $user_id, 'bp_xprofile_visibility_levels', true ); $current_level = isset( $current_levels[ $field_id ] ) ? $current_levels[ $field_id ] : ''; // Use the user's stored level, unless custom visibility is disabled. $field = xprofile_get_field( $field_id, null, false ); if ( isset( $field->allow_custom_visibility ) && 'disabled' === $field->allow_custom_visibility ) { $current_level = $field->default_visibility; } // If we're still empty, it means that overrides are permitted, but the // user has not provided a value. Use the default value. if ( empty( $current_level ) ) { $current_level = $field->default_visibility; } return $current_level; } /** * Delete XProfile field data. * * @since 1.1.0 * * @param string $field Field to delete. * @param int $user_id User ID to delete field from. * @return bool Whether or not the field was deleted. */ function xprofile_delete_field_data( $field = '', $user_id = 0 ) { // Get the field ID. if ( is_numeric( $field ) ) { $field_id = (int) $field; } else { $field_id = xprofile_get_field_id_from_name( $field ); } // Bail if field or user ID are empty. if ( empty( $field_id ) || empty( $user_id ) ) { return false; } // Get the profile field data to delete. $field = new BP_XProfile_ProfileData( $field_id, $user_id ); // Delete the field data. return $field->delete(); } /** * Check if field is a required field. * * @since 1.1.0 * * @param int $field_id ID of the field to check for. * @return bool Whether or not field is required. */ function xprofile_check_is_required_field( $field_id ) { $field = new BP_XProfile_Field( $field_id ); $retval = false; if ( isset( $field->is_required ) ) { $retval = $field->is_required; } return (bool) $retval; } /** * Returns the ID for the field based on the field name. * * @since 1.0.0 * * @param string $field_name The name of the field to get the ID for. * @return int|null $field_id on success, false on failure. */ function xprofile_get_field_id_from_name( $field_name ) { return BP_XProfile_Field::get_id_from_name( $field_name ); } /** * Fetches a random piece of profile data for the user. * * @since 1.0.0 * * @global BuddyPress $bp The one true BuddyPress instance. * @global object $wpdb WordPress DB access object. * @global object $current_user WordPress global variable containing current logged in user information. * * @param int $user_id User ID of the user to get random data for. * @param bool $exclude_fullname Optional; whether or not to exclude the full name field as random data. * Defaults to true. * @return string|bool The fetched random data for the user, or false if no data or no match. */ function xprofile_get_random_profile_data( $user_id, $exclude_fullname = true ) { $field_data = BP_XProfile_ProfileData::get_random( $user_id, $exclude_fullname ); if ( empty( $field_data ) ) { return false; } $field_data[0]->value = xprofile_format_profile_field( $field_data[0]->type, $field_data[0]->value ); if ( empty( $field_data[0]->value ) ) { return false; } /** * Filters a random piece of profile data for the user. * * @since 1.0.0 * * @param array $field_data Array holding random profile data. */ return apply_filters( 'xprofile_get_random_profile_data', $field_data ); } /** * Formats a profile field according to its type. [ TODO: Should really be moved to filters ] * * @since 1.0.0 * * @param string $field_type The type of field: datebox, selectbox, textbox etc. * @param string $field_value The actual value. * @return string|bool The formatted value, or false if value is empty. */ function xprofile_format_profile_field( $field_type, $field_value ) { if ( empty( $field_value ) ) { return false; } $field_value = bp_unserialize_profile_field( $field_value ); if ( 'datebox' != $field_type ) { $content = $field_value; $field_value = str_replace( ']]>', ']]>', $content ); } return xprofile_filter_format_field_value_by_type( stripslashes_deep( $field_value ), $field_type ); } /** * Update the field position for a provided field. * * @since 1.1.0 * * @param int $field_id ID of the field to update. * @param int $position Position to update the field to. * @param int $field_group_id Group ID for group the field is in. * @return bool */ function xprofile_update_field_position( $field_id, $position, $field_group_id ) { return BP_XProfile_Field::update_position( $field_id, $position, $field_group_id ); } /** * Replace the displayed and logged-in users fullnames with the xprofile name, if required. * * The Members component uses the logged-in user's display_name to set the * value of buddypress()->loggedin_user->fullname. However, in cases where * profile sync is disabled, display_name may diverge from the xprofile * fullname field value, and the xprofile field should take precedence. * * Runs at bp_setup_globals:100 to ensure that all components have loaded their * globals before attempting any overrides. * * @since 2.0.0 */ function xprofile_override_user_fullnames() { // If sync is enabled, the two names will match. No need to continue. if ( ! bp_disable_profile_sync() ) { return; } if ( bp_loggedin_user_id() ) { buddypress()->loggedin_user->fullname = bp_core_get_user_displayname( bp_loggedin_user_id() ); } if ( bp_displayed_user_id() ) { buddypress()->displayed_user->fullname = bp_core_get_user_displayname( bp_displayed_user_id() ); } } add_action( 'bp_setup_globals', 'xprofile_override_user_fullnames', 100 ); /** * When search_terms are passed to BP_User_Query, search against xprofile fields. * * @since 2.0.0 * * @param array $sql Clauses in the user_id SQL query. * @param BP_User_Query $query User query object. * @return array */ function bp_xprofile_bp_user_query_search( $sql, BP_User_Query $query ) { global $wpdb; if ( empty( $query->query_vars['search_terms'] ) || empty( $sql['where']['search'] ) ) { return $sql; } $bp = buddypress(); $search_terms_clean = bp_esc_like( wp_kses_normalize_entities( $query->query_vars['search_terms'] ) ); if ( $query->query_vars['search_wildcard'] === 'left' ) { $search_terms_nospace = '%' . $search_terms_clean; $search_terms_space = '%' . $search_terms_clean . ' %'; } elseif ( $query->query_vars['search_wildcard'] === 'right' ) { $search_terms_nospace = $search_terms_clean . '%'; $search_terms_space = '% ' . $search_terms_clean . '%'; } else { $search_terms_nospace = '%' . $search_terms_clean . '%'; $search_terms_space = '%' . $search_terms_clean . '%'; } // Combine the core search (against wp_users) into a single OR clause // with the xprofile_data search. $matched_user_ids = $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->profile->table_name_data} WHERE value LIKE %s OR value LIKE %s", $search_terms_nospace, $search_terms_space ) ); if ( ! empty( $matched_user_ids ) ) { $search_core = $sql['where']['search']; $search_combined = " ( u.{$query->uid_name} IN (" . implode(',', $matched_user_ids) . ") OR {$search_core} )"; $sql['where']['search'] = $search_combined; } return $sql; } add_action( 'bp_user_query_uid_clauses', 'bp_xprofile_bp_user_query_search', 10, 2 ); /** * Syncs Xprofile data to the standard built in WordPress profile data. * * @since 1.0.0 * @since 9.2.0 Adds the $args arguments to catch hook's additional arguments. * * @param int $user_id ID of the user to sync. * @param array $args Hook's additional arguments. * @return bool */ function xprofile_sync_wp_profile( $user_id = 0, ...$args ) { // Bail if profile syncing is disabled. if ( bp_disable_profile_sync() ) { return true; } if ( empty( $user_id ) ) { $user_id = bp_loggedin_user_id(); } if ( empty( $user_id ) ) { return false; } $fullname_field_id = (int) bp_xprofile_fullname_field_id(); $usermeta = array(); $userdata = array(); if ( isset( $args[1]['meta'] ) ) { $usermeta = $args[1]['meta']; } elseif ( isset( $args[3] ) ) { $usermeta = $args[3]; } if ( isset( $usermeta['profile_field_ids'] ) ) { $xprofile_fields = wp_parse_id_list( $usermeta['profile_field_ids'] ); $xprofile_fields = array_diff( $xprofile_fields, array( $fullname_field_id ) ); foreach ( $xprofile_fields as $xprofile_field_id ) { $field_type = bp_xprofile_get_field_type( $xprofile_field_id ); $field_key = 'field_' . $xprofile_field_id; if ( isset( $field_type->wp_user_key ) && isset( $usermeta[ $field_key ] ) && $usermeta[ $field_key ] ) { $userdata[ $field_type->wp_user_key ] = $usermeta[ $field_key ]; } } } $fullname = xprofile_get_field_data( $fullname_field_id, $user_id ); $space = strpos( $fullname, ' ' ); if ( false === $space ) { if ( ! isset( $userdata['first_name'] ) ) { $userdata['first_name'] = $fullname; } if ( ! isset( $userdata['last_name'] ) ) { $userdata['last_name'] = ''; } } else { if ( ! isset( $userdata['first_name'] ) ) { $userdata['first_name'] = substr( $fullname, 0, $space ); } if ( ! isset( $userdata['last_name'] ) ) { $userdata['last_name'] = trim( substr( $fullname, $space, strlen( $fullname ) ) ); } } bp_update_user_meta( $user_id, 'nickname', $fullname ); bp_update_user_meta( $user_id, 'first_name', $userdata['first_name'] ); bp_update_user_meta( $user_id, 'last_name', $userdata['last_name'] ); wp_update_user( array( 'ID' => $user_id, 'display_name' => $fullname ) ); } add_action( 'bp_core_signup_user', 'xprofile_sync_wp_profile', 10, 5 ); add_action( 'bp_core_activated_user', 'xprofile_sync_wp_profile', 10, 3 ); /** * Syncs the standard built in WordPress profile data to XProfile. * * @since 1.2.4 * * @param object $errors Array of errors. Passed by reference. * @param bool $update Whether or not being upated. * @param object $user User object whose profile is being synced. Passed by reference. */ function xprofile_sync_bp_profile( &$errors, $update, &$user ) { // Bail if profile syncing is disabled. if ( bp_disable_profile_sync() || ! $update || $errors->get_error_codes() ) { return; } xprofile_set_field_data( bp_xprofile_fullname_field_id(), $user->ID, $user->display_name ); } add_action( 'user_profile_update_errors', 'xprofile_sync_bp_profile', 10, 3 ); /** * Update the WP display, last, and first name fields when the xprofile display name field is updated. * * @since 3.0.0 * * @param BP_XProfile_ProfileData $data Current instance of the profile data being saved. */ function xprofile_sync_wp_profile_on_single_field_set( $data ) { if ( bp_xprofile_fullname_field_id() !== $data->field_id ) { return; } xprofile_sync_wp_profile( $data->user_id ); } add_action( 'xprofile_data_after_save', 'xprofile_sync_wp_profile_on_single_field_set' ); /** * When a user is deleted, we need to clean up the database and remove all the * profile data from each table. Also we need to clean anything up in the * usermeta table that this component uses. * * @since 1.0.0 * * @param int $user_id The ID of the deleted user. */ function xprofile_remove_data( $user_id ) { BP_XProfile_ProfileData::delete_data_for_user( $user_id ); } add_action( 'wpmu_delete_user', 'xprofile_remove_data' ); add_action( 'bp_make_spam_user', 'xprofile_remove_data' ); /** * Deletes user XProfile data on the 'delete_user' hook. * * @since 6.0.0 * * @param int $user_id The ID of the deleted user. */ function xprofile_remove_data_on_delete_user( $user_id ) { if ( ! bp_remove_user_data_on_delete_user_hook( 'xprofile', $user_id ) ) { return; } xprofile_remove_data( $user_id ); } add_action( 'delete_user', 'xprofile_remove_data_on_delete_user' ); /*** XProfile Meta ****************************************************/ /** * Delete a piece of xprofile metadata. * * @since 1.5.0 * * @param int $object_id ID of the object the metadata belongs to. * @param string $object_type Type of object. 'group', 'field', or 'data'. * @param string|bool $meta_key Key of the metadata being deleted. If omitted, all * metadata for the object will be deleted. * @param mixed $meta_value Optional. If provided, only metadata that matches * the value will be permitted. * @param bool $delete_all Optional. If true, delete matching metadata entries * for all objects, ignoring the specified object_id. Otherwise, only * delete matching metadata entries for the specified object. * Default: false. * @return bool True on success, false on failure. */ function bp_xprofile_delete_meta( $object_id, $object_type, $meta_key = false, $meta_value = false, $delete_all = false ) { global $wpdb; // Sanitize object type. if ( ! in_array( $object_type, array( 'group', 'field', 'data' ) ) ) { return false; } // Legacy - if no meta_key is passed, delete all for the item. if ( empty( $meta_key ) ) { $table_key = 'xprofile_' . $object_type . 'meta'; $table_name = $wpdb->{$table_key}; $keys = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM {$table_name} WHERE object_type = %s AND object_id = %d", $object_type, $object_id ) ); // Force delete_all to false if deleting all for object. $delete_all = false; } else { $keys = array( $meta_key ); } add_filter( 'query', 'bp_filter_metaid_column_name' ); add_filter( 'query', 'bp_xprofile_filter_meta_query' ); $retval = false; foreach ( $keys as $key ) { $retval = delete_metadata( 'xprofile_' . $object_type, $object_id, $key, $meta_value, $delete_all ); } remove_filter( 'query', 'bp_xprofile_filter_meta_query' ); remove_filter( 'query', 'bp_filter_metaid_column_name' ); return $retval; } /** * Get a piece of xprofile metadata. * * Note that the default value of $single is true, unlike in the case of the * underlying get_metadata() function. This is for backward compatibility. * * @since 1.5.0 * * @param int $object_id ID of the object the metadata belongs to. * @param string $object_type Type of object. 'group', 'field', or 'data'. * @param string $meta_key Key of the metadata being fetched. If omitted, all * metadata for the object will be retrieved. * @param bool $single Optional. If true, return only the first value of the * specified meta_key. This parameter has no effect if meta_key is not * specified. Default: true. * @return mixed An array of values if `$single` is false. * The value of the meta field if `$single` is true. * False for an invalid `$object_type` (one of `group`, `field`, `data`). * False for an invalid `$object_id` (non-numeric, zero, or negative value), * or if `$meta_type` is not specified. * An empty string if a valid but non-existing object ID is passed. */ function bp_xprofile_get_meta( $object_id, $object_type, $meta_key = '', $single = true ) { // Sanitize object type. if ( ! in_array( $object_type, array( 'group', 'field', 'data' ) ) ) { return false; } add_filter( 'query', 'bp_filter_metaid_column_name' ); add_filter( 'query', 'bp_xprofile_filter_meta_query' ); $retval = get_metadata( 'xprofile_' . $object_type, $object_id, $meta_key, $single ); remove_filter( 'query', 'bp_filter_metaid_column_name' ); remove_filter( 'query', 'bp_xprofile_filter_meta_query' ); return $retval; } /** * Update a piece of xprofile metadata. * * @since 1.5.0 * * @param int $object_id ID of the object the metadata belongs to. * @param string $object_type Type of object. 'group', 'field', or 'data'. * @param string $meta_key Key of the metadata being updated. * @param string $meta_value Value of the metadata being updated. * @param mixed $prev_value Optional. If specified, only update existing * metadata entries with the specified value. * Otherwise update all entries. * @return bool|int Returns false on failure. On successful update of existing * metadata, returns true. On successful creation of new metadata, * returns the integer ID of the new metadata row. */ function bp_xprofile_update_meta( $object_id, $object_type, $meta_key, $meta_value, $prev_value = '' ) { add_filter( 'query', 'bp_filter_metaid_column_name' ); add_filter( 'query', 'bp_xprofile_filter_meta_query' ); $retval = update_metadata( 'xprofile_' . $object_type, $object_id, $meta_key, $meta_value, $prev_value ); remove_filter( 'query', 'bp_xprofile_filter_meta_query' ); remove_filter( 'query', 'bp_filter_metaid_column_name' ); return $retval; } /** * Add a piece of xprofile metadata. * * @since 2.0.0 * * @param int $object_id ID of the object the metadata belongs to. * @param string $object_type Type of object. 'group', 'field', or 'data'. * @param string $meta_key Metadata key. * @param mixed $meta_value Metadata value. * @param bool $unique Optional. Whether to enforce a single metadata value * for the given key. If true, and the object already * has a value for the key, no change will be made. * Default false. * @return int|bool The meta ID on successful update, false on failure. */ function bp_xprofile_add_meta( $object_id, $object_type, $meta_key, $meta_value, $unique = false ) { add_filter( 'query', 'bp_filter_metaid_column_name' ); add_filter( 'query', 'bp_xprofile_filter_meta_query' ); $retval = add_metadata( 'xprofile_' . $object_type , $object_id, $meta_key, $meta_value, $unique ); remove_filter( 'query', 'bp_filter_metaid_column_name' ); remove_filter( 'query', 'bp_xprofile_filter_meta_query' ); return $retval; } /** * Updates the fieldgroup metadata. * * @since 1.5.0 * * @param int $field_group_id Group ID for the group field belongs to. * @param string $meta_key Meta key to update. * @param string $meta_value Meta value to update to. * @return bool|int */ function bp_xprofile_update_fieldgroup_meta( $field_group_id, $meta_key, $meta_value ) { return bp_xprofile_update_meta( $field_group_id, 'group', $meta_key, $meta_value ); } /** * Updates the field metadata. * * @since 1.5.0 * * @param int $field_id Field ID to update. * @param string $meta_key Meta key to update. * @param string $meta_value Meta value to update to. * @return bool|int */ function bp_xprofile_update_field_meta( $field_id, $meta_key, $meta_value ) { return bp_xprofile_update_meta( $field_id, 'field', $meta_key, $meta_value ); } /** * Updates the fielddata metadata. * * @since 1.5.0 * * @param int $field_data_id Field ID to update. * @param string $meta_key Meta key to update. * @param string $meta_value Meta value to update to. * @return bool|int */ function bp_xprofile_update_fielddata_meta( $field_data_id, $meta_key, $meta_value ) { return bp_xprofile_update_meta( $field_data_id, 'data', $meta_key, $meta_value ); } /** * Return the field ID for the Full Name xprofile field. * * @since 2.0.0 * * @return int Field ID. */ function bp_xprofile_fullname_field_id() { $id = wp_cache_get( 'fullname_field_id', 'bp_xprofile' ); if ( false === $id ) { global $wpdb; $bp = buddypress(); $id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s", addslashes( bp_xprofile_fullname_field_name() ) ) ); wp_cache_set( 'fullname_field_id', $id, 'bp_xprofile' ); } return absint( $id ); } /** * Return the field name for the Full Name xprofile field. * * @since 1.5.0 * * @return string The field name. */ function bp_xprofile_fullname_field_name() { /** * Filters the field name for the Full Name xprofile field. * * @since 1.5.0 * * @param string $value BP_XPROFILE_FULLNAME_FIELD_NAME Full name field constant. */ return apply_filters( 'bp_xprofile_fullname_field_name', BP_XPROFILE_FULLNAME_FIELD_NAME ); } /** * Is rich text enabled for this profile field? * * By default, rich text is enabled for textarea fields and disabled for all other field types. * * @since 2.4.0 * * @param int|null $field_id Optional. Default current field ID. * @return bool */ function bp_xprofile_is_richtext_enabled_for_field( $field_id = null ) { if ( ! $field_id ) { $field_id = bp_get_the_profile_field_id(); } $field = xprofile_get_field( $field_id, null, false ); $enabled = false; if ( $field instanceof BP_XProfile_Field ) { $enabled = (bool) $field->type_obj->supports_richtext; } /** * Filters whether richtext is enabled for the given field. * * @since 2.4.0 * * @param bool $enabled True if richtext is enabled for the field, otherwise false. * @param int $field_id ID of the field. */ return apply_filters( 'bp_xprofile_is_richtext_enabled_for_field', $enabled, $field_id ); } /** * Get visibility levels out of the $bp global. * * @since 1.6.0 * * @return array */ function bp_xprofile_get_visibility_levels() { /** * Filters the visibility levels out of the $bp global. * * @since 1.6.0 * * @param array $visibility_levels Array of visibility levels. */ return apply_filters( 'bp_xprofile_get_visibility_levels', buddypress()->profile->visibility_levels ); } /** * Get the ids of fields that are hidden for this displayed/loggedin user pair. * * This is the function primarily responsible for profile field visibility. It works by determining * the relationship between the displayed_user (ie the profile owner) and the current_user (ie the * profile viewer). Then, based on that relationship, we query for the set of fields that should * be excluded from the profile loop. * * @since 1.6.0 * * @see BP_XProfile_Group::get() * or if you have added your own custom levels. * * @param int $displayed_user_id The id of the user the profile fields belong to. * @param int $current_user_id The id of the user viewing the profile. * @return array An array of field ids that should be excluded from the profile query */ function bp_xprofile_get_hidden_fields_for_user( $displayed_user_id = 0, $current_user_id = 0 ) { if ( !$displayed_user_id ) { $displayed_user_id = bp_displayed_user_id(); } if ( !$displayed_user_id ) { return array(); } if ( !$current_user_id ) { $current_user_id = bp_loggedin_user_id(); } // @todo - This is where you'd swap out for current_user_can() checks. $hidden_levels = bp_xprofile_get_hidden_field_types_for_user( $displayed_user_id, $current_user_id ); $hidden_fields = bp_xprofile_get_fields_by_visibility_levels( $displayed_user_id, $hidden_levels ); /** * Filters the ids of fields that are hidden for this displayed/loggedin user pair. * * @since 1.6.0 * * @param array $hidden_fields Array of hidden fields for the displayed/logged in user. * @param int $displayed_user_id ID of the displayed user. * @param int $current_user_id ID of the current user. */ return apply_filters( 'bp_xprofile_get_hidden_fields_for_user', $hidden_fields, $displayed_user_id, $current_user_id ); } /** * Get the visibility levels that should be hidden for this user pair. * * Field visibility is determined based on the relationship between the * logged-in user, the displayed user, and the visibility setting for the * current field. (See bp_xprofile_get_hidden_fields_for_user().) This * utility function speeds up this matching by fetching the visibility levels * that should be hidden for the current user pair. * * @since 1.8.2 * * @see bp_xprofile_get_hidden_fields_for_user() * * @param int $displayed_user_id The id of the user the profile fields belong to. * @param int $current_user_id The id of the user viewing the profile. * @return array An array of visibility levels hidden to the current user. */ function bp_xprofile_get_hidden_field_types_for_user( $displayed_user_id = 0, $current_user_id = 0 ) { // Current user is logged in. if ( ! empty( $current_user_id ) ) { // Nothing's private when viewing your own profile, or when the // current user is an admin. if ( $displayed_user_id == $current_user_id || bp_current_user_can( 'bp_moderate' ) ) { $hidden_levels = array(); // If the current user and displayed user are friends, show all. } elseif ( bp_is_active( 'friends' ) && friends_check_friendship( $displayed_user_id, $current_user_id ) ) { $hidden_levels = array( 'adminsonly', ); // Current user is logged in but not friends, so exclude friends-only. } else { $hidden_levels = array( 'friends', 'adminsonly', ); } // Current user is not logged in, so exclude friends-only, loggedin, and adminsonly. } else { $hidden_levels = array( 'friends', 'loggedin', 'adminsonly', ); } /** * Filters the visibility levels that should be hidden for this user pair. * * @since 2.0.0 * * @param array $hidden_fields Array of hidden fields for the displayed/logged in user. * @param int $displayed_user_id ID of the displayed user. * @param int $current_user_id ID of the current user. */ return apply_filters( 'bp_xprofile_get_hidden_field_types_for_user', $hidden_levels, $displayed_user_id, $current_user_id ); } /** * Fetch an array of the xprofile fields that a given user has marked with certain visibility levels. * * @since 1.6.0 * * @see bp_xprofile_get_hidden_fields_for_user() * * @param int $user_id The id of the profile owner. * @param array $levels An array of visibility levels ('public', 'friends', 'loggedin', 'adminsonly' etc) to be * checked against. * @return array $field_ids The fields that match the requested visibility levels for the given user. */ function bp_xprofile_get_fields_by_visibility_levels( $user_id, $levels = array() ) { if ( !is_array( $levels ) ) { $levels = (array)$levels; } $user_visibility_levels = (array) bp_get_user_meta( $user_id, 'bp_xprofile_visibility_levels', true ); // Parse the user-provided visibility levels with the default levels, which may take // precedence. $default_visibility_levels = BP_XProfile_Group::fetch_default_visibility_levels(); foreach( (array) $default_visibility_levels as $d_field_id => $defaults ) { // If the admin has forbidden custom visibility levels for this field, replace // the user-provided setting with the default specified by the admin. if ( isset( $defaults['allow_custom'] ) && isset( $defaults['default'] ) && 'disabled' == $defaults['allow_custom'] ) { $user_visibility_levels[$d_field_id] = $defaults['default']; } } $field_ids = array(); foreach( $user_visibility_levels as $field_id => $field_visibility ) { if ( in_array( $field_visibility, $levels ) ) { $field_ids[] = $field_id; } } // Never allow the fullname field to be excluded. if ( in_array( 1, $field_ids ) ) { $key = array_search( 1, $field_ids ); unset( $field_ids[$key] ); } return $field_ids; } /** * Formats datebox field values passed through a POST request. * * @since 2.8.0 * * @param int $field_id The id of the current field being looped through. * @return void This function only changes the global $_POST that should contain * the datebox data. */ function bp_xprofile_maybe_format_datebox_post_data( $field_id ) { if ( ! isset( $_POST['field_' . $field_id] ) ) { if ( ! empty( $_POST['field_' . $field_id . '_day'] ) && ! empty( $_POST['field_' . $field_id . '_month'] ) && ! empty( $_POST['field_' . $field_id . '_year'] ) ) { // Concatenate the values. $date_value = $_POST['field_' . $field_id . '_day'] . ' ' . $_POST['field_' . $field_id . '_month'] . ' ' . $_POST['field_' . $field_id . '_year']; // Check that the concatenated value can be turned into a timestamp. if ( $timestamp = strtotime( $date_value ) ) { // Add the timestamp to the global $_POST that should contain the datebox data. $_POST['field_' . $field_id] = date( 'Y-m-d H:i:s', $timestamp ); } } } } /** * Finds and exports personal data associated with an email address from the XProfile tables. * * @since 4.0.0 * * @param string $email_address The users email address. * @return array An array of personal data. */ function bp_xprofile_personal_data_exporter( $email_address ) { $email_address = trim( $email_address ); $data_to_export = array(); $user = get_user_by( 'email', $email_address ); if ( ! $user ) { return array( 'data' => array(), 'done' => true, ); } $user_data_to_export = array(); $user_profile_data = BP_XProfile_ProfileData::get_all_for_user( $user->ID ); foreach ( $user_profile_data as $field_name => $field ) { // Skip non-array fields, which don't belong to XProfile. if ( ! is_array( $field ) ) { continue; } // Re-pull the data so that BuddyPress formats and sanitizes properly. $value = xprofile_get_field_data( $field['field_id'], $user->ID, 'comma' ); $user_data_to_export[] = array( 'name' => $field_name, 'value' => $value, ); } $data_to_export[] = array( 'group_id' => 'bp_xprofile', 'group_label' => __( 'Extended Profile Data', 'buddypress' ), 'item_id' => "bp-xprofile-{$user->ID}", 'data' => $user_data_to_export, ); return array( 'data' => $data_to_export, 'done' => true, ); } /** * Returns the list of supporterd WordPress field meta keys. * * @since 8.0.0 * * @return string[] List of supported WordPress user keys. */ function bp_xprofile_get_wp_user_keys() { return array_merge( array( 'first_name', 'last_name', 'user_url', 'description' ), array_keys( wp_get_user_contact_methods() ) ); } /** * Returns the signup field IDs. * * @since 8.0.0 * * @return int[] The signup field IDs. */ function bp_xprofile_get_signup_field_ids() { $signup_field_ids = wp_cache_get( 'signup_fields', 'bp_xprofile' ); if ( ! $signup_field_ids ) { global $wpdb; $bp = buddypress(); $signup_field_ids = $wpdb->get_col( "SELECT object_id FROM {$bp->profile->table_name_meta} WHERE object_type = 'field' AND meta_key = 'signup_position' ORDER BY CONVERT(meta_value, SIGNED) ASC" ); wp_cache_set( 'signup_fields', $signup_field_ids, 'bp_xprofile' ); } return array_map( 'intval', $signup_field_ids ); } /** * Returns xProfile loop's signup arguments. * * @since 8.0.0 * * @param array $extra Optional extra arguments. * @return array The xProfile loop's signup arguments. */ function bp_xprofile_signup_args( $extra = array() ) { $signup_fields = (array) bp_xprofile_get_signup_field_ids(); $default_args = array( 'fetch_fields' => true, 'fetch_field_data' => false, ); // No signup fields? Let's bring back primary group. if ( ! $signup_fields && bp_is_register_page() ) { $default_args['profile_group_id'] = 1; } else { $default_args['signup_fields_only'] = true; } return array_merge( $default_args, $extra ); }