Skip to:
Content

BuddyPress.org


Ignore:
Timestamp:
03/27/2014 07:34:02 PM (12 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-template.php

    r8142 r8178  
    222222                        $css_classes[] = 'alt';
    223223
     224                $css_classes[] = 'field_type_' . sanitize_title( $profile_template->field->type );
    224225                $css_classes = apply_filters_ref_array( 'bp_field_css_classes', array( &$css_classes ) );
    225226
     
    387388                global $field;
    388389
    389                 $array_box = false;
    390                 if ( 'multiselectbox' == $field->type )
    391                         $array_box = '[]';
    392 
    393                 return apply_filters( 'bp_get_the_profile_field_input_name', 'field_' . $field->id . $array_box );
     390                return apply_filters( 'bp_get_the_profile_field_input_name', 'field_' . $field->id );
    394391        }
    395392
     
    424421 * @param array $args Specify type for datebox. Allowed 'day', 'month', 'year'.
    425422 */
    426 function bp_the_profile_field_options( $args = '' ) {
     423function bp_the_profile_field_options( $args = array() ) {
    427424        echo bp_get_the_profile_field_options( $args );
    428425}
     
    430427         * bp_get_the_profile_field_options()
    431428         *
    432          * Retrieves field options HTML for field types of 'selectbox', 'multiselectbox',
    433          * 'radio', 'checkbox', and 'datebox'.
     429         * Retrieves field options HTML for field types of 'selectbox', 'multiselectbox', 'radio', 'checkbox', and 'datebox'.
    434430         *
    435431         * @package BuddyPress Xprofile
     
    447443         * }
    448444         */
    449         function bp_get_the_profile_field_options( $args = '' ) {
    450                 global $field;
    451 
    452                 $defaults = array(
     445        function bp_get_the_profile_field_options( $args = array() ) {
     446                global $field;
     447
     448                $args = bp_parse_args( $args, array(
    453449                        'type'    => false,
    454450                        'user_id' => bp_displayed_user_id(),
    455                 );
    456 
    457                 $r = wp_parse_args( $args, $defaults );
    458                 extract( $r, EXTR_SKIP );
    459 
    460                 // In some cases, the $field global is not an instantiation of the BP_XProfile_Field
    461                 // class. However, we have to make sure that all data originally in $field gets
    462                 // merged back in, after reinstantiation.
    463                 if ( !method_exists( $field, 'get_children' ) ) {
     451                ), 'get_the_profile_field_options' );
     452
     453                /**
     454                 * In some cases, the $field global is not an instantiation of the BP_XProfile_Field class.
     455                 * However, we have to make sure that all data originally in $field gets merged back in, after reinstantiation.
     456                 */
     457                if ( ! method_exists( $field, 'get_children' ) ) {
    464458                        $field_obj = new BP_XProfile_Field( $field->id );
    465459
    466                         foreach( $field as $field_prop => $field_prop_value ) {
    467                                 if ( !isset( $field_obj->{$field_prop} ) ) {
     460                        foreach ( $field as $field_prop => $field_prop_value ) {
     461                                if ( ! isset( $field_obj->{$field_prop} ) )
    468462                                        $field_obj->{$field_prop} = $field_prop_value;
    469                                 }
    470463                        }
    471464
     
    473466                }
    474467
    475                 $options = $field->get_children();
    476 
    477                 // Setup some defaults
    478                 $html     = '';
    479                 $selected = '';
    480 
    481                 switch ( $field->type ) {
    482                         case 'selectbox':
    483 
    484                                 $html .= '<option value="">' . /* translators: no option picked in select box */ __( '----', 'buddypress' ) . '</option>';
    485 
    486                                 $original_option_values = '';
    487                                 $original_option_values = maybe_unserialize( BP_XProfile_ProfileData::get_value_byid( $field->id, $user_id ) );
    488 
    489                                 if ( empty( $original_option_values ) && !empty( $_POST['field_' . $field->id] ) ) {
    490                                         $original_option_values = $_POST['field_' . $field->id];
    491                                 }
    492 
    493                                 $option_values = (array) $original_option_values;
    494 
    495                                 for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
    496 
    497                                         // Check for updated posted values, but errors preventing them from being saved first time
    498                                         foreach( $option_values as $i => $option_value ) {
    499                                                 if ( isset( $_POST['field_' . $field->id] ) && $_POST['field_' . $field->id] != $option_value ) {
    500                                                         if ( !empty( $_POST['field_' . $field->id] ) ) {
    501                                                                 $option_values[$i] = $_POST['field_' . $field->id];
    502                                                         }
    503                                                 }
    504                                         }
    505 
    506                                         $selected = '';
    507 
    508                                         // Run the allowed option name through the before_save filter, so we'll be sure to get a match
    509                                         $allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
    510 
    511                                         // First, check to see whether the user-entered value matches
    512                                         if ( in_array( $allowed_options, (array) $option_values ) ) {
    513                                                 $selected = ' selected="selected"';
    514                                         }
    515 
    516                                         // Then, if the user has not provided a value, check for defaults
    517                                         if ( !is_array( $original_option_values ) && empty( $option_values ) && $options[$k]->is_default_option ) {
    518                                                 $selected = ' selected="selected"';
    519                                         }
    520 
    521                                         $html .= apply_filters( 'bp_get_the_profile_field_options_select', '<option' . $selected . ' value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '">' . esc_attr( stripslashes( $options[$k]->name ) ) . '</option>', $options[$k], $field->id, $selected, $k );
    522                                 }
    523                                 break;
    524 
    525                         case 'multiselectbox':
    526                                 $original_option_values = '';
    527                                 $original_option_values = maybe_unserialize( BP_XProfile_ProfileData::get_value_byid( $field->id, $user_id ) );
    528 
    529                                 if ( empty( $original_option_values ) && !empty( $_POST['field_' . $field->id] ) ) {
    530                                         $original_option_values = $_POST['field_' . $field->id];
    531                                 }
    532 
    533                                 $option_values = (array) $original_option_values;
    534 
    535                                 for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
    536 
    537                                         // Check for updated posted values, but errors preventing them from being saved first time
    538                                         foreach( $option_values as $i => $option_value ) {
    539                                                 if ( isset( $_POST['field_' . $field->id] ) && $_POST['field_' . $field->id][$i] != $option_value ) {
    540                                                         if ( !empty( $_POST['field_' . $field->id][$i] ) ) {
    541                                                                 $option_values[] = $_POST['field_' . $field->id][$i];
    542                                                         }
    543                                                 }
    544                                         }
    545                                         $selected = '';
    546 
    547                                         // Run the allowed option name through the before_save filter, so we'll be sure to get a match
    548                                         $allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
    549 
    550                                         // First, check to see whether the user-entered value matches
    551                                         if ( in_array( $allowed_options, (array) $option_values ) ) {
    552                                                 $selected = ' selected="selected"';
    553                                         }
    554 
    555                                         // Then, if the user has not provided a value, check for defaults
    556                                         if ( !is_array( $original_option_values ) && empty( $option_values ) && !empty( $options[$k]->is_default_option ) ) {
    557                                                 $selected = ' selected="selected"';
    558                                         }
    559 
    560                                         $html .= apply_filters( 'bp_get_the_profile_field_options_multiselect', '<option' . $selected . ' value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '">' . esc_attr( stripslashes( $options[$k]->name ) ) . '</option>', $options[$k], $field->id, $selected, $k );
    561                                 }
    562                                 break;
    563 
    564                         case 'radio':
    565                                 $html .= '<div id="field_' . $field->id . '">';
    566                                 $option_value = BP_XProfile_ProfileData::get_value_byid( $field->id, $user_id );
    567 
    568                                 for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
    569 
    570                                         // Check for updated posted values, but errors preventing them from being saved first time
    571                                         if ( isset( $_POST['field_' . $field->id] ) && $option_value != $_POST['field_' . $field->id] ) {
    572                                                 if ( !empty( $_POST['field_' . $field->id] ) ) {
    573                                                         $option_value = $_POST['field_' . $field->id];
    574                                                 }
    575                                         }
    576 
    577                                         // Run the allowed option name through the before_save
    578                                         // filter, so we'll be sure to get a match
    579                                         $allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
    580                                         $selected        = '';
    581 
    582                                         if ( $option_value == $allowed_options || ( empty( $option_value ) && !empty( $options[$k]->is_default_option ) ) )
    583                                                 $selected = ' checked="checked"';
    584 
    585                                         $html .= apply_filters( 'bp_get_the_profile_field_options_radio', '<label><input' . $selected . ' type="radio" name="field_' . $field->id . '" id="option_' . $options[$k]->id . '" value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '"> ' . esc_attr( stripslashes( $options[$k]->name ) ) . '</label>', $options[$k], $field->id, $selected, $k );
    586                                 }
    587 
    588                                 $html .= '</div>';
    589                                 break;
    590 
    591                         case 'checkbox':
    592                                 $option_values = BP_XProfile_ProfileData::get_value_byid( $field->id, $user_id );
    593                                 $option_values = (array) maybe_unserialize( $option_values );
    594 
    595                                 // Check for updated posted values, but errors preventing them from being saved first time
    596                                 if ( isset( $_POST['field_' . $field->id] ) && $option_values != maybe_serialize( $_POST['field_' . $field->id] ) ) {
    597                                         if ( !empty( $_POST['field_' . $field->id] ) )
    598                                                 $option_values = $_POST['field_' . $field->id];
    599                                 }
    600 
    601                                 for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
    602                                         $selected = '';
    603 
    604                                         // First, check to see whether the user's saved values
    605                                         // match the option
    606                                         for ( $j = 0, $count_values = count( $option_values ); $j < $count_values; ++$j ) {
    607 
    608                                                 // Run the allowed option name through the
    609                                                 // before_save filter, so we'll be sure to get a match
    610                                                 $allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
    611 
    612                                                 if ( $option_values[$j] == $allowed_options || @in_array( $allowed_options, $option_values ) ) {
    613                                                         $selected = ' checked="checked"';
    614                                                         break;
    615                                                 }
    616                                         }
    617 
    618                                         // If the user has not yet supplied a value for this field,
    619                                         // check to see whether there is a default value available
    620                                         if ( !is_array( $option_values ) && empty( $option_values ) && empty( $selected ) && !empty( $options[$k]->is_default_option ) ) {
    621                                                 $selected = ' checked="checked"';
    622                                         }
    623 
    624                                         $html .= apply_filters( 'bp_get_the_profile_field_options_checkbox', '<label><input' . $selected . ' type="checkbox" name="field_' . $field->id . '[]" id="field_' . $options[$k]->id . '_' . $k . '" value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '"> ' . esc_attr( stripslashes( $options[$k]->name ) ) . '</label>', $options[$k], $field->id, $selected, $k );
    625                                 }
    626                                 break;
    627 
    628                         case 'datebox':
    629                                 $date = BP_XProfile_ProfileData::get_value_byid( $field->id, $user_id );
    630 
    631                                 // Set day, month, year defaults
    632                                 $day   = '';
    633                                 $month = '';
    634                                 $year  = '';
    635 
    636                                 if ( !empty( $date ) ) {
    637 
    638                                         // If Unix timestamp
    639                                         if ( is_numeric( $date ) ) {
    640                                                 $day   = date( 'j', $date );
    641                                                 $month = date( 'F', $date );
    642                                                 $year  = date( 'Y', $date );
    643 
    644                                         // If MySQL timestamp
    645                                         } else {
    646                                                 $day   = mysql2date( 'j', $date );
    647                                                 $month = mysql2date( 'F', $date, false ); // Not localized, so that selected() works below
    648                                                 $year  = mysql2date( 'Y', $date );
    649                                         }
    650                                 }
    651 
    652                                 // Check for updated posted values, and errors preventing
    653                                 // them from being saved first time.
    654                                 if ( !empty( $_POST['field_' . $field->id . '_day'] ) ) {
    655                                         if ( $day != $_POST['field_' . $field->id . '_day'] ) {
    656                                                 $day = $_POST['field_' . $field->id . '_day'];
    657                                         }
    658                                 }
    659 
    660                                 if ( !empty( $_POST['field_' . $field->id . '_month'] ) ) {
    661                                         if ( $month != $_POST['field_' . $field->id . '_month'] ) {
    662                                                 $month = $_POST['field_' . $field->id . '_month'];
    663                                         }
    664                                 }
    665 
    666                                 if ( !empty( $_POST['field_' . $field->id . '_year'] ) ) {
    667                                         if ( $year != date( "j", $_POST['field_' . $field->id . '_year'] ) ) {
    668                                                 $year = $_POST['field_' . $field->id . '_year'];
    669                                         }
    670                                 }
    671 
    672                                 // $type will be passed by calling function when needed
    673                                 switch ( $type ) {
    674                                         case 'day':
    675                                                 $html .= '<option value=""' . selected( $day, '', false ) . '>--</option>';
    676 
    677                                                 for ( $i = 1; $i < 32; ++$i ) {
    678                                                         $html .= '<option value="' . $i .'"' . selected( $day, $i, false ) . '>' . $i . '</option>';
    679                                                 }
    680                                                 break;
    681 
    682                                         case 'month':
    683                                                 $eng_months = array( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' );
    684 
    685                                                 $months = array(
    686                                                         __( 'January', 'buddypress' ),
    687                                                         __( 'February', 'buddypress' ),
    688                                                         __( 'March', 'buddypress' ),
    689                                                         __( 'April', 'buddypress' ),
    690                                                         __( 'May', 'buddypress' ),
    691                                                         __( 'June', 'buddypress' ),
    692                                                         __( 'July', 'buddypress' ),
    693                                                         __( 'August', 'buddypress' ),
    694                                                         __( 'September', 'buddypress' ),
    695                                                         __( 'October', 'buddypress' ),
    696                                                         __( 'November', 'buddypress' ),
    697                                                         __( 'December', 'buddypress' )
    698                                                 );
    699 
    700                                                 $html .= '<option value=""' . selected( $month, '', false ) . '>------</option>';
    701 
    702                                                 for ( $i = 0; $i < 12; ++$i ) {
    703                                                         $html .= '<option value="' . $eng_months[$i] . '"' . selected( $month, $eng_months[$i], false ) . '>' . $months[$i] . '</option>';
    704                                                 }
    705                                                 break;
    706 
    707                                         case 'year':
    708                                                 $html .= '<option value=""' . selected( $year, '', false ) . '>----</option>';
    709 
    710                                                 for ( $i = 2037; $i > 1901; $i-- ) {
    711                                                         $html .= '<option value="' . $i .'"' . selected( $year, $i, false ) . '>' . $i . '</option>';
    712                                                 }
    713                                                 break;
    714                                 }
    715 
    716                                 $html = apply_filters( 'bp_get_the_profile_field_datebox', $html, $type, $day, $month, $year, $field->id, $date );
    717 
    718                                 break;
    719                 }
     468                ob_start();
     469                $field->type_obj->edit_field_options_html( $args );
     470                $html = ob_get_contents();
     471                ob_end_clean();
    720472
    721473                return $html;
Note: See TracChangeset for help on using the changeset viewer.