Skip to:
Content

BuddyPress.org


Ignore:
Timestamp:
03/27/2014 07:34:02 PM (10 years ago)
Author:
djpaul
Message:

xProfile: re-architecture profile field types and de-duplicate the templating and validation logic to make it easier for core and plugins to add new profile field types.

Until now, it's been pretty hard to add new types of profile field to BuddyPress. There are a couple of plugins that do a good job, but BuddyPress makes it much harder than it should be because, historically, we've hardcoded values and checks in templates throughout the project. For example, profile field templating was duplicated in the registration template, the member/profile/edit template, in parts of the wp-admin xProfile screens, and in the new wp-admin extended profile editor.

This change implements a new approach that creates a class for each profile field type; selectbox, textbox, textarea, and so on. They all share an abstract base class BP_XProfile_Field_Type which consolidates a lot of special behaviour that had been added to BuddyPress over the years (e.g. some fields accept null values, some accept multiple default values), and adds true field value validation. Unit tests are included.

We've also implemented a new "Numbers" field type with these changes. It behaves much the same as a regular textbox field does, but it only accepts numbers.

Fixes #5220 and #4694

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/bp-xprofile/bp-xprofile-functions.php

    r8135 r8178  
    6060
    6161/*** Field Management *********************************************************/
     62
     63/**
     64 * Get details of all xprofile field types.
     65 *
     66 * @return array Key/value pairs (field type => class name).
     67 * @since BuddyPress (2.0.0)
     68 */
     69function bp_xprofile_get_field_types() {
     70    $fields = array(
     71        'checkbox'       => 'BP_XProfile_Field_Type_Checkbox',
     72        'datebox'        => 'BP_XProfile_Field_Type_Datebox',
     73        'multiselectbox' => 'BP_XProfile_Field_Type_Multiselectbox',
     74        'number'         => 'BP_XProfile_Field_Type_Number',
     75        'radio'          => 'BP_XProfile_Field_Type_Radiobutton',
     76        'selectbox'      => 'BP_XProfile_Field_Type_Selectbox',
     77        'textarea'       => 'BP_XProfile_Field_Type_Textarea',
     78        'textbox'        => 'BP_XProfile_Field_Type_Textbox',
     79    );
     80
     81    // If you've added a custom field type in a plugin, register it with this filter.
     82    return apply_filters( 'bp_xprofile_get_field_types', $fields );
     83}
     84
     85/**
     86 * Creates the specified field type object; used for validation and templating.
     87 *
     88 * @param string $type Type of profile field to create. See {@link bp_xprofile_get_field_types()} for default core values.
     89 * @return object If field type unknown, returns BP_XProfile_Field_Type_Textarea. Otherwise returns an instance of the relevant child class of BP_XProfile_Field_Type.
     90 * @since BuddyPress (2.0.0)
     91 */
     92function bp_xprofile_create_field_type( $type ) {
     93
     94    $field = bp_xprofile_get_field_types();
     95    $class = isset( $field[$type] ) ? $field[$type] : '';
     96
     97    /**
     98     * For backpat and to handle (missing) field types introduced by other plugins, fallback to
     99     * textbox if a type is unknown. Textbox validation and display is intentionally low key.
     100     */
     101    if ( $class && class_exists( $class ) ) {
     102        return new $class;
     103    } else {
     104        return new BP_XProfile_Field_Type_Textbox;
     105    }
     106}
    62107
    63108function xprofile_insert_field( $args = '' ) {
     
    212257        return false;
    213258
    214     if ( $is_required && ( empty( $value ) || !is_array( $value ) && !strlen( trim( $value ) ) ) )
    215         return false;
    216 
    217     $field = new BP_XProfile_Field( $field_id );
    218 
    219     // If the value is empty, then delete any field data that exists, unless the field is of a
    220     // type where null values are semantically meaningful
    221     if ( empty( $value ) && 'checkbox' != $field->type && 'multiselectbox' != $field->type ) {
     259    // Special-case support for integer 0 for the number field type
     260    if ( $is_required && ! is_integer( $value ) && $value !== '0' && ( empty( $value ) || ! is_array( $value ) && ! strlen( trim( $value ) ) ) ) {
     261        return false;
     262    }
     263
     264    $field          = new BP_XProfile_Field( $field_id );
     265    $field_type     = BP_XProfile_Field::get_type( $field_id );
     266    $field_type_obj = bp_xprofile_create_field_type( $field_type );
     267
     268    /**
     269     * Certain types of fields (checkboxes, multiselects) may come through empty.
     270     * Save as empty array so this isn't overwritten by the default on next edit.
     271     *
     272     * Special-case support for integer 0 for the number field type
     273     */
     274    if ( empty( $value ) && ! is_integer( $value ) && $value !== '0' && $field_type_obj->accepts_null_value ) {
     275        $value = array();
     276    }
     277
     278    // 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
     279    if ( empty( $value ) && ! is_integer( $value ) && $value !== '0' && ! $field_type_obj->accepts_null_value ) {
    222280        xprofile_delete_field_data( $field_id, $user_id );
    223281        return true;
    224282    }
    225283
    226     $possible_values = array();
    227 
    228     // Check the value is an acceptable value
    229     if ( 'checkbox' == $field->type || 'radio' == $field->type || 'selectbox' == $field->type || 'multiselectbox' == $field->type ) {
    230         $options = $field->get_children();
    231 
    232         foreach( $options as $option )
    233             $possible_values[] = $option->name;
    234 
    235         if ( is_array( $value ) ) {
    236             foreach( $value as $i => $single ) {
    237                 if ( !in_array( $single, $possible_values ) ) {
    238                     unset( $value[$i] );
    239                 }
    240             }
    241 
    242             // Reset the keys by merging with an empty array
    243             $value = array_merge( array(), $value );
    244         } else {
    245             if ( !in_array( $value, $possible_values ) ) {
    246                 return false;
    247             }
    248         }
     284    // For certain fields, only certain parameters are acceptable, so add them to the whitelist.
     285    if ( $field_type_obj->supports_options ) {
     286        $field_type_obj->set_whitelist_values( wp_list_pluck( $field->get_children(), 'name' ) );
     287    }
     288
     289    // Check the value is in an accepted format for this form field.
     290    if ( ! $field_type_obj->is_valid( $value ) ) {
     291        return false;
    249292    }
    250293
Note: See TracChangeset for help on using the changeset viewer.