Skip to:
Content

BuddyPress.org

Changeset 8178


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

Location:
trunk
Files:
1 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/bp-members/admin/css/admin.css

    r7963 r8178  
    1414    border-bottom:solid 1px #ccc;
    1515    width:100%;
    16 }
    17 
    18 div#community-profile-page h2:first-of-type {
    19     margin-bottom:1em;
    20 }
    21 
    22 div#community-profile-page  h2.profile-section {
    23     border-bottom:dotted 1px #ccc;
    2416}
    2517
     
    10597}
    10698
    107 div#community-profile-page .form-table td.admin-field-visibility-td {
    108     padding:5px 5px 15px 5px;
     99.alt {
     100    background: none;
     101}
     102.bp-profile-field {
     103    border-bottom: dotted 1px #ccc;
     104    font-size: 14px;
     105    margin: 15px 0;
     106}
     107.bp-profile-field:last-child {
     108    border-bottom: 0;
     109}
     110.bp-profile-field p {
     111    font-size: 14px;
     112}
     113.bp-profile-field .datebox > label,
     114.bp-profile-field .radio > label,
     115.bp-profile-field > label {  /* label takes on left side 200px */
     116    display: inline-block;
     117    font-weight: 600;
     118    text-align: left;
     119    vertical-align: middle;
     120    width: 200px;
    109121}
    110122
    111 div#community-profile-page .form-table tr.admin-field-visibility-tr {
    112     border-bottom:dotted 1px #ccc;
     123.field_type_textarea > label,
     124.field_type_multiselectbox > label,
     125.field_type_radio .radio > label,
     126.field_type_checkbox .checkbox > label {  /* these fields are usually pretty tall, so align the label for better consistency */
     127    vertical-align: top;
    113128}
    114 
    115 div#community-profile-page .form-table tr.admin-field-visibility-tr:last-child{
    116     border:none;
     129.bp-profile-field .description {  /* description also sits in the right side column */
     130    margin: 10px 0 10px 200px;
     131    text-align: left;
    117132}
    118 
    119 div#community-profile-page .field-visibility-settings legend,
    120 div#community-profile-page .field-visibility-settings-notoggle {
    121     font-size: 14px;
    122     font-style: italic;
    123     color:#666;
     133.clear-value {  /* 'clear value' option also sits in the right side column */
     134    display: block;
     135    font-size: 12px;
     136    margin-left: 200px;
    124137}
    125 
    126 div#community-profile-page .field-visibility-settings ul {
    127     margin:0;
    128     font-size: 13px;
     138.field_type_checkbox .checkbox > label + label {  /* force checkboxes to new lines, in the right side column */
     139    display: block;
     140    margin-left: 200px;
     141    width: auto;
    129142}
    130 
    131 div#community-profile-page .field-visibility-settings ul li {
    132     display:inline-block;
    133     margin-right:1em;
     143.field_type_radio .radio div:not(.field-visibility-settings) label {  /* force radio buttons to new lines */
     144    display: block;
    134145}
    135 
    136 div#community-profile-page .form-table td.admin-field-radio label,
    137 div#community-profile-page .form-table td.admin-field-checkbox label {
    138     display:block;
     146.field_type_radio .radio div:not(.field-visibility-settings) {  /* make the radio buttons sit in the right side column */
     147    display: inline-block;
    139148}
    140 
    141 .field-visibility-settings {
     149.field-visibility-settings-notoggle,
     150.field-visibility-settings-toggle {  /* visibility settings go in the left column */
     151    margin: 10px 0 10px 200px;
     152    text-align: left;
     153}
     154.field-visibility-settings {  /* visibility settings go in the left column */
    142155    display: none;
     156    margin-left: 200px;
    143157}
     158.field-visibility-settings .button {  /* visibility setting close button */
     159    margin-bottom: 15px;
     160}
     161#normal-sortables .field-visibility-settings legend {  /* id required for css selector weight */
     162    font-size: 16px;
     163    margin-bottom: 10px;
     164}
  • trunk/bp-templates/bp-legacy/buddypress/members/register.php

    r7965 r8178  
    6969                        <div class="editfield">
    7070
    71                             <?php if ( 'textbox' == bp_get_the_profile_field_type() ) : ?>
     71                            <?php
     72                            $field_type = bp_xprofile_create_field_type( bp_get_the_profile_field_type() );
     73                            $field_type->edit_field_html();
    7274
    73                                 <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    74                                 <?php do_action( bp_get_the_profile_field_errors_action() ); ?>
    75                                 <input type="text" name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" value="<?php bp_the_profile_field_edit_value(); ?>" />
     75                            do_action( 'bp_custom_profile_edit_fields_pre_visibility' );
    7676
    77                             <?php endif; ?>
    78 
    79                             <?php if ( 'textarea' == bp_get_the_profile_field_type() ) : ?>
    80 
    81                                 <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    82                                 <?php do_action( bp_get_the_profile_field_errors_action() ); ?>
    83                                 <textarea rows="5" cols="40" name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_edit_value(); ?></textarea>
    84 
    85                             <?php endif; ?>
    86 
    87                             <?php if ( 'selectbox' == bp_get_the_profile_field_type() ) : ?>
    88 
    89                                 <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    90                                 <?php do_action( bp_get_the_profile_field_errors_action() ); ?>
    91                                 <select name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>">
    92                                     <?php bp_the_profile_field_options(); ?>
    93                                 </select>
    94 
    95                             <?php endif; ?>
    96 
    97                             <?php if ( 'multiselectbox' == bp_get_the_profile_field_type() ) : ?>
    98 
    99                                 <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    100                                 <?php do_action( bp_get_the_profile_field_errors_action() ); ?>
    101                                 <select name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" multiple="multiple">
    102                                     <?php bp_the_profile_field_options(); ?>
    103                                 </select>
    104 
    105                             <?php endif; ?>
    106 
    107                             <?php if ( 'radio' == bp_get_the_profile_field_type() ) : ?>
    108 
    109                                 <div class="radio">
    110                                     <span class="label"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></span>
    111 
    112                                     <?php do_action( bp_get_the_profile_field_errors_action() ); ?>
    113                                     <?php bp_the_profile_field_options(); ?>
    114 
    115                                     <?php if ( !bp_get_the_profile_field_is_required() ) : ?>
    116                                         <a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name(); ?>' );"><?php _e( 'Clear', 'buddypress' ); ?></a>
    117                                     <?php endif; ?>
    118                                 </div>
    119 
    120                             <?php endif; ?>
    121 
    122                             <?php if ( 'checkbox' == bp_get_the_profile_field_type() ) : ?>
    123 
    124                                 <div class="checkbox">
    125                                     <span class="label"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></span>
    126 
    127                                     <?php do_action( bp_get_the_profile_field_errors_action() ); ?>
    128                                     <?php bp_the_profile_field_options(); ?>
    129                                 </div>
    130 
    131                             <?php endif; ?>
    132 
    133                             <?php if ( 'datebox' == bp_get_the_profile_field_type() ) : ?>
    134 
    135                                 <div class="datebox">
    136                                     <label for="<?php bp_the_profile_field_input_name(); ?>_day"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    137                                     <?php do_action( bp_get_the_profile_field_errors_action() ); ?>
    138 
    139                                     <select name="<?php bp_the_profile_field_input_name(); ?>_day" id="<?php bp_the_profile_field_input_name(); ?>_day">
    140                                         <?php bp_the_profile_field_options( 'type=day' ); ?>
    141                                     </select>
    142 
    143                                     <select name="<?php bp_the_profile_field_input_name(); ?>_month" id="<?php bp_the_profile_field_input_name(); ?>_month">
    144                                         <?php bp_the_profile_field_options( 'type=month' ); ?>
    145                                     </select>
    146 
    147                                     <select name="<?php bp_the_profile_field_input_name(); ?>_year" id="<?php bp_the_profile_field_input_name(); ?>_year">
    148                                         <?php bp_the_profile_field_options( 'type=year' ); ?>
    149                                     </select>
    150                                 </div>
    151 
    152                             <?php endif; ?>
    153 
    154                             <?php do_action( 'bp_custom_profile_edit_fields_pre_visibility' ); ?>
    155 
    156                             <?php if ( bp_current_user_can( 'bp_xprofile_change_field_visibility' ) ) : ?>
     77                            if ( bp_current_user_can( 'bp_xprofile_change_field_visibility' ) ) : ?>
    15778                                <p class="field-visibility-settings-toggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
    15879                                    <?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?> <a href="#" class="visibility-toggle-link"><?php _ex( 'Change', 'Change profile field visibility level', 'buddypress' ); ?></a>
  • trunk/bp-templates/bp-legacy/buddypress/members/single/profile/edit.php

    r7965 r8178  
    2222            <div<?php bp_field_css_class( 'editfield' ); ?>>
    2323
    24                 <?php if ( 'textbox' == bp_get_the_profile_field_type() ) : ?>
     24                <?php
     25                $field_type = bp_xprofile_create_field_type( bp_get_the_profile_field_type() );
     26                $field_type->edit_field_html();
    2527
    26                     <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    27                     <input type="text" name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" value="<?php bp_the_profile_field_edit_value(); ?>" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>/>
    28 
    29                 <?php endif; ?>
    30 
    31                 <?php if ( 'textarea' == bp_get_the_profile_field_type() ) : ?>
    32 
    33                     <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    34                     <textarea rows="5" cols="40" name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>><?php bp_the_profile_field_edit_value(); ?></textarea>
    35 
    36                 <?php endif; ?>
    37 
    38                 <?php if ( 'selectbox' == bp_get_the_profile_field_type() ) : ?>
    39 
    40                     <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    41                     <select name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    42                         <?php bp_the_profile_field_options(); ?>
    43                     </select>
    44 
    45                 <?php endif; ?>
    46 
    47                 <?php if ( 'multiselectbox' == bp_get_the_profile_field_type() ) : ?>
    48 
    49                     <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    50                     <select name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" multiple="multiple" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    51 
    52                         <?php bp_the_profile_field_options(); ?>
    53 
    54                     </select>
    55 
    56                     <?php if ( !bp_get_the_profile_field_is_required() ) : ?>
    57 
    58                         <a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name(); ?>' );"><?php _e( 'Clear', 'buddypress' ); ?></a>
    59 
    60                     <?php endif; ?>
    61 
    62                 <?php endif; ?>
    63 
    64                 <?php if ( 'radio' == bp_get_the_profile_field_type() ) : ?>
    65 
    66                     <div class="radio">
    67                         <span class="label"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></span>
    68 
    69                         <?php bp_the_profile_field_options(); ?>
    70 
    71                         <?php if ( !bp_get_the_profile_field_is_required() ) : ?>
    72 
    73                             <a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name(); ?>' );"><?php _e( 'Clear', 'buddypress' ); ?></a>
    74 
    75                         <?php endif; ?>
    76                     </div>
    77 
    78                 <?php endif; ?>
    79 
    80                 <?php if ( 'checkbox' == bp_get_the_profile_field_type() ) : ?>
    81 
    82                     <div class="checkbox">
    83                         <span class="label"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></span>
    84 
    85                         <?php bp_the_profile_field_options(); ?>
    86                     </div>
    87 
    88                 <?php endif; ?>
    89 
    90                 <?php if ( 'datebox' == bp_get_the_profile_field_type() ) : ?>
    91 
    92                     <div class="datebox">
    93                         <label for="<?php bp_the_profile_field_input_name(); ?>_day"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    94 
    95                         <select name="<?php bp_the_profile_field_input_name(); ?>_day" id="<?php bp_the_profile_field_input_name(); ?>_day" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    96 
    97                             <?php bp_the_profile_field_options( 'type=day' ); ?>
    98 
    99                         </select>
    100 
    101                         <select name="<?php bp_the_profile_field_input_name(); ?>_month" id="<?php bp_the_profile_field_input_name(); ?>_month" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    102 
    103                             <?php bp_the_profile_field_options( 'type=month' ); ?>
    104 
    105                         </select>
    106 
    107                         <select name="<?php bp_the_profile_field_input_name(); ?>_year" id="<?php bp_the_profile_field_input_name(); ?>_year" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    108 
    109                             <?php bp_the_profile_field_options( 'type=year' ); ?>
    110 
    111                         </select>
    112                     </div>
    113 
    114                 <?php endif; ?>
    115 
    116                 <?php do_action( 'bp_custom_profile_edit_fields_pre_visibility' ); ?>
     28                do_action( 'bp_custom_profile_edit_fields_pre_visibility' );
     29                ?>
    11730
    11831                <?php if ( bp_current_user_can( 'bp_xprofile_change_field_visibility' ) ) : ?>
  • trunk/bp-templates/bp-legacy/css/buddypress.css

    r8063 r8178  
    608608    margin: 15px 0 5px 0;
    609609}
    610 #buddypress .standard-form div.checkbox label,
    611 #buddypress .standard-form div.radio label {
     610#buddypress .standard-form div.checkbox label:nth-child(n+2),
     611#buddypress .standard-form div.radio div label {
    612612    color: #888;
    613613    font-size: 100%;
  • trunk/bp-xprofile/bp-xprofile-admin.php

    r8028 r8178  
    383383        <div class="field-wrapper">
    384384
    385             <?php switch ( $field->type ) : case 'textbox' : ?>
    386 
    387                 <input type="text" name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>" value="" />
    388 
    389             <?php break; case 'textarea' : ?>
    390 
    391                 <textarea rows="5" cols="40" name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>"></textarea>
    392 
    393             <?php break; case 'selectbox' : ?>
    394 
    395                 <select name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>">
    396 
    397                     <?php bp_the_profile_field_options() ?>
    398 
    399                 </select>
    400 
    401             <?php break; case 'multiselectbox' : ?>
    402 
    403                 <select name="<?php bp_the_profile_field_input_name() ?>" id="<?php bp_the_profile_field_input_name() ?>" multiple="multiple">
    404 
    405                     <?php bp_the_profile_field_options() ?>
    406 
    407                 </select>
    408 
    409             <?php break; case 'radio' : ?>
    410 
    411                 <?php bp_the_profile_field_options() ?>
    412 
    413                 <?php if ( !bp_get_the_profile_field_is_required() ) : ?>
    414 
    415                     <a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name() ?>' );"><?php _e( 'Clear', 'buddypress' ) ?></a>
    416 
    417                 <?php endif; ?>
    418 
    419             <?php break; case 'checkbox' : ?>
    420 
    421                 <?php bp_the_profile_field_options(); ?>
    422 
    423             <?php break; case 'datebox' : ?>
    424 
    425                 <select name="<?php bp_the_profile_field_input_name(); ?>_day" id="<?php bp_the_profile_field_input_name(); ?>_day">
    426 
    427                     <?php bp_the_profile_field_options( 'type=day' ); ?>
    428 
    429                 </select>
    430 
    431                 <select name="<?php bp_the_profile_field_input_name(); ?>_month" id="<?php bp_the_profile_field_input_name(); ?>_month">
    432 
    433                     <?php bp_the_profile_field_options( 'type=month' ); ?>
    434 
    435                 </select>
    436 
    437                 <select name="<?php bp_the_profile_field_input_name(); ?>_year" id="<?php bp_the_profile_field_input_name(); ?>_year">
    438 
    439                     <?php bp_the_profile_field_options( 'type=year' ); ?>
    440 
    441                 </select>
    442 
    443             <?php break; default : ?>
    444 
    445             <?php do_action( 'xprofile_admin_field', $field, 1 ); ?>
    446 
    447             <?php endswitch; ?>
     385            <?php
     386            $field_type = bp_xprofile_create_field_type( $field->type );
     387            $field_type->admin_field_html();
     388
     389            do_action( 'xprofile_admin_field', $field, 1 );
     390            ?>
    448391
    449392            <?php if ( $field->description ) : ?>
     
    466409
    467410<?php
     411}
     412
     413/**
     414 * Print <option> elements containing the xprofile field types.
     415 *
     416 * @param string $select_field_type The name of the field type that should be selected. Will defaults to "textbox" if NULL is passed.
     417 * @since BuddyPress (2.0.0)
     418 */
     419function bp_xprofile_admin_form_field_types( $select_field_type ) {
     420    $categories = array();
     421
     422    if ( is_null( $select_field_type ) ) {
     423        $select_field_type = 'textbox';
     424    }
     425
     426    // Sort each field type into its category
     427    foreach ( bp_xprofile_get_field_types() as $field_name => $field_class ) {
     428        $field_type_obj = new $field_class;
     429        $the_category   = $field_type_obj->category;
     430
     431        // Fallback to a catch-all if category not set
     432        if ( ! $the_category ) {
     433            $the_category = _x( 'Other', 'xprofile field type category', 'buddypress' );
     434        }
     435
     436        if ( isset( $categories[$the_category] ) ) {
     437            $categories[$the_category][] = array( $field_name, $field_type_obj );
     438        } else {
     439            $categories[$the_category] = array( array( $field_name, $field_type_obj ) );
     440        }
     441    }
     442
     443    // Sort the categories alphabetically. ksort()'s SORT_NATURAL is only in PHP >= 5.4 :((
     444    uksort( $categories, 'strnatcmp' );
     445
     446    // Loop through each category and output form <options>
     447    foreach ( $categories as $category => $fields ) {
     448        printf( '<optgroup label="%1$s">', esc_attr( $category ) );  // Already i18n'd in each profile type class
     449
     450        // Sort these fields types alphabetically
     451        uasort( $fields, create_function( '$a, $b', 'return strnatcmp( $a[1]->name, $b[1]->name );' ) );
     452
     453        foreach ( $fields as $field_type_obj ) {
     454            $field_name     = $field_type_obj[0];
     455            $field_type_obj = $field_type_obj[1];
     456
     457            printf( '<option value="%1$s" %2$s>%3$s</option>', esc_attr( $field_name ), selected( $select_field_type, $field_name, false ), esc_html( $field_type_obj->name ) );
     458        }
     459
     460        printf( '</optgroup>' );
     461    }
    468462}
    469463
     
    643637                $errors = false;
    644638
    645                 // Now we've checked for required fields, lets save the values.
     639                // Now we've checked for required fields, let's save the values.
    646640                foreach ( (array) $posted_field_ids as $field_id ) {
    647641
    648642                    // Certain types of fields (checkboxes, multiselects) may come through empty. Save them as an empty array so that they don't get overwritten by the default on the next edit.
    649                     if ( empty( $_POST['field_' . $field_id] ) ) {
    650                         $value = array();
    651                     } else {
    652                         $value = $_POST['field_' . $field_id];
    653                     }
     643                    $value = isset( $_POST['field_' . $field_id] ) ? $_POST['field_' . $field_id] : '';
    654644
    655645                    if ( ! xprofile_set_field_data( $field_id, $user_id, $value, $is_required[ $field_id ] ) ) {
     
    703693
    704694        if ( bp_has_profile( $r ) ) :
    705 
    706695            while ( bp_profile_groups() ) : bp_the_profile_group(); ?>
    707 
    708                 <p class="description"><?php bp_the_profile_group_description(); ?></p>
    709 
    710                 <table class="form-table">
    711                     <tbody>
    712 
    713                     <?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
    714 
    715                         <tr>
    716 
    717                             <?php if ( 'textbox' === bp_get_the_profile_field_type() ) : ?>
    718 
    719                                 <th><label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label></th>
    720                                 <td class="admin-field-<?php bp_the_profile_field_type();?>">
    721                                     <input type="text" name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" value="<?php bp_the_profile_field_edit_value(); ?>" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>/>
    722                                     <span class="description"><?php bp_the_profile_field_description(); ?></span>
    723                                 </td>
    724 
     696                <input type="hidden" name="field_ids[]" id="<?php echo esc_attr( 'field_ids_' . bp_get_the_profile_group_slug() ); ?>" value="<?php echo esc_attr( bp_get_the_profile_group_field_ids() ); ?>" />
     697
     698                <?php if ( bp_get_the_profile_group_description() ) : ?>
     699                    <p class="description"><?php bp_the_profile_group_description(); ?></p>
     700                <?php
     701                endif;
     702
     703                while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
     704
     705                    <div<?php bp_field_css_class( 'bp-profile-field' ); ?>>
     706                        <?php
     707                        $field_type = bp_xprofile_create_field_type( bp_get_the_profile_field_type() );
     708                        $field_type->edit_field_html( array( 'user_id' => $r['user_id'] ) );
     709
     710                        if ( bp_get_the_profile_field_description() ) : ?>
     711                            <p class="description"><?php bp_the_profile_field_description(); ?></p>
     712                        <?php endif;
     713
     714                        do_action( 'bp_custom_profile_edit_fields_pre_visibility' );
     715                        $can_change_visibility = bp_current_user_can( 'bp_xprofile_change_field_visibility' );
     716                        ?>
     717
     718                        <p class="field-visibility-settings-<?php echo $can_change_visibility ? 'toggle' : 'notoggle'; ?>" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id(); ?>">
     719                            <?php
     720                            printf( __( 'This field can be seen by: <span class="%s">%s</span>', 'buddypress' ), esc_attr( 'current-visibility-level' ), bp_get_the_profile_field_visibility_level_label() );
     721
     722                            if ( $can_change_visibility ) : ?>
     723                                 <a href="#" class="button visibility-toggle-link"><?php _e( 'Change', 'buddypress' ); ?></a>
    725724                            <?php endif; ?>
    726 
    727                             <?php if ( 'textarea' === bp_get_the_profile_field_type() ) : ?>
    728 
    729                                 <th><label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label></th>
    730                                 <td class="admin-field-<?php bp_the_profile_field_type();?>">
    731                                     <textarea rows="5" cols="40" name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>><?php bp_the_profile_field_edit_value(); ?></textarea>
    732                                     <p class="description"><?php bp_the_profile_field_description(); ?></p>
    733                                 </td>
    734 
    735                             <?php endif; ?>
    736 
    737                             <?php if ( 'selectbox' === bp_get_the_profile_field_type() ) : ?>
    738 
    739                                 <th><label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label></th>
    740                                 <td class="admin-field-<?php bp_the_profile_field_type();?>">
    741                                     <select name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    742                                         <?php bp_the_profile_field_options( array( 'user_id' => $r['user_id'], ) ); ?>
    743                                     </select>
    744                                     <span class="description"><?php bp_the_profile_field_description(); ?></span>
    745                                 </td>
    746 
    747                             <?php endif; ?>
    748 
    749                             <?php if ( 'multiselectbox' === bp_get_the_profile_field_type() ) : ?>
    750 
    751                                 <th><label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label></th>
    752                                 <td class="admin-field-<?php bp_the_profile_field_type();?>">
    753                                     <select name="<?php bp_the_profile_field_input_name(); ?>" id="<?php bp_the_profile_field_input_name(); ?>" multiple="multiple" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    754 
    755                                         <?php bp_the_profile_field_options( array( 'user_id' => $r['user_id'], ) ); ?>
    756 
    757                                     </select>
    758 
    759 
    760                                     <?php if ( !bp_get_the_profile_field_is_required() ) : ?>
    761 
    762                                         <p><a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name(); ?>' );"><?php _e( 'Clear', 'buddypress' ); ?></a></p>
    763 
    764                                     <?php endif; ?>
    765                                     <p class="description"><?php bp_the_profile_field_description(); ?></p>
    766                                 </td>
    767 
    768                             <?php endif; ?>
    769 
    770                             <?php if ( 'radio' === bp_get_the_profile_field_type() ) : ?>
    771 
    772                                 <th>
    773                                     <span class="label"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></span>
    774                                 </th>
    775                                 <td class="admin-field-<?php bp_the_profile_field_type();?>">
    776                                     <fieldset>
    777                                         <legend class="screen-reader-text"><span><?php bp_the_profile_field_name(); ?></span></legend>
    778                                         <?php bp_the_profile_field_options( array( 'user_id' => $r['user_id'], ) ); ?>
    779                                     </fieldset>
    780 
    781                                     <?php if ( !bp_get_the_profile_field_is_required() ) : ?>
    782 
    783                                         <p><a class="clear-value" href="javascript:clear( '<?php bp_the_profile_field_input_name(); ?>' );"><?php _e( 'Clear', 'buddypress' ); ?></a></p>
    784 
    785                                     <?php endif; ?>
    786                                     <p class="description"><?php bp_the_profile_field_description(); ?></p>
    787                                 </td>
    788 
    789                             <?php endif; ?>
    790 
    791                             <?php if ( 'checkbox' === bp_get_the_profile_field_type() ) : ?>
    792 
    793                                 <th>
    794                                     <span class="label"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></span>
    795                                 </th>
    796                                 <td class="admin-field-<?php bp_the_profile_field_type();?>">
    797                                     <?php bp_the_profile_field_options( array( 'user_id' => $r['user_id'], ) ); ?>
    798                                     <p class="description"><?php bp_the_profile_field_description(); ?></p>
    799                                 </td>
    800 
    801                             <?php endif; ?>
    802 
    803                             <?php if ( 'datebox' === bp_get_the_profile_field_type() ) : ?>
    804 
    805                                 <th>
    806                                     <label for="<?php bp_the_profile_field_input_name(); ?>_day"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
    807                                 </th>
    808                                 <td class="admin-field-<?php bp_the_profile_field_type();?>">
    809                                     <select name="<?php bp_the_profile_field_input_name(); ?>_day" id="<?php bp_the_profile_field_input_name(); ?>_day" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    810 
    811                                         <?php bp_the_profile_field_options( array( 'user_id' => $r['user_id'], 'type' => 'day', ) ); ?>
    812 
    813                                     </select>
    814 
    815                                     <select name="<?php bp_the_profile_field_input_name(); ?>_month" id="<?php bp_the_profile_field_input_name(); ?>_month" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    816 
    817                                         <?php bp_the_profile_field_options( array( 'user_id' => $r['user_id'], 'type' => 'month', ) ); ?>
    818 
    819                                     </select>
    820 
    821                                     <select name="<?php bp_the_profile_field_input_name(); ?>_year" id="<?php bp_the_profile_field_input_name(); ?>_year" <?php if ( bp_get_the_profile_field_is_required() ) : ?>aria-required="true"<?php endif; ?>>
    822 
    823                                         <?php bp_the_profile_field_options( array( 'user_id' => $r['user_id'], 'type' => 'year', ) ); ?>
    824 
    825                                     </select>
    826                                     <p class="description"><?php bp_the_profile_field_description(); ?></p>
    827                                 </td>
    828 
    829                             <?php endif; ?>
    830 
    831                         </tr>
    832 
    833                         <tr class="admin-field-visibility-tr">
    834                             <td class="admin-field-visibility-td">&nbsp;</td>
    835                             <td class="admin-field-visibility-td">
    836 
    837                                 <?php do_action( 'bp_custom_profile_edit_fields_pre_visibility' ); ?>
    838 
    839                                 <?php if ( bp_current_user_can( 'bp_xprofile_change_field_visibility' ) ) : ?>
    840                                     <p class="description field-visibility-settings-toggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
    841                                         <?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?> <a href="#" class="visibility-toggle-link"><?php _e( 'Change', 'buddypress' ); ?></a>
    842                                     </p>
    843 
    844                                     <div class="field-visibility-settings" id="field-visibility-settings-<?php bp_the_profile_field_id() ?>">
    845                                         <fieldset>
    846                                             <legend><?php esc_html_e( 'Who can see this field?', 'buddypress' ) ?></legend>
    847 
    848                                             <?php bp_profile_visibility_radio_buttons() ?>
    849 
    850                                         </fieldset>
    851                                         <a class="field-visibility-settings-close" href="#"><?php esc_html_e( 'Close', 'buddypress' ) ?></a>
    852                                     </div>
    853                                 <?php else : ?>
    854                                     <div class="field-visibility-settings-notoggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
    855                                         <?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?>
    856                                     </div>
    857                                 <?php endif ?>
    858 
    859                             </td>
    860                         </tr>
    861 
    862                     <?php endwhile; ?>
    863                     </tbody>
    864 
    865                 </table>
    866                 <input type="hidden" name="field_ids[]" id="field_ids_<?php bp_the_profile_group_slug(); ?>" value="<?php bp_the_profile_group_field_ids(); ?>" />
    867             <?php endwhile;
     725                        </p>
     726
     727                        <?php if ( $can_change_visibility ) : ?>
     728                            <div class="field-visibility-settings" id="field-visibility-settings-<?php bp_the_profile_field_id() ?>">
     729                                <fieldset>
     730                                    <legend><?php _e( 'Who can see this field?', 'buddypress' ); ?></legend>
     731                                    <?php bp_profile_visibility_radio_buttons(); ?>
     732                                </fieldset>
     733                                <a class="button field-visibility-settings-close" href="#"><?php _e( 'Close', 'buddypress' ); ?></a>
     734                            </div>
     735                        <?php endif;
     736
     737                        do_action( 'bp_custom_profile_edit_fields' ); ?>
     738                    </div>
     739
     740                <?php
     741                endwhile; // bp_profile_fields()
     742
     743            endwhile; // bp_profile_groups()
    868744        endif;
    869745    }
  • trunk/bp-xprofile/bp-xprofile-classes.php

    r8165 r8178  
    542542    public $allow_custom_visibility = 'allowed';
    543543
     544    /**
     545     * @since BuddyPress (2.0.0)
     546     * @var BP_XProfile_Field_Type Field type object used for validation
     547     */
     548    public $type_obj = null;
     549
    544550    public $data;
    545551    public $message = null;
     
    547553
    548554    public function __construct( $id = null, $user_id = null, $get_data = true ) {
    549         if ( !empty( $id ) )
     555        if ( !empty( $id ) ) {
    550556            $this->populate( $id, $user_id, $get_data );
     557
     558        // Initialise the type obj to prevent fatals when creating new profile fields
     559        } else {
     560            $this->type_obj            = bp_xprofile_create_field_type( 'textbox' );
     561            $this->type_obj->field_obj = $this;
     562        }
    551563    }
    552564
     
    574586            $this->is_default_option = $field->is_default_option;
    575587
     588            // Create the field type and store a reference back to this object.
     589            $this->type_obj            = bp_xprofile_create_field_type( $field->type );
     590            $this->type_obj->field_obj = $this;
     591
    576592            if ( $get_data && $user_id ) {
    577593                $this->data          = $this->get_field_data( $user_id );
     
    619635        $this->field_order = apply_filters( 'xprofile_field_field_order_before_save', $this->field_order, $this->id );
    620636        $this->can_delete  = apply_filters( 'xprofile_field_can_delete_before_save',  $this->can_delete,  $this->id );
     637        $this->type_obj    = bp_xprofile_create_field_type( $this->type );
    621638
    622639        do_action_ref_array( 'xprofile_field_before_save', array( $this ) );
     
    656673             * We need to add the options to the db, if it is.
    657674             */
    658             if ( 'radio' == $this->type || 'selectbox' == $this->type || 'checkbox' == $this->type || 'multiselectbox' == $this->type ) {
     675            if ( $this->type_obj->supports_options ) {
    659676
    660677                if ( !empty( $this->id ) ) {
     
    664681                }
    665682
    666                 if ( 'radio' == $this->type ) {
    667                     $post_option  = !empty( $_POST['radio_option']           ) ? $_POST['radio_option']           : '';
    668                     $post_default = !empty( $_POST['isDefault_radio_option'] ) ? $_POST['isDefault_radio_option'] : '';
    669 
    670                     $options    = apply_filters( 'xprofile_field_options_before_save', $post_option,  'radio' );
    671                     $defaults   = apply_filters( 'xprofile_field_default_before_save', $post_default, 'radio' );
    672 
    673                 } elseif ( 'selectbox' == $this->type ) {
    674                     $post_option  = !empty( $_POST['selectbox_option']           ) ? $_POST['selectbox_option']           : '';
    675                     $post_default = !empty( $_POST['isDefault_selectbox_option'] ) ? $_POST['isDefault_selectbox_option'] : '';
    676 
    677                     $options    = apply_filters( 'xprofile_field_options_before_save', $post_option, 'selectbox' );
    678                     $defaults   = apply_filters( 'xprofile_field_default_before_save', $post_default, 'selectbox' );
    679 
    680                 } elseif ( 'multiselectbox' == $this->type ) {
    681                     $post_option  = !empty( $_POST['multiselectbox_option']           ) ? $_POST['multiselectbox_option']           : '';
    682                     $post_default = !empty( $_POST['isDefault_multiselectbox_option'] ) ? $_POST['isDefault_multiselectbox_option'] : '';
    683 
    684                     $options    = apply_filters( 'xprofile_field_options_before_save', $post_option, 'multiselectbox' );
    685                     $defaults   = apply_filters( 'xprofile_field_default_before_save', $post_default, 'multiselectbox' );
    686 
    687                 } elseif ( 'checkbox' == $this->type ) {
    688                     $post_option  = !empty( $_POST['checkbox_option']           ) ? $_POST['checkbox_option']           : '';
    689                     $post_default = !empty( $_POST['isDefault_checkbox_option'] ) ? $_POST['isDefault_checkbox_option'] : '';
    690 
    691                     $options    = apply_filters( 'xprofile_field_options_before_save', $post_option, 'checkbox' );
    692                     $defaults   = apply_filters( 'xprofile_field_default_before_save', $post_default, 'checkbox' );
    693                 }
     683                // Allow plugins to filter the field's child options (i.e. the items in a selectbox).
     684                $post_option  = ! empty( $_POST["{$this->type}_option"] ) ? $_POST["{$this->type}_option"] : '';
     685                $post_default = ! empty( $_POST["isDefault_{$this->type}_option"] ) ? $_POST["isDefault_{$this->type}_option"] : '';
     686                $options      = apply_filters( 'xprofile_field_options_before_save', $post_option,  $this->type );
     687                $defaults     = apply_filters( 'xprofile_field_default_before_save', $post_default, $this->type );
    694688
    695689                $counter = 1;
     
    719713            do_action_ref_array( 'xprofile_field_after_save', array( $this ) );
    720714
     715            // Recreate type_obj in case someone changed $this->type via a filter
     716            $this->type_obj            = bp_xprofile_create_field_type( $this->type );
     717            $this->type_obj->field_obj = $this;
     718
    721719            return $field_id;
    722720        } else {
     
    825823    }
    826824
    827     /* ADMIN AREA HTML.
    828     * TODO: Get this out of here and replace with standard template loops
    829     */
    830 
    831     /* This function populates the items for radio buttons checkboxes and drop down boxes */
     825    /**
     826     * This function populates the items for radio buttons checkboxes and drop down boxes
     827     */
    832828    public function render_admin_form_children() {
    833         $input_types = array( 'checkbox', 'selectbox', 'multiselectbox', 'radio' );
    834 
    835         foreach ( $input_types as $type ) {
    836             $default_name = '';
    837 
    838             if ( ( 'multiselectbox' == $type ) || ( 'checkbox' == $type ) ) {
    839                 $default_input = 'checkbox';
    840             } else {
    841                 $default_input = 'radio';
    842             }
    843 
    844             $class = $this->type != $type ? 'display: none;' : '';
    845 
    846             if ( empty( $this->default_visibility ) ) {
    847                 $this->default_visibility = 'public';
    848             }
    849 
    850             ?>
    851 
    852             <div id="<?php echo esc_attr( $type ); ?>" class="postbox bp-options-box" style="<?php echo esc_attr( $class ); ?> margin-top: 15px;">
    853                 <h3><?php _e( 'Please enter options for this Field:', 'buddypress' ); ?></h3>
    854                 <div class="inside">
    855                     <p>
    856                         <label for="sort_order_<?php echo esc_attr( $type ); ?>"><?php _e( 'Sort Order:', 'buddypress' ); ?></label>
    857                         <select name="sort_order_<?php echo esc_attr( $type ); ?>" id="sort_order_<?php echo esc_attr( $type ); ?>" >
    858                             <option value="custom" <?php selected( 'custom', $this->order_by ); ?>><?php _e( 'Custom',     'buddypress' ); ?></option>
    859                             <option value="asc"    <?php selected( 'asc',    $this->order_by ); ?>><?php _e( 'Ascending',  'buddypress' ); ?></option>
    860                             <option value="desc"   <?php selected( 'desc',   $this->order_by ); ?>><?php _e( 'Descending', 'buddypress' ); ?></option>
    861                         </select>
    862                     </p>
    863 
    864                     <?php if ( !$options = $this->get_children( true ) ) {
    865 
    866                         $i = 1;
    867                         while ( isset( $_POST[$type . '_option'][$i] ) ) {
    868                             (array) $options[] = (object) array(
    869                                 'id'                => -1,
    870                                 'name'              => $_POST[$type . '_option'][$i],
    871                                 'is_default_option' => ( ( 'multiselectbox' != $type ) && ( 'checkbox' != $type ) && ( $_POST["isDefault_{$type}_option"] == $i ) ) ? 1 : $_POST["isDefault_{$type}_option"][$i]
    872                             );
    873 
    874                             ++$i;
    875                         }
    876                     }
    877 
    878                     if ( !empty( $options ) ) {
    879                         for ( $i = 0, $count = count( $options ); $i < $count; ++$i ) {
    880                             $j = $i + 1;
    881 
    882                             if ( 'multiselectbox' == $type || 'checkbox' == $type )
    883                                 $default_name = '[' . $j . ']'; ?>
    884 
    885                             <p class="sortable">
    886                                 <span>&nbsp;&Xi;&nbsp;</span>
    887                                 <input type="text" name="<?php echo esc_attr( $type ); ?>_option[<?php echo esc_attr( $j ); ?>]" id="<?php echo esc_attr( $type ); ?>_option<?php echo esc_attr( $j ); ?>" value="<?php echo stripslashes( esc_attr( $options[$i]->name ) ); ?>" />
    888                                 <input type="<?php echo $default_input; ?>" name="isDefault_<?php echo esc_attr( $type ); ?>_option<?php echo esc_attr( $default_name ); ?>" <?php checked( (int) $options[$i]->is_default_option, true ); ?> value="<?php echo esc_attr( $j ); ?>" />
    889                                 <span><?php _e( 'Default Value', 'buddypress' ); ?></span>
    890                                 <a href="<?php echo esc_url( 'users.php?page=bp-profile-setup&amp;mode=delete_option&amp;option_id=' . $options[$i]->id ); ?>" class="ajax-option-delete" id="delete-<?php echo esc_attr( $options[$i]->id ); ?>">[x]</a>
    891                             </p>
    892 
    893                         <?php } /* end for */ ?>
    894 
    895                         <input type="hidden" name="<?php echo esc_attr( $type ); ?>_option_number" id="<?php echo esc_attr( $type ); ?>_option_number" value="<?php echo esc_attr( (int) $j + 1 ); ?>" />
    896 
    897                     <?php } else {
    898 
    899                         if ( 'multiselectbox' == $type || 'checkbox' == $type )
    900                             $default_name = '[1]'; ?>
    901 
    902                         <p class="sortable">
    903                             <span>&nbsp;&Xi;&nbsp;</span>
    904                             <input type="text" name="<?php echo esc_attr( $type ); ?>_option[1]" id="<?php echo esc_attr( $type ); ?>_option1" />
    905                             <input type="<?php echo esc_attr( $default_input ); ?>" name="isDefault_<?php echo esc_attr( $type ); ?>_option<?php echo esc_attr( $default_name ); ?>" id="isDefault_<?php echo esc_attr( $type ); ?>_option" value="1" />
    906                             <span><?php _e( 'Default Value', 'buddypress' ); ?></span>
    907                             <input type="hidden" name="<?php echo esc_attr( $type ); ?>_option_number" id="<?php echo esc_attr( $type ); ?>_option_number" value="2" />
    908                         </p>
    909 
    910                     <?php } /* end if */ ?>
    911 
    912                     <div id="<?php echo esc_attr( $type ); ?>_more"></div>
    913                     <p><a href="javascript:add_option('<?php echo esc_attr( $type ); ?>')"><?php _e( 'Add Another Option', 'buddypress' ); ?></a></p>
    914                 </div>
    915             </div>
    916 
    917         <?php }
     829        foreach ( array_keys( bp_xprofile_get_field_types() ) as $field_type ) {
     830            $type_obj = bp_xprofile_create_field_type( $field_type );
     831            $type_obj->admin_new_field_html( $this );
     832        }
    918833    }
    919834
     
    1042957                                    <div class="inside">
    1043958                                        <select name="fieldtype" id="fieldtype" onchange="show_options(this.value)" style="width: 30%">
    1044                                             <optgroup label="<?php esc_attr_e( 'Single Fields', 'buddypress' ); ?>">
    1045                                                 <option value="textbox"        <?php selected( $this->type, 'textbox'        ); ?>><?php _e( 'Text Box',             'buddypress' ); ?></option>
    1046                                                 <option value="textarea"       <?php selected( $this->type, 'textarea'       ); ?>><?php _e( 'Multi-line Text Area', 'buddypress' ); ?></option>
    1047                                                 <option value="datebox"        <?php selected( $this->type, 'datebox'        ); ?>><?php _e( 'Date Selector',        'buddypress' ); ?></option>
    1048                                             </optgroup>
    1049                                             <optgroup label="<?php esc_attr_e( 'Multi Fields', 'buddypress' ); ?>">
    1050                                                 <option value="radio"          <?php selected( $this->type, 'radio'          ); ?>><?php _e( 'Radio Buttons',        'buddypress' ); ?></option>
    1051                                                 <option value="selectbox"      <?php selected( $this->type, 'selectbox'      ); ?>><?php _e( 'Drop Down Select Box', 'buddypress' ); ?></option>
    1052                                                 <option value="multiselectbox" <?php selected( $this->type, 'multiselectbox' ); ?>><?php _e( 'Multi Select Box',     'buddypress' ); ?></option>
    1053                                                 <option value="checkbox"       <?php selected( $this->type, 'checkbox'       ); ?>><?php _e( 'Checkboxes',           'buddypress' ); ?></option>
    1054                                             </optgroup>
     959                                            <?php bp_xprofile_admin_form_field_types( $this->type ); ?>
    1055960                                        </select>
    1056961
    1057                                         <?php do_action_ref_array( 'xprofile_field_additional_options', array( $this ) ); ?>
    1058 
    1059                                         <?php $this->render_admin_form_children(); ?>
    1060 
     962                                        <?php
     963                                        // Deprecated filter, don't use. Go look at {@link BP_XProfile_Field_Type::admin_new_field_html()}.
     964                                        do_action( 'xprofile_field_additional_options', $this );
     965
     966                                        $this->render_admin_form_children();
     967                                        ?>
    1061968                                    </div>
    1062969                                </div>
     
    1087994            $message = __( 'Please make sure you fill out all required fields.', 'buddypress' );
    1088995            return false;
    1089         } else if ( empty( $_POST['field_file'] ) && $_POST['fieldtype'] == 'radio' && empty( $_POST['radio_option'][1] ) ) {
    1090             $message = __( 'Radio button field types require at least one option. Please add options below.', 'buddypress' );
    1091             return false;
    1092         } else if ( empty( $_POST['field_file'] ) && $_POST['fieldtype'] == 'selectbox' && empty( $_POST['selectbox_option'][1] ) ) {
    1093             $message = __( 'Select box field types require at least one option. Please add options below.', 'buddypress' );
    1094             return false;
    1095         } else if ( empty( $_POST['field_file'] ) && $_POST['fieldtype'] == 'multiselectbox' && empty( $_POST['multiselectbox_option'][1] ) ) {
    1096             $message = __( 'Select box field types require at least one option. Please add options below.', 'buddypress' );
    1097             return false;
    1098         } else if ( empty( $_POST['field_file'] ) && $_POST['fieldtype'] == 'checkbox' && empty( $_POST['checkbox_option'][1] ) ) {
    1099             $message = __( 'Checkbox field types require at least one option. Please add options below.', 'buddypress' );
    1100             return false;
    1101         } else {
    1102             return true;
    1103         }
     996
     997        } elseif ( empty( $_POST['field_file'] ) ) {
     998            $field_type  = bp_xprofile_create_field_type( $_POST['fieldtype'] );
     999            $option_name = "{$_POST['fieldtype']}_option";
     1000
     1001            if ( $field_type->supports_options && isset( $_POST[$option_name] ) && empty( $_POST[$option_name][1] ) ) {
     1002                $message = __( 'This field type require at least one option. Please add options below.', 'buddypress' );
     1003                return false;
     1004            }
     1005        }
     1006
     1007        return true;
    11041008    }
    11051009}
    1106 
    11071010
    11081011class BP_XProfile_ProfileData {
     
    11961099
    11971100        if ( $this->is_valid_field() ) {
    1198             if ( $this->exists() && !empty( $this->value ) && strlen( trim( $this->value ) ) ) {
     1101            if ( $this->exists() && strlen( trim( $this->value ) ) ) {
    11991102                $result   = $wpdb->query( $wpdb->prepare( "UPDATE {$bp->profile->table_name_data} SET value = %s, last_updated = %s WHERE user_id = %d AND field_id = %d", $this->value, $this->last_updated, $this->user_id, $this->field_id ) );
    12001103
     
    15451448    }
    15461449}
     1450
     1451/**
     1452 * Datebox xprofile field type.
     1453 *
     1454 * @since BuddyPress (2.0.0)
     1455 */
     1456class BP_XProfile_Field_Type_Datebox extends BP_XProfile_Field_Type {
     1457
     1458    /**
     1459     * Constructor for the datebox field type
     1460     *
     1461     * @since BuddyPress (2.0.0)
     1462     */
     1463    public function __construct() {
     1464        parent::__construct();
     1465
     1466        $this->category = _x( 'Single Fields', 'xprofile field type category', 'buddypress' );
     1467        $this->name     = _x( 'Date Selector', 'xprofile field type', 'buddypress' );
     1468
     1469        $this->set_format( '/^\d{4}-\d{1,2}-\d{1,2} 00:00:00$/', 'replace' );  // "Y-m-d 00:00:00"
     1470        do_action( 'bp_xprofile_field_type_datebox', $this );
     1471    }
     1472
     1473    /**
     1474     * Output the edit field HTML for this field type.
     1475     *
     1476     * Must be used inside the {@link bp_profile_fields()} template loop.
     1477     *
     1478     * @param array $raw_properties Optional key/value array of {@link http://dev.w3.org/html5/markup/input.html permitted attributes} that you want to add.
     1479     * @since BuddyPress (2.0.0)
     1480     */
     1481    public function edit_field_html( array $raw_properties = array() ) {
     1482        $user_id = bp_displayed_user_id();
     1483
     1484        // user_id is a special optional parameter that we pass to {@link bp_the_profile_field_options()}.
     1485        if ( isset( $raw_properties['user_id'] ) ) {
     1486            $user_id = (int) $raw_properties['user_id'];
     1487            unset( $raw_properties['user_id'] );
     1488        }
     1489
     1490        $day_html = $this->get_edit_field_html_elements( array_merge(
     1491            array(
     1492                'id'   => bp_get_the_profile_field_input_name() . '_day',
     1493                'name' => bp_get_the_profile_field_input_name() . '_day',
     1494            ),
     1495            $raw_properties
     1496        ) );
     1497
     1498        $month_html = $this->get_edit_field_html_elements( array_merge(
     1499            array(
     1500                'id'   => bp_get_the_profile_field_input_name() . '_month',
     1501                'name' => bp_get_the_profile_field_input_name() . '_month',
     1502            ),
     1503            $raw_properties
     1504        ) );
     1505
     1506        $year_html = $this->get_edit_field_html_elements( array_merge(
     1507            array(
     1508                'id'   => bp_get_the_profile_field_input_name() . '_year',
     1509                'name' => bp_get_the_profile_field_input_name() . '_year',
     1510            ),
     1511            $raw_properties
     1512        ) );
     1513    ?>
     1514        <div class="datebox">
     1515
     1516            <label for="<?php bp_the_profile_field_input_name(); ?>_day"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php esc_html_e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
     1517
     1518            <select <?php echo $day_html; ?>>
     1519                <?php bp_the_profile_field_options( array( 'type' => 'day', 'user_id' => $user_id ) ); ?>
     1520            </select>
     1521
     1522            <select <?php echo $month_html; ?>>
     1523                <?php bp_the_profile_field_options( array( 'type' => 'month', 'user_id' => $user_id ) ); ?>
     1524            </select>
     1525
     1526            <select <?php echo $year_html; ?>>
     1527                <?php bp_the_profile_field_options( array( 'type' => 'year', 'user_id' => $user_id ) ); ?>
     1528            </select>
     1529
     1530        </div>
     1531    <?php
     1532    }
     1533
     1534    /**
     1535     * Output the edit field options HTML for this field type.
     1536     *
     1537     * BuddyPress considers a field's "options" to be, for example, the items in a selectbox.
     1538     * These are stored separately in the database, and their templating is handled seperately.
     1539     *
     1540     * This templating is separate from {@link BP_XProfile_Field_Type::edit_field_html()} because
     1541     * it's also used in the wp-admin screens when creating new fields, and for backwards compatibility.
     1542     *
     1543     * Must be used inside the {@link bp_profile_fields()} template loop.
     1544     *
     1545     * @param array $args Optional. The arguments passed to {@link bp_the_profile_field_options()}.
     1546     * @since BuddyPress (2.0.0)
     1547     */
     1548    public function edit_field_options_html( array $args = array() ) {
     1549        $options = $this->field_obj->get_children();
     1550        $date    = BP_XProfile_ProfileData::get_value_byid( $this->field_obj->id, $args['user_id'] );
     1551
     1552        $day   = 0;
     1553        $month = 0;
     1554        $year  = 0;
     1555        $html  = '';
     1556
     1557        // Set day, month, year defaults
     1558        if ( ! empty( $date ) ) {
     1559
     1560            // If Unix timestamp
     1561            if ( is_numeric( $date ) ) {
     1562                $day   = date( 'j', $date );
     1563                $month = date( 'F', $date );
     1564                $year  = date( 'Y', $date );
     1565
     1566            // If MySQL timestamp
     1567            } else {
     1568                $day   = mysql2date( 'j', $date );
     1569                $month = mysql2date( 'F', $date, false ); // Not localized, so that selected() works below
     1570                $year  = mysql2date( 'Y', $date );
     1571            }
     1572        }
     1573
     1574        // Check for updated posted values, and errors preventing them from being saved first time.
     1575        if ( ! empty( $_POST['field_' . $this->field_obj->id . '_day'] ) ) {
     1576            $new_day = (int) $_POST['field_' . $this->field_obj->id . '_day'];
     1577            $day     = ( $day != $new_day ) ? $new_day : $day;
     1578        }
     1579
     1580        if ( ! empty( $_POST['field_' . $this->field_obj->id . '_month'] ) ) {
     1581            $new_month = (int) $_POST['field_' . $this->field_obj->id . '_month'];
     1582            $month     = ( $month != $new_month ) ? $new_month : $month;
     1583        }
     1584
     1585        if ( ! empty( $_POST['field_' . $this->field_obj->id . '_year'] ) ) {
     1586            $new_year = date( 'j', (int) $_POST['field_' . $this->field_obj->id . '_year'] );
     1587            $year     = ( $year != $new_year ) ? $new_year : $year;
     1588        }
     1589
     1590        // $type will be passed by calling function when needed
     1591        switch ( $args['type'] ) {
     1592            case 'day':
     1593                $html = sprintf( '<option value="" %1$s>%2$s</option>', selected( $day, 0, false ), /* translators: no option picked in select box */ __( '----', 'buddypress' ) );
     1594
     1595                for ( $i = 1; $i < 32; ++$i ) {
     1596                    $html .= sprintf( '<option value="%1$s" %2$s>%3$s</option>', (int) $i, selected( $day, $i, false ), (int) $i );
     1597                }
     1598            break;
     1599
     1600            case 'month':
     1601                $eng_months = array( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' );
     1602
     1603                $months = array(
     1604                    __( 'January', 'buddypress' ),
     1605                    __( 'February', 'buddypress' ),
     1606                    __( 'March', 'buddypress' ),
     1607                    __( 'April', 'buddypress' ),
     1608                    __( 'May', 'buddypress' ),
     1609                    __( 'June', 'buddypress' ),
     1610                    __( 'July', 'buddypress' ),
     1611                    __( 'August', 'buddypress' ),
     1612                    __( 'September', 'buddypress' ),
     1613                    __( 'October', 'buddypress' ),
     1614                    __( 'November', 'buddypress' ),
     1615                    __( 'December', 'buddypress' )
     1616                );
     1617
     1618                $html = sprintf( '<option value="" %1$s>%2$s</option>', selected( $month, 0, false ), /* translators: no option picked in select box */ __( '----', 'buddypress' ) );
     1619
     1620                for ( $i = 0; $i < 12; ++$i ) {
     1621                    $html .= sprintf( '<option value="%1$s" %2$s>%3$s</option>', esc_attr( $eng_months[$i] ), selected( $month, $eng_months[$i], false ), $months[$i] );
     1622                }
     1623            break;
     1624
     1625            case 'year':
     1626                $html = sprintf( '<option value="" %1$s>%2$s</option>', selected( $year, 0, false ), /* translators: no option picked in select box */ __( '----', 'buddypress' ) );
     1627
     1628                for ( $i = 2037; $i > 1901; $i-- ) {
     1629                    $html .= sprintf( '<option value="%1$s" %2$s>%3$s</option>', (int) $i, selected( $year, $i, false ), (int) $i );
     1630                }
     1631            break;
     1632        }
     1633
     1634        echo apply_filters( 'bp_get_the_profile_field_datebox', $html, $args['type'], $day, $month, $year, $this->field_obj->id, $date );
     1635    }
     1636
     1637    /**
     1638     * Output HTML for this field type on the wp-admin Profile Fields screen.
     1639     *
     1640     * Must be used inside the {@link bp_profile_fields()} template loop.
     1641     *
     1642     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     1643     * @since BuddyPress (2.0.0)
     1644     */
     1645    public function admin_field_html( array $raw_properties = array() ) {
     1646        $day_html = $this->get_edit_field_html_elements( array_merge(
     1647            array(
     1648                'id'   => bp_get_the_profile_field_input_name() . '_day',
     1649                'name' => bp_get_the_profile_field_input_name() . '_day',
     1650            ),
     1651            $raw_properties
     1652        ) );
     1653
     1654        $month_html = $this->get_edit_field_html_elements( array_merge(
     1655            array(
     1656                'id'   => bp_get_the_profile_field_input_name() . '_month',
     1657                'name' => bp_get_the_profile_field_input_name() . '_month',
     1658            ),
     1659            $raw_properties
     1660        ) );
     1661
     1662        $year_html = $this->get_edit_field_html_elements( array_merge(
     1663            array(
     1664                'id'   => bp_get_the_profile_field_input_name() . '_year',
     1665                'name' => bp_get_the_profile_field_input_name() . '_year',
     1666            ),
     1667            $raw_properties
     1668        ) );
     1669    ?>
     1670        <select <?php echo $day_html; ?>>
     1671            <?php bp_the_profile_field_options( 'type=day' ); ?>
     1672        </select>
     1673
     1674        <select <?php echo $month_html; ?>>
     1675            <?php bp_the_profile_field_options( 'type=month' ); ?>
     1676        </select>
     1677
     1678        <select <?php echo $year_html; ?>>
     1679            <?php bp_the_profile_field_options( 'type=year' ); ?>
     1680        </select>
     1681    <?php
     1682    }
     1683
     1684    /**
     1685     * This method usually outputs HTML for this field type's children options on the wp-admin Profile Fields
     1686     * "Add Field" and "Edit Field" screens, but for this field type, we don't want it, so it's stubbed out.
     1687     *
     1688     * @param BP_XProfile_Field $current_field The current profile field on the add/edit screen.
     1689     * @param string $control_type Optional. HTML input type used to render the current field's child options.
     1690     * @since BuddyPress (2.0.0)
     1691     */
     1692    public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {}
     1693}
     1694
     1695/**
     1696 * Checkbox xprofile field type.
     1697 *
     1698 * @since BuddyPress (2.0.0)
     1699 */
     1700class BP_XProfile_Field_Type_Checkbox extends BP_XProfile_Field_Type {
     1701
     1702    /**
     1703     * Constructor for the checkbox field type
     1704     *
     1705     * @since BuddyPress (2.0.0)
     1706     */
     1707    public function __construct() {
     1708        parent::__construct();
     1709
     1710        $this->category = _x( 'Multi Fields', 'xprofile field type category', 'buddypress' );
     1711        $this->name     = _x( 'Checkboxes', 'xprofile field type', 'buddypress' );
     1712
     1713        $this->supports_multiple_defaults = true;
     1714        $this->accepts_null_value         = true;
     1715        $this->supports_options           = true;
     1716
     1717        $this->set_format( '/^.+$/', 'replace' );
     1718        do_action( 'bp_xprofile_field_type_checkbox', $this );
     1719    }
     1720
     1721    /**
     1722     * Output the edit field HTML for this field type.
     1723     *
     1724     * Must be used inside the {@link bp_profile_fields()} template loop.
     1725     *
     1726     * @param array $raw_properties Optional key/value array of {@link http://dev.w3.org/html5/markup/input.checkbox.html permitted attributes} that you want to add.
     1727     * @since BuddyPress (2.0.0)
     1728     */
     1729    public function edit_field_html( array $raw_properties = array() ) {
     1730        $user_id = bp_displayed_user_id();
     1731
     1732        // user_id is a special optional parameter that we pass to {@link bp_the_profile_field_options()}.
     1733        if ( isset( $raw_properties['user_id'] ) ) {
     1734            $user_id = (int) $raw_properties['user_id'];
     1735            unset( $raw_properties['user_id'] );
     1736        }
     1737    ?>
     1738        <div class="checkbox">
     1739
     1740            <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php esc_html_e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
     1741            <?php bp_the_profile_field_options( "user_id={$user_id}" ); ?>
     1742
     1743        </div>
     1744        <?php
     1745    }
     1746
     1747    /**
     1748     * Output the edit field options HTML for this field type.
     1749     *
     1750     * BuddyPress considers a field's "options" to be, for example, the items in a selectbox.
     1751     * These are stored separately in the database, and their templating is handled seperately.
     1752     *
     1753     * This templating is separate from {@link BP_XProfile_Field_Type::edit_field_html()} because
     1754     * it's also used in the wp-admin screens when creating new fields, and for backwards compatibility.
     1755     *
     1756     * Must be used inside the {@link bp_profile_fields()} template loop.
     1757     *
     1758     * @param array $args Optional. The arguments passed to {@link bp_the_profile_field_options()}.
     1759     * @since BuddyPress (2.0.0)
     1760     */
     1761    public function edit_field_options_html( array $args = array() ) {
     1762        $options       = $this->field_obj->get_children();
     1763        $option_values = BP_XProfile_ProfileData::get_value_byid( $this->field_obj->id, $args['user_id'] );
     1764        $option_values = (array) maybe_unserialize( $option_values );
     1765
     1766        $html = '';
     1767
     1768        // Check for updated posted values, but errors preventing them from being saved first time
     1769        if ( isset( $_POST['field_' . $this->field_obj->id] ) && $option_values != maybe_serialize( $_POST['field_' . $this->field_obj->id] ) ) {
     1770            if ( ! empty( $_POST['field_' . $this->field_obj->id] ) ) {
     1771                $option_values = array_map( 'sanitize_text_field', $_POST['field_' . $this->field_obj->id] );
     1772            }
     1773        }
     1774
     1775        for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
     1776            $selected = '';
     1777
     1778            // First, check to see whether the user's saved values match the option
     1779            for ( $j = 0, $count_values = count( $option_values ); $j < $count_values; ++$j ) {
     1780
     1781                // Run the allowed option name through the before_save filter, so we'll be sure to get a match
     1782                $allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
     1783
     1784                if ( $option_values[$j] === $allowed_options || in_array( $allowed_options, $option_values ) ) {
     1785                    $selected = ' checked="checked"';
     1786                    break;
     1787                }
     1788            }
     1789
     1790            // If the user has not yet supplied a value for this field, check to see whether there is a default value available
     1791            if ( ! is_array( $option_values ) && empty( $option_values ) && empty( $selected ) && ! empty( $options[$k]->is_default_option ) ) {
     1792                $selected = ' checked="checked"';
     1793            }
     1794
     1795            $new_html = sprintf( '<label><input %1$s type="checkbox" name="%2$s" id="%3$s" value="%4$s">%5$s</label>',
     1796                $selected,
     1797                esc_attr( "field_{$this->field_obj->id}[]" ),
     1798                esc_attr( "field_{$options[$k]->id}_{$k}" ),
     1799                esc_attr( stripslashes( $options[$k]->name ) ),
     1800                esc_html( stripslashes( $options[$k]->name ) )
     1801            );
     1802            $html .= apply_filters( 'bp_get_the_profile_field_options_checkbox', $new_html, $options[$k], $this->field_obj->id, $selected, $k );
     1803        }
     1804
     1805        echo $html;
     1806    }
     1807
     1808    /**
     1809     * Output HTML for this field type on the wp-admin Profile Fields screen.
     1810     *
     1811     * Must be used inside the {@link bp_profile_fields()} template loop.
     1812     *
     1813     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     1814     * @since BuddyPress (2.0.0)
     1815     */
     1816    public function admin_field_html( array $raw_properties = array() ) {
     1817        bp_the_profile_field_options();
     1818    }
     1819
     1820    /**
     1821     * Output HTML for this field type's children options on the wp-admin Profile Fields "Add Field" and "Edit Field" screens.
     1822     *
     1823     * Must be used inside the {@link bp_profile_fields()} template loop.
     1824     *
     1825     * @param BP_XProfile_Field $current_field The current profile field on the add/edit screen.
     1826     * @param string $control_type Optional. HTML input type used to render the current field's child options.
     1827     * @since BuddyPress (2.0.0)
     1828     */
     1829    public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {
     1830        parent::admin_new_field_html( $current_field, 'checkbox' );
     1831    }
     1832}
     1833
     1834/**
     1835 * Radio button xprofile field type.
     1836 *
     1837 * @since BuddyPress (2.0.0)
     1838 */
     1839class BP_XProfile_Field_Type_Radiobutton extends BP_XProfile_Field_Type {
     1840
     1841    /**
     1842     * Constructor for the radio button field type
     1843     *
     1844     * @since BuddyPress (2.0.0)
     1845     */
     1846    public function __construct() {
     1847        parent::__construct();
     1848
     1849        $this->category = _x( 'Multi Fields', 'xprofile field type category', 'buddypress' );
     1850        $this->name     = _x( 'Radio Buttons', 'xprofile field type', 'buddypress' );
     1851
     1852        $this->supports_options = true;
     1853
     1854        $this->set_format( '/^.+$/', 'replace' );
     1855        do_action( 'bp_xprofile_field_type_radiobutton', $this );
     1856    }
     1857
     1858    /**
     1859     * Output the edit field HTML for this field type.
     1860     *
     1861     * Must be used inside the {@link bp_profile_fields()} template loop.
     1862     *
     1863     * @param array $raw_properties Optional key/value array of {@link http://dev.w3.org/html5/markup/input.radio.html permitted attributes} that you want to add.
     1864     * @since BuddyPress (2.0.0)
     1865     */
     1866    public function edit_field_html( array $raw_properties = array() ) {
     1867        $user_id = bp_displayed_user_id();
     1868
     1869        // user_id is a special optional parameter that we pass to {@link bp_the_profile_field_options()}.
     1870        if ( isset( $raw_properties['user_id'] ) ) {
     1871            $user_id = (int) $raw_properties['user_id'];
     1872            unset( $raw_properties['user_id'] );
     1873        }
     1874    ?>
     1875        <div class="radio">
     1876
     1877            <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php esc_html_e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
     1878            <?php bp_the_profile_field_options( "user_id={$user_id}" );
     1879
     1880            if ( ! bp_get_the_profile_field_is_required() ) : ?>
     1881                <a class="clear-value" href="javascript:clear( '<?php echo esc_js( bp_get_the_profile_field_input_name() ); ?>' );"><?php esc_html_e( 'Clear', 'buddypress' ); ?></a>
     1882            <?php endif; ?>
     1883
     1884        </div>
     1885        <?php
     1886    }
     1887
     1888    /**
     1889     * Output the edit field options HTML for this field type.
     1890     *
     1891     * BuddyPress considers a field's "options" to be, for example, the items in a selectbox.
     1892     * These are stored separately in the database, and their templating is handled seperately.
     1893     *
     1894     * This templating is separate from {@link BP_XProfile_Field_Type::edit_field_html()} because
     1895     * it's also used in the wp-admin screens when creating new fields, and for backwards compatibility.
     1896     *
     1897     * Must be used inside the {@link bp_profile_fields()} template loop.
     1898     *
     1899     * @param array $args Optional. The arguments passed to {@link bp_the_profile_field_options()}.
     1900     * @since BuddyPress (2.0.0)
     1901     */
     1902    public function edit_field_options_html( array $args = array() ) {
     1903        $option_value = BP_XProfile_ProfileData::get_value_byid( $this->field_obj->id, $args['user_id'] );
     1904        $options      = $this->field_obj->get_children();
     1905
     1906        $html = sprintf( '<div id="%s">', esc_attr( 'field_' . $this->field_obj->id ) );
     1907
     1908        for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
     1909
     1910            // Check for updated posted values, but errors preventing them from being saved first time
     1911            if ( isset( $_POST['field_' . $this->field_obj->id] ) && $option_value != $_POST['field_' . $this->field_obj->id] ) {
     1912                if ( ! empty( $_POST['field_' . $this->field_obj->id] ) ) {
     1913                    $option_value = sanitize_text_field( $_POST['field_' . $this->field_obj->id] );
     1914                }
     1915            }
     1916
     1917            // Run the allowed option name through the before_save filter, so we'll be sure to get a match
     1918            $allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
     1919            $selected        = '';
     1920
     1921            if ( $option_value === $allowed_options || ( empty( $option_value ) && ! empty( $options[$k]->is_default_option ) ) ) {
     1922                $selected = ' checked="checked"';
     1923            }
     1924
     1925            $new_html = sprintf( '<label><input %1$s type="radio" name="%2$s" id="%3$s" value="%4$s">%5$s</label>',
     1926                $selected,
     1927                esc_attr( "field_{$this->field_obj->id}" ),
     1928                esc_attr( "option_{$options[$k]->id}" ),
     1929                esc_attr( stripslashes( $options[$k]->name ) ),
     1930                esc_html( stripslashes( $options[$k]->name ) )
     1931            );
     1932            $html .= apply_filters( 'bp_get_the_profile_field_options_radio', $new_html, $options[$k], $this->field_obj->id, $selected, $k );
     1933        }
     1934
     1935        echo $html . '</div>';
     1936    }
     1937
     1938    /**
     1939     * Output HTML for this field type on the wp-admin Profile Fields screen.
     1940     *
     1941     * Must be used inside the {@link bp_profile_fields()} template loop.
     1942     *
     1943     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     1944     * @since BuddyPress (2.0.0)
     1945     */
     1946    public function admin_field_html( array $raw_properties = array() ) {
     1947        bp_the_profile_field_options();
     1948
     1949        if ( ! bp_get_the_profile_field_is_required() ) : ?>
     1950            <a class="clear-value" href="javascript:clear( '<?php echo esc_js( bp_get_the_profile_field_input_name() ); ?>' );"><?php esc_html_e( 'Clear', 'buddypress' ); ?></a>
     1951        <?php endif; ?>
     1952    <?php
     1953    }
     1954
     1955    /**
     1956     * Output HTML for this field type's children options on the wp-admin Profile Fields "Add Field" and "Edit Field" screens.
     1957     *
     1958     * Must be used inside the {@link bp_profile_fields()} template loop.
     1959     *
     1960     * @param BP_XProfile_Field $current_field The current profile field on the add/edit screen.
     1961     * @param string $control_type Optional. HTML input type used to render the current field's child options.
     1962     * @since BuddyPress (2.0.0)
     1963     */
     1964    public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {
     1965        parent::admin_new_field_html( $current_field, 'radio' );
     1966    }
     1967}
     1968
     1969/**
     1970 * Multi-selectbox xprofile field type.
     1971 *
     1972 * @since BuddyPress (2.0.0)
     1973 */
     1974class BP_XProfile_Field_Type_Multiselectbox extends BP_XProfile_Field_Type {
     1975
     1976    /**
     1977     * Constructor for the multi-selectbox field type
     1978     *
     1979     * @since BuddyPress (2.0.0)
     1980     */
     1981    public function __construct() {
     1982        parent::__construct();
     1983
     1984        $this->category = _x( 'Multi Fields', 'xprofile field type category', 'buddypress' );
     1985        $this->name     = _x( 'Multi Select Box', 'xprofile field type', 'buddypress' );
     1986
     1987        $this->supports_multiple_defaults = true;
     1988        $this->accepts_null_value         = true;
     1989        $this->supports_options           = true;
     1990
     1991        $this->set_format( '/^.+$/', 'replace' );
     1992        do_action( 'bp_xprofile_field_type_multiselectbox', $this );
     1993    }
     1994
     1995    /**
     1996     * Output the edit field HTML for this field type.
     1997     *
     1998     * Must be used inside the {@link bp_profile_fields()} template loop.
     1999     *
     2000     * @param array $raw_properties Optional key/value array of {@link http://dev.w3.org/html5/markup/select.html permitted attributes} that you want to add.
     2001     * @since BuddyPress (2.0.0)
     2002     */
     2003    public function edit_field_html( array $raw_properties = array() ) {
     2004        $user_id = bp_displayed_user_id();
     2005
     2006        // user_id is a special optional parameter that we pass to {@link bp_the_profile_field_options()}.
     2007        if ( isset( $raw_properties['user_id'] ) ) {
     2008            $user_id = (int) $raw_properties['user_id'];
     2009            unset( $raw_properties['user_id'] );
     2010        }
     2011
     2012        $html = $this->get_edit_field_html_elements( array_merge(
     2013            array(
     2014                'multiple' => 'multiple',
     2015                'id'       => bp_get_the_profile_field_input_name() . '[]',
     2016                'name'     => bp_get_the_profile_field_input_name() . '[]',
     2017            ),
     2018            $raw_properties
     2019        ) );
     2020    ?>
     2021        <label for="<?php bp_the_profile_field_input_name(); ?>[]"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php _e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
     2022        <select <?php echo $html; ?>>
     2023            <?php bp_the_profile_field_options( "user_id={$user_id}" ); ?>
     2024        </select>
     2025
     2026        <?php if ( ! bp_get_the_profile_field_is_required() ) : ?>
     2027            <a class="clear-value" href="javascript:clear( '<?php echo esc_js( bp_get_the_profile_field_input_name() ); ?>[]' );"><?php esc_html_e( 'Clear', 'buddypress' ); ?></a>
     2028        <?php endif; ?>
     2029    <?php
     2030    }
     2031
     2032    /**
     2033     * Output the edit field options HTML for this field type.
     2034     *
     2035     * BuddyPress considers a field's "options" to be, for example, the items in a selectbox.
     2036     * These are stored separately in the database, and their templating is handled seperately.
     2037     *
     2038     * This templating is separate from {@link BP_XProfile_Field_Type::edit_field_html()} because
     2039     * it's also used in the wp-admin screens when creating new fields, and for backwards compatibility.
     2040     *
     2041     * Must be used inside the {@link bp_profile_fields()} template loop.
     2042     *
     2043     * @param array $args Optional. The arguments passed to {@link bp_the_profile_field_options()}.
     2044     * @since BuddyPress (2.0.0)
     2045     */
     2046    public function edit_field_options_html( array $args = array() ) {
     2047        $original_option_values = maybe_unserialize( BP_XProfile_ProfileData::get_value_byid( $this->field_obj->id, $args['user_id'] ) );
     2048
     2049        $options = $this->field_obj->get_children();
     2050        $html    = '';
     2051
     2052        if ( empty( $original_option_values ) && ! empty( $_POST['field_' . $this->field_obj->id] ) ) {
     2053            $original_option_values = sanitize_text_field( $_POST['field_' . $this->field_obj->id] );
     2054        }
     2055
     2056        $option_values = (array) $original_option_values;
     2057        for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
     2058            $selected = '';
     2059
     2060            // Check for updated posted values, but errors preventing them from being saved first time
     2061            foreach( $option_values as $i => $option_value ) {
     2062                if ( isset( $_POST['field_' . $this->field_obj->id] ) && $_POST['field_' . $this->field_obj->id][$i] != $option_value ) {
     2063                    if ( ! empty( $_POST['field_' . $this->field_obj->id][$i] ) ) {
     2064                        $option_values[] = sanitize_text_field( $_POST['field_' . $this->field_obj->id][$i] );
     2065                    }
     2066                }
     2067            }
     2068
     2069            // Run the allowed option name through the before_save filter, so we'll be sure to get a match
     2070            $allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
     2071
     2072            // First, check to see whether the user-entered value matches
     2073            if ( in_array( $allowed_options, $option_values ) ) {
     2074                $selected = ' selected="selected"';
     2075            }
     2076
     2077            // Then, if the user has not provided a value, check for defaults
     2078            if ( ! is_array( $original_option_values ) && empty( $option_values ) && ! empty( $options[$k]->is_default_option ) ) {
     2079                $selected = ' selected="selected"';
     2080            }
     2081
     2082            $html .= apply_filters( 'bp_get_the_profile_field_options_multiselect', '<option' . $selected . ' value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '">' . esc_html( stripslashes( $options[$k]->name ) ) . '</option>', $options[$k], $this->field_obj->id, $selected, $k );
     2083        }
     2084
     2085        echo $html;
     2086    }
     2087
     2088    /**
     2089     * Output HTML for this field type on the wp-admin Profile Fields screen.
     2090     *
     2091     * Must be used inside the {@link bp_profile_fields()} template loop.
     2092     *
     2093     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     2094     * @since BuddyPress (2.0.0)
     2095     */
     2096    public function admin_field_html( array $raw_properties = array() ) {
     2097        $html = $this->get_edit_field_html_elements( array_merge(
     2098            array( 'multiple' => 'multiple' ),
     2099            $raw_properties
     2100        ) );
     2101    ?>
     2102        <select <?php echo $html; ?>>
     2103            <?php bp_the_profile_field_options(); ?>
     2104        </select>
     2105    <?php
     2106    }
     2107
     2108    /**
     2109     * Output HTML for this field type's children options on the wp-admin Profile Fields "Add Field" and "Edit Field" screens.
     2110     *
     2111     * Must be used inside the {@link bp_profile_fields()} template loop.
     2112     *
     2113     * @param BP_XProfile_Field $current_field The current profile field on the add/edit screen.
     2114     * @param string $control_type Optional. HTML input type used to render the current field's child options.
     2115     * @since BuddyPress (2.0.0)
     2116     */
     2117    public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {
     2118        parent::admin_new_field_html( $current_field, 'checkbox' );
     2119    }
     2120}
     2121
     2122/**
     2123 * Selectbox xprofile field type.
     2124 *
     2125 * @since BuddyPress (2.0.0)
     2126 */
     2127class BP_XProfile_Field_Type_Selectbox extends BP_XProfile_Field_Type {
     2128
     2129    /**
     2130     * Constructor for the selectbox field type
     2131     *
     2132     * @since BuddyPress (2.0.0)
     2133     */
     2134    public function __construct() {
     2135        parent::__construct();
     2136
     2137        $this->category = _x( 'Multi Fields', 'xprofile field type category', 'buddypress' );
     2138        $this->name     = _x( 'Drop Down Select Box', 'xprofile field type', 'buddypress' );
     2139
     2140        $this->supports_options = true;
     2141
     2142        $this->set_format( '/^.+$/', 'replace' );
     2143        do_action( 'bp_xprofile_field_type_selectbox', $this );
     2144    }
     2145
     2146    /**
     2147     * Output the edit field HTML for this field type.
     2148     *
     2149     * Must be used inside the {@link bp_profile_fields()} template loop.
     2150     *
     2151     * @param array $raw_properties Optional key/value array of {@link http://dev.w3.org/html5/markup/select.html permitted attributes} that you want to add.
     2152     * @since BuddyPress (2.0.0)
     2153     */
     2154    public function edit_field_html( array $raw_properties = array() ) {
     2155        $user_id = bp_displayed_user_id();
     2156
     2157        // user_id is a special optional parameter that we pass to {@link bp_the_profile_field_options()}.
     2158        if ( isset( $raw_properties['user_id'] ) ) {
     2159            $user_id = (int) $raw_properties['user_id'];
     2160            unset( $raw_properties['user_id'] );
     2161        }
     2162
     2163        $html = $this->get_edit_field_html_elements( $raw_properties );
     2164    ?>
     2165        <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php esc_html_e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
     2166        <select <?php echo $html; ?>>
     2167            <?php bp_the_profile_field_options( "user_id={$user_id}" ); ?>
     2168        </select>
     2169    <?php
     2170    }
     2171
     2172    /**
     2173     * Output the edit field options HTML for this field type.
     2174     *
     2175     * BuddyPress considers a field's "options" to be, for example, the items in a selectbox.
     2176     * These are stored separately in the database, and their templating is handled seperately.
     2177     *
     2178     * This templating is separate from {@link BP_XProfile_Field_Type::edit_field_html()} because
     2179     * it's also used in the wp-admin screens when creating new fields, and for backwards compatibility.
     2180     *
     2181     * Must be used inside the {@link bp_profile_fields()} template loop.
     2182     *
     2183     * @param array $args Optional. The arguments passed to {@link bp_the_profile_field_options()}.
     2184     * @since BuddyPress (2.0.0)
     2185     */
     2186    public function edit_field_options_html( array $args = array() ) {
     2187        $original_option_values = maybe_unserialize( BP_XProfile_ProfileData::get_value_byid( $this->field_obj->id, $args['user_id'] ) );
     2188
     2189        $options = $this->field_obj->get_children();
     2190        $html     = '<option value="">' . /* translators: no option picked in select box */ esc_html__( '----', 'buddypress' ) . '</option>';
     2191
     2192        if ( empty( $original_option_values ) && !empty( $_POST['field_' . $this->field_obj->id] ) ) {
     2193            $original_option_values = sanitize_text_field(  $_POST['field_' . $this->field_obj->id] );
     2194        }
     2195
     2196        $option_values = (array) $original_option_values;
     2197        for ( $k = 0, $count = count( $options ); $k < $count; ++$k ) {
     2198            $selected = '';
     2199
     2200            // Check for updated posted values, but errors preventing them from being saved first time
     2201            foreach( $option_values as $i => $option_value ) {
     2202                if ( isset( $_POST['field_' . $this->field_obj->id] ) && $_POST['field_' . $this->field_obj->id] != $option_value ) {
     2203                    if ( ! empty( $_POST['field_' . $this->field_obj->id] ) ) {
     2204                        $option_values[$i] = sanitize_text_field( $_POST['field_' . $this->field_obj->id] );
     2205                    }
     2206                }
     2207            }
     2208
     2209            // Run the allowed option name through the before_save filter, so we'll be sure to get a match
     2210            $allowed_options = xprofile_sanitize_data_value_before_save( $options[$k]->name, false, false );
     2211
     2212            // First, check to see whether the user-entered value matches
     2213            if ( in_array( $allowed_options, $option_values ) ) {
     2214                $selected = ' selected="selected"';
     2215            }
     2216
     2217            // Then, if the user has not provided a value, check for defaults
     2218            if ( ! is_array( $original_option_values ) && empty( $option_values ) && $options[$k]->is_default_option ) {
     2219                $selected = ' selected="selected"';
     2220            }
     2221
     2222            $html .= apply_filters( 'bp_get_the_profile_field_options_select', '<option' . $selected . ' value="' . esc_attr( stripslashes( $options[$k]->name ) ) . '">' . esc_html( stripslashes( $options[$k]->name ) ) . '</option>', $options[$k], $this->field_obj->id, $selected, $k );
     2223        }
     2224
     2225        echo $html;
     2226    }
     2227
     2228    /**
     2229     * Output HTML for this field type on the wp-admin Profile Fields screen.
     2230     *
     2231     * Must be used inside the {@link bp_profile_fields()} template loop.
     2232     *
     2233     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     2234     * @since BuddyPress (2.0.0)
     2235     */
     2236    public function admin_field_html( array $raw_properties = array() ) {
     2237        $html = $this->get_edit_field_html_elements( $raw_properties );
     2238    ?>
     2239        <select <?php echo $html; ?>>
     2240            <?php bp_the_profile_field_options(); ?>
     2241        </select>
     2242    <?php
     2243    }
     2244
     2245    /**
     2246     * Output HTML for this field type's children options on the wp-admin Profile Fields "Add Field" and "Edit Field" screens.
     2247     *
     2248     * Must be used inside the {@link bp_profile_fields()} template loop.
     2249     *
     2250     * @param BP_XProfile_Field $current_field The current profile field on the add/edit screen.
     2251     * @param string $control_type Optional. HTML input type used to render the current field's child options.
     2252     * @since BuddyPress (2.0.0)
     2253     */
     2254    public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {
     2255        parent::admin_new_field_html( $current_field, 'radio' );
     2256    }
     2257}
     2258
     2259/**
     2260 * Textarea xprofile field type.
     2261 *
     2262 * @since BuddyPress (2.0.0)
     2263 */
     2264class BP_XProfile_Field_Type_Textarea extends BP_XProfile_Field_Type {
     2265
     2266    /**
     2267     * Constructor for the textarea field type
     2268     *
     2269     * @since BuddyPress (2.0.0)
     2270     */
     2271    public function __construct() {
     2272        parent::__construct();
     2273
     2274        $this->category = _x( 'Single Fields', 'xprofile field type category', 'buddypress' );
     2275        $this->name     = _x( 'Multi-line Text Area', 'xprofile field type', 'buddypress' );
     2276
     2277        $this->set_format( '/^.*$/m', 'replace' );
     2278        do_action( 'bp_xprofile_field_type_textarea', $this );
     2279    }
     2280
     2281    /**
     2282     * Output the edit field HTML for this field type.
     2283     *
     2284     * Must be used inside the {@link bp_profile_fields()} template loop.
     2285     *
     2286     * @param array $raw_properties Optional key/value array of {@link http://dev.w3.org/html5/markup/textarea.html permitted attributes} that you want to add.
     2287     * @since BuddyPress (2.0.0)
     2288     */
     2289    public function edit_field_html( array $raw_properties = array() ) {
     2290
     2291        // user_id is a special optional parameter that certain other fields types pass to {@link bp_the_profile_field_options()}.
     2292        if ( isset( $raw_properties['user_id'] ) ) {
     2293            unset( $raw_properties['user_id'] );
     2294        }
     2295
     2296        $html = $this->get_edit_field_html_elements( array_merge(
     2297            array(
     2298                'cols' => 40,
     2299                'rows' => 5,
     2300            ),
     2301            $raw_properties
     2302        ) );
     2303    ?>
     2304        <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php esc_html_e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
     2305        <textarea <?php echo $html; ?>><?php bp_the_profile_field_edit_value(); ?></textarea>
     2306    <?php
     2307    }
     2308
     2309    /**
     2310     * Output HTML for this field type on the wp-admin Profile Fields screen.
     2311     *
     2312     * Must be used inside the {@link bp_profile_fields()} template loop.
     2313     *
     2314     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     2315     * @since BuddyPress (2.0.0)
     2316     */
     2317    public function admin_field_html( array $raw_properties = array() ) {
     2318        $html = $this->get_edit_field_html_elements( array_merge(
     2319            array(
     2320                'cols' => 40,
     2321                'rows' => 5,
     2322            ),
     2323            $raw_properties
     2324        ) );
     2325    ?>
     2326        <textarea <?php echo $html; ?>></textarea>
     2327    <?php
     2328    }
     2329
     2330    /**
     2331     * This method usually outputs HTML for this field type's children options on the wp-admin Profile Fields
     2332     * "Add Field" and "Edit Field" screens, but for this field type, we don't want it, so it's stubbed out.
     2333     *
     2334     * @param BP_XProfile_Field $current_field The current profile field on the add/edit screen.
     2335     * @param string $control_type Optional. HTML input type used to render the current field's child options.
     2336     * @since BuddyPress (2.0.0)
     2337     */
     2338    public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {}
     2339}
     2340
     2341/**
     2342 * Textbox xprofile field type.
     2343 *
     2344 * @since BuddyPress (2.0.0)
     2345 */
     2346class BP_XProfile_Field_Type_Textbox extends BP_XProfile_Field_Type {
     2347
     2348    /**
     2349     * Constructor for the textbox field type
     2350     *
     2351     * @since BuddyPress (2.0.0)
     2352     */
     2353    public function __construct() {
     2354        parent::__construct();
     2355
     2356        $this->category = _x( 'Single Fields', 'xprofile field type category', 'buddypress' );
     2357        $this->name     = _x( 'Text Box', 'xprofile field type', 'buddypress' );
     2358
     2359        $this->set_format( '/^.*$/', 'replace' );
     2360        do_action( 'bp_xprofile_field_type_textbox', $this );
     2361    }
     2362
     2363    /**
     2364     * Output the edit field HTML for this field type.
     2365     *
     2366     * Must be used inside the {@link bp_profile_fields()} template loop.
     2367     *
     2368     * @param array $raw_properties Optional key/value array of {@link http://dev.w3.org/html5/markup/input.text.html permitted attributes} that you want to add.
     2369     * @since BuddyPress (2.0.0)
     2370     */
     2371    public function edit_field_html( array $raw_properties = array() ) {
     2372
     2373        // user_id is a special optional parameter that certain other fields types pass to {@link bp_the_profile_field_options()}.
     2374        if ( isset( $raw_properties['user_id'] ) ) {
     2375            unset( $raw_properties['user_id'] );
     2376        }
     2377
     2378        $html = $this->get_edit_field_html_elements( array_merge(
     2379            array(
     2380                'type'  => 'text',
     2381                'value' => bp_get_the_profile_field_edit_value(),
     2382            ),
     2383            $raw_properties
     2384        ) );
     2385    ?>
     2386        <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php esc_html_e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
     2387        <input <?php echo $html; ?>>
     2388    <?php
     2389    }
     2390
     2391    /**
     2392     * Output HTML for this field type on the wp-admin Profile Fields screen.
     2393     *
     2394     * Must be used inside the {@link bp_profile_fields()} template loop.
     2395     *
     2396     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     2397     * @since BuddyPress (2.0.0)
     2398     */
     2399    public function admin_field_html( array $raw_properties = array() ) {
     2400        $html = $this->get_edit_field_html_elements( array_merge(
     2401            array( 'type' => 'text' ),
     2402            $raw_properties
     2403        ) );
     2404    ?>
     2405        <input <?php echo $html; ?>>
     2406    <?php
     2407    }
     2408
     2409    /**
     2410     * This method usually outputs HTML for this field type's children options on the wp-admin Profile Fields
     2411     * "Add Field" and "Edit Field" screens, but for this field type, we don't want it, so it's stubbed out.
     2412     *
     2413     * @param BP_XProfile_Field $current_field The current profile field on the add/edit screen.
     2414     * @param string $control_type Optional. HTML input type used to render the current field's child options.
     2415     * @since BuddyPress (2.0.0)
     2416     */
     2417    public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {}
     2418}
     2419
     2420/**
     2421 * Number xprofile field type.
     2422 *
     2423 * @since BuddyPress (2.0.0)
     2424 */
     2425class BP_XProfile_Field_Type_Number extends BP_XProfile_Field_Type {
     2426
     2427    /**
     2428     * Constructor for the number field type
     2429     *
     2430     * @since BuddyPress (2.0.0)
     2431     */
     2432    public function __construct() {
     2433        parent::__construct();
     2434
     2435        $this->category = _x( 'Single Fields', 'xprofile field type category', 'buddypress' );
     2436        $this->name     = _x( 'Number', 'xprofile field type', 'buddypress' );
     2437
     2438        $this->set_format( '/^\d+|-\d+$/', 'replace' );
     2439        do_action( 'bp_xprofile_field_type_number', $this );
     2440    }
     2441
     2442    /**
     2443     * Output the edit field HTML for this field type.
     2444     *
     2445     * Must be used inside the {@link bp_profile_fields()} template loop.
     2446     *
     2447     * @param array $raw_properties Optional key/value array of {@link http://dev.w3.org/html5/markup/input.number.html permitted attributes} that you want to add.
     2448     * @since BuddyPress (2.0.0)
     2449     */
     2450    public function edit_field_html( array $raw_properties = array() ) {
     2451
     2452        // user_id is a special optional parameter that certain other fields types pass to {@link bp_the_profile_field_options()}.
     2453        if ( isset( $raw_properties['user_id'] ) ) {
     2454            unset( $raw_properties['user_id'] );
     2455        }
     2456
     2457        $html = $this->get_edit_field_html_elements( array_merge(
     2458            array(
     2459                'type'  => 'number',
     2460                'value' =>  bp_get_the_profile_field_edit_value(),
     2461            ),
     2462            $raw_properties
     2463        ) );
     2464    ?>
     2465        <label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php esc_html_e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
     2466        <input <?php echo $html; ?>>
     2467    <?php
     2468    }
     2469
     2470    /**
     2471     * Output HTML for this field type on the wp-admin Profile Fields screen.
     2472     *
     2473     * Must be used inside the {@link bp_profile_fields()} template loop.
     2474     *
     2475     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     2476     * @since BuddyPress (2.0.0)
     2477     */
     2478    public function admin_field_html( array $raw_properties = array() ) {
     2479        $html = $this->get_edit_field_html_elements( array_merge(
     2480            array( 'type' => 'number' ),
     2481            $raw_properties
     2482        ) );
     2483    ?>
     2484        <input <?php echo $html; ?>>
     2485    <?php
     2486    }
     2487
     2488    /**
     2489     * This method usually outputs HTML for this field type's children options on the wp-admin Profile Fields
     2490     * "Add Field" and "Edit Field" screens, but for this field type, we don't want it, so it's stubbed out.
     2491     *
     2492     * @param BP_XProfile_Field $current_field The current profile field on the add/edit screen.
     2493     * @param string $control_type Optional. HTML input type used to render the current field's child options.
     2494     * @since BuddyPress (2.0.0)
     2495     */
     2496    public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {}
     2497}
     2498
     2499/**
     2500 * Represents a type of XProfile field and holds meta information about the type of value that it accepts.
     2501 *
     2502 * @since BuddyPress (2.0.0)
     2503 */
     2504abstract class BP_XProfile_Field_Type {
     2505
     2506    /**
     2507     * @since BuddyPress (2.0.0)
     2508     * @var array Field type validation regexes
     2509     */
     2510    protected $validation_regex = array();
     2511
     2512    /**
     2513     * @since BuddyPress (2.0.0)
     2514     * @var array Field type whitelisted values
     2515     */
     2516    protected $validation_whitelist = array();
     2517
     2518    /**
     2519     * @since BuddyPress (2.0.0)
     2520     * @var string The name of this field type
     2521     */
     2522    public $name = '';
     2523
     2524    /**
     2525     * The name of the category that this field type should be grouped with. Used on the [Users > Profile Fields] screen in wp-admin.
     2526     *
     2527     * @since BuddyPress (2.0.0)
     2528     * @var string
     2529     */
     2530    public $category = '';
     2531
     2532    /**
     2533     * @since BuddyPress (2.0.0)
     2534     * @var bool If this is set, allow BP to store null/empty values for this field type.
     2535     */
     2536    public $accepts_null_value = false;
     2537
     2538    /**
     2539     * If this is set, BP will set this field type's validation whitelist from the field's options (e.g checkbox, selectbox).
     2540     *
     2541     * @since BuddyPress (2.0.0)
     2542     * @var bool Does this field support options? e.g. selectbox, radio buttons, etc.
     2543     */
     2544    public $supports_options = false;
     2545
     2546    /**
     2547     * @since BuddyPress (2.0.0)
     2548     * @var bool Does this field type support multiple options being set as default values? e.g. multiselectbox, checkbox.
     2549     */
     2550    public $supports_multiple_defaults = false;
     2551
     2552    /**
     2553     * @since BuddyPress (2.0.0)
     2554     * @var BP_XProfile_Field If this object is created by instantiating a {@link BP_XProfile_Field}, this is a reference back to that object.
     2555     */
     2556    public $field_obj = null;
     2557
     2558    /**
     2559     * Constructor
     2560     *
     2561     * @since BuddyPress (2.0.0)
     2562     */
     2563    public function __construct() {
     2564        do_action( 'bp_xprofile_field_type', $this );
     2565    }
     2566
     2567    /**
     2568     * Set a regex that profile data will be asserted against.
     2569     *
     2570     * You can call this method multiple times to set multiple formats. When validation is performed,
     2571     * it's successful as long as the new value matches any one of the registered formats.
     2572     *
     2573     * @param string $format Regex string
     2574     * @param string $replace_format Optional; if 'replace', replaces the format instead of adding to it. Defaults to 'add'.
     2575     * @return BP_XProfile_Field_Type
     2576     * @since BuddyPress (2.0.0)
     2577     */
     2578    public function set_format( $format, $replace_format = 'add' ) {
     2579
     2580        $format = apply_filters( 'bp_xprofile_field_type_set_format', $format, $replace_format, $this );
     2581
     2582        if ( 'add' === $replace_format ) {
     2583            $this->validation_regex[] = $format;
     2584        } elseif ( 'replace' === $replace_format ) {
     2585            $this->validation_regex = array( $format );
     2586        }
     2587
     2588        return $this;
     2589    }
     2590
     2591    /**
     2592     * Add a value to this type's whitelist that that profile data will be asserted against.
     2593     *
     2594     * You can call this method multiple times to set multiple formats. When validation is performed,
     2595     * it's successful as long as the new value matches any one of the registered formats.
     2596     *
     2597     * @param string|array $values
     2598     * @return BP_XProfile_Field_Type
     2599     * @since BuddyPress (2.0.0)
     2600     */
     2601    public function set_whitelist_values( $values ) {
     2602        foreach ( (array) $values as $value ) {
     2603            $this->validation_whitelist[] = apply_filters( 'bp_xprofile_field_type_set_whitelist_values', $value, $values, $this );
     2604        }
     2605
     2606        return $this;
     2607    }
     2608
     2609    /**
     2610     * Check the given string against the registered formats for this field type.
     2611     *
     2612     * This method doesn't support chaining.
     2613     *
     2614     * @param string|array $values Value to check against the registered formats
     2615     * @return bool True if the value validates
     2616     * @since BuddyPress (2.0.0)
     2617     */
     2618    public function is_valid( $values ) {
     2619        $validated = false;
     2620
     2621        // Some types of field (e.g. multi-selectbox) may have multiple values to check
     2622        foreach ( (array) $values as $value ) {
     2623
     2624            // Validate the $value against the type's accepted format(s).
     2625            foreach ( $this->validation_regex as $format ) {
     2626                if ( 1 === preg_match( $format, $value ) ) {
     2627                    $validated = true;
     2628                    continue;
     2629
     2630                } else {
     2631                    $validated = false;
     2632                }
     2633            }
     2634        }
     2635
     2636        // Handle field types with accepts_null_value set if $values is an empty array
     2637        if ( ! $validated && is_array( $values ) && empty( $values ) && $this->accepts_null_value ) {
     2638            $validated = true;
     2639        }
     2640
     2641        // If there's a whitelist set, also check the $value.
     2642        if ( $validated && ! empty( $values ) && ! empty( $this->validation_whitelist ) ) {
     2643
     2644            foreach ( (array) $values as $value ) {
     2645                $validated = in_array( $value, $this->validation_whitelist, true );
     2646            }
     2647        }
     2648
     2649        return (bool) apply_filters( 'bp_xprofile_field_type_is_valid', $validated, $values, $this );
     2650    }
     2651
     2652    /**
     2653     * Output the edit field HTML for this field type.
     2654     *
     2655     * Must be used inside the {@link bp_profile_fields()} template loop.
     2656     *
     2657     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     2658     * @since BuddyPress (2.0.0)
     2659     */
     2660    abstract public function edit_field_html( array $raw_properties = array() );
     2661
     2662    /**
     2663     * Output the edit field options HTML for this field type.
     2664     *
     2665     * BuddyPress considers a field's "options" to be, for example, the items in a selectbox.
     2666     * These are stored separately in the database, and their templating is handled separately.
     2667     * Populate this method in a child class if it's required. Otherwise, you can leave it out.
     2668     *
     2669     * This templating is separate from {@link BP_XProfile_Field_Type::edit_field_html()} because
     2670     * it's also used in the wp-admin screens when creating new fields, and for backwards compatibility.
     2671     *
     2672     * Must be used inside the {@link bp_profile_fields()} template loop.
     2673     *
     2674     * @param array $args Optional. The arguments passed to {@link bp_the_profile_field_options()}.
     2675     * @since BuddyPress (2.0.0)
     2676     */
     2677    public function edit_field_options_html( array $args = array() ) {}
     2678
     2679    /**
     2680     * Output HTML for this field type on the wp-admin Profile Fields screen.
     2681     *
     2682     * Must be used inside the {@link bp_profile_fields()} template loop.
     2683     *
     2684     * @param array $raw_properties Optional key/value array of permitted attributes that you want to add.
     2685     * @since BuddyPress (2.0.0)
     2686     */
     2687    abstract public function admin_field_html( array $raw_properties = array() );
     2688
     2689    /**
     2690     * Output HTML for this field type's children options on the wp-admin Profile Fields "Add Field" and "Edit Field" screens.
     2691     *
     2692     * You don't need to implement this method for all field types. It's used in core by the
     2693     * selectbox, multi selectbox, checkbox, and radio button fields, to allow the admin to
     2694     * enter the child option values (e.g. the choices in a select box).
     2695     *
     2696     * Must be used inside the {@link bp_profile_fields()} template loop.
     2697     *
     2698     * @param BP_XProfile_Field $current_field The current profile field on the add/edit screen.
     2699     * @param string $control_type Optional. HTML input type used to render the current field's child options.
     2700     * @since BuddyPress (2.0.0)
     2701     */
     2702    public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {
     2703        $type = array_search( get_class( $this ), bp_xprofile_get_field_types() );
     2704        if ( false === $type ) {
     2705            return;
     2706        }
     2707
     2708        $class            = $current_field->type != $type ? 'display: none;' : '';
     2709        $current_type_obj = bp_xprofile_create_field_type( $type );
     2710        ?>
     2711
     2712        <div id="<?php echo esc_attr( $type ); ?>" class="postbox bp-options-box" style="<?php echo esc_attr( $class ); ?> margin-top: 15px;">
     2713            <h3><?php esc_html_e( 'Please enter options for this Field:', 'buddypress' ); ?></h3>
     2714            <div class="inside">
     2715                <p>
     2716                    <label for="sort_order_<?php echo esc_attr( $type ); ?>"><?php esc_html_e( 'Sort Order:', 'buddypress' ); ?></label>
     2717                    <select name="sort_order_<?php echo esc_attr( $type ); ?>" id="sort_order_<?php echo esc_attr( $type ); ?>" >
     2718                        <option value="custom" <?php selected( 'custom', $current_field->order_by ); ?>><?php esc_html_e( 'Custom',     'buddypress' ); ?></option>
     2719                        <option value="asc"    <?php selected( 'asc',    $current_field->order_by ); ?>><?php esc_html_e( 'Ascending',  'buddypress' ); ?></option>
     2720                        <option value="desc"   <?php selected( 'desc',   $current_field->order_by ); ?>><?php esc_html_e( 'Descending', 'buddypress' ); ?></option>
     2721                    </select>
     2722                </p>
     2723
     2724                <?php
     2725                $options = $current_field->get_children( true );
     2726
     2727                // If no children options exists for this field, check in $_POST for a submitted form (e.g. on the "new field" screen).
     2728                if ( ! $options ) {
     2729
     2730                    $options = array();
     2731                    $i       = 1;
     2732
     2733                    while ( isset( $_POST[$type . '_option'][$i] ) ) {
     2734
     2735                        // Multiselectbox and checkboxes support MULTIPLE default options; all other core types support only ONE.
     2736                        if ( $current_type_obj->supports_options && ! $current_type_obj->supports_multiple_defaults && isset( $_POST["isDefault_{$type}_option"][$i] ) && (int) $_POST["isDefault_{$type}_option"] === $i ) {
     2737                            $is_default_option = true;
     2738                        } elseif ( isset( $_POST["isDefault_{$type}_option"][$i] ) ) {
     2739                            $is_default_option = (bool) $_POST["isDefault_{$type}_option"][$i];
     2740                        } else {
     2741                            $is_default_option = false;
     2742                        }
     2743
     2744                        // Grab the values from $_POST to use as the form's options
     2745                        $options[] = (object) array(
     2746                            'id'                => -1,
     2747                            'is_default_option' => $is_default_option,
     2748                            'name'              => sanitize_text_field( stripslashes( $_POST[$type . '_option'][$i] ) ),
     2749                        );
     2750
     2751                        ++$i;
     2752                    }
     2753
     2754                    // If there are still no children options set, this must be the "new field" screen, so add one new/empty option.
     2755                    if ( ! $options ) {
     2756                        $options[] = (object) array(
     2757                            'id'                => -1,
     2758                            'is_default_option' => false,
     2759                            'name'              => '',
     2760                        );
     2761                    }
     2762                }
     2763
     2764                // Render the markup for the children options
     2765                if ( ! empty( $options ) ) {
     2766                    $default_name = '';
     2767
     2768                    for ( $i = 0, $count = count( $options ); $i < $count; ++$i ) :
     2769                        $j = $i + 1;
     2770
     2771                        // Multiselectbox and checkboxes support MULTIPLE default options; all other core types support only ONE.
     2772                        if ( $current_type_obj->supports_options && $current_type_obj->supports_multiple_defaults ) {
     2773                            $default_name = '[' . $j . ']';
     2774                        }
     2775                        ?>
     2776
     2777                        <p class="sortable">
     2778                            <span>&nbsp;&Xi;&nbsp;</span>
     2779                            <input type="text" name="<?php echo esc_attr( "{$type}_option[{$j}]" ); ?>" id="<?php echo esc_attr( "{$type}_option{$j}" ); ?>" value="<?php echo esc_attr( $options[$i]->name ); ?>" />
     2780                            <input type="<?php echo esc_attr( $control_type ); ?>" name="<?php echo esc_attr( "isDefault_{$type}_option{$default_name}" ); ?>" <?php checked( $options[$i]->is_default_option, true ); ?> value="<?php echo esc_attr( $j ); ?>" />
     2781                            <span><?php _e( 'Default Value', 'buddypress' ); ?></span>
     2782                        </p>
     2783                    <?php endfor; ?>
     2784
     2785                    <input type="hidden" name="<?php echo esc_attr( "{$type}_option_number" ); ?>" id="<?php echo esc_attr( "{$type}_option_number" ); ?>" value="<?php echo esc_attr( $j + 1 ); ?>" />
     2786                <?php } ?>
     2787
     2788                <div id="<?php echo esc_attr( "{$type}_more" ); ?>"></div>
     2789                <p><a href="javascript:add_option('<?php echo esc_js( $type ); ?>')"><?php esc_html_e( 'Add Another Option', 'buddypress' ); ?></a></p>
     2790            </div>
     2791        </div>
     2792
     2793        <?php
     2794    }
     2795
     2796
     2797    /**
     2798     * Internal protected/private helper methods past this point.
     2799     */
     2800
     2801    /**
     2802     * Get a sanitised and escaped string of the edit field's HTML elements and attributes.
     2803     *
     2804     * Must be used inside the {@link bp_profile_fields()} template loop.
     2805     * This method was intended to be static but couldn't be because php.net/lsb/ requires PHP >= 5.3.
     2806     *
     2807     * @param array $properties Optional key/value array of attributes for this edit field.
     2808     * @return string
     2809     * @since BuddyPress (2.0.0)
     2810     */
     2811    protected function get_edit_field_html_elements( array $properties = array() ) {
     2812
     2813        $properties = array_merge( array(
     2814            'id'   => bp_get_the_profile_field_input_name(),
     2815            'name' => bp_get_the_profile_field_input_name(),
     2816        ), $properties );
     2817
     2818        if ( bp_get_the_profile_field_is_required() ) {
     2819            $properties['aria-required'] = 'true';
     2820        }
     2821
     2822        $html       = '';
     2823        $properties = (array) apply_filters( 'bp_xprofile_field_edit_html_elements', $properties, get_class( $this ) );
     2824
     2825        foreach ( $properties as $name => $value ) {
     2826            $html .= sprintf( '%s="%s" ', sanitize_key( $name ), esc_attr( $value ) );
     2827        }
     2828
     2829        return $html;
     2830    }
     2831}
  • 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
  • trunk/bp-xprofile/bp-xprofile-loader.php

    r7960 r8178  
    9999
    100100        // Set the support field type ids
    101         $this->field_types = apply_filters( 'xprofile_field_types', array(
    102             'textbox',
    103             'textarea',
    104             'radio',
    105             'checkbox',
    106             'selectbox',
    107             'multiselectbox',
    108             'datebox'
    109         ) );
     101        $this->field_types = apply_filters( 'xprofile_field_types', array_keys( bp_xprofile_get_field_types() ) );
    110102
    111103        // Register the visibility levels. See bp_xprofile_get_visibility_levels() to filter
  • trunk/bp-xprofile/bp-xprofile-screens.php

    r7998 r8178  
    107107
    108108                // Certain types of fields (checkboxes, multiselects) may come through empty. Save them as an empty array so that they don't get overwritten by the default on the next edit.
    109                 if ( empty( $_POST['field_' . $field_id] ) ) {
    110                     $value = array();
    111                 } else {
    112                     $value = $_POST['field_' . $field_id];
    113                 }
     109                $value = isset( $_POST['field_' . $field_id] ) ? $_POST['field_' . $field_id] : '';
    114110
    115111                if ( !xprofile_set_field_data( $field_id, bp_displayed_user_id(), $value, $is_required[$field_id] ) ) {
  • 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;
  • trunk/tests/testcases/xprofile/functions.php

    r8133 r8178  
    553553        $this->assertSame( 'foo', $found );
    554554    }
     555
     556    /**
     557     * @group xprofile_set_field_data
     558     */
     559    public function test_get_field_data_integer_zero() {
     560        $u = $this->create_user();
     561        $g = $this->factory->xprofile_group->create();
     562        $f = $this->factory->xprofile_field->create( array(
     563            'field_group_id' => $g,
     564            'type' => 'number',
     565            'name' => 'Pens',
     566        ) );
     567        xprofile_set_field_data( $f, $u, 0 );
     568
     569        $this->assertEquals( 0, xprofile_get_field_data( 'Pens', $u ) );
     570    }
    555571}
Note: See TracChangeset for help on using the changeset viewer.