Skip to:
Content

BuddyPress.org

Ticket #6347: 6347.04.patch

File 6347.04.patch, 65.4 KB (added by johnjamesjacoby, 5 years ago)

More incomplete iteration; uploading here incase I die before tomorrow

  • src/bp-xprofile/bp-xprofile-classes.php

     
    2323require dirname( __FILE__ ) . '/classes/class-bp-xprofile-field-type-number.php';
    2424require dirname( __FILE__ ) . '/classes/class-bp-xprofile-field-type-url.php';
    2525require dirname( __FILE__ ) . '/classes/class-bp-xprofile-field-type-placeholder.php';
     26require dirname( __FILE__ ) . '/classes/class-bp-xprofile-meta-query.php';
    2627require dirname( __FILE__ ) . '/classes/class-bp-xprofile-query.php';
  • src/bp-xprofile/bp-xprofile-filters.php

     
    6969add_filter( 'xprofile_field_option_order_before_save', 'absint' );
    7070add_filter( 'xprofile_field_can_delete_before_save',   'absint' );
    7171
     72// Save field options
     73add_filter( 'xprofile_field_options_before_save', 'bp_xprofile_sanitize_field_options' );
     74add_filter( 'xprofile_field_default_before_save', 'bp_xprofile_sanitize_field_default' );
     75
    7276/**
     77 * Sanitize each field option name for saving to the database
     78 *
     79 * @since BuddyPress (2.3.0)
     80 *
     81 * @param  mixed $field_options
     82 * @return mixed
     83 */
     84function bp_xprofile_sanitize_field_options( $field_options = '' ) {
     85        if ( is_array( $field_options ) ) {
     86                return array_map( 'sanitize_text_field', $field_options );
     87        } else {
     88                return sanitize_text_field( $field_options );
     89        }
     90}
     91
     92/**
     93 * Sanitize each field option default for saving to the database
     94 *
     95 * @since BuddyPress (2.3.0)
     96 *
     97 * @param  mixed $field_default
     98 * @return mixed
     99 */
     100function bp_xprofile_sanitize_field_default( $field_default = '' ) {
     101        if ( is_array( $field_default ) ) {
     102                return array_map( 'intval', $field_default );
     103        } else {
     104                return intval( $field_default );
     105        }
     106}
     107
     108/**
    73109 * xprofile_filter_kses ( $content )
    74110 *
    75111 * Run profile field values through kses with filterable allowed tags.
  • src/bp-xprofile/bp-xprofile-functions.php

     
    968968}
    969969
    970970/**
     971 * Get array of field IDs to show on member registration page
     972 *
     973 * @since BuddyPress (2.3.0)
     974 *
     975 * @return array
     976 */
     977function bp_xprofile_get_signup_field_ids() {
     978
     979        // Query for specificly set signup fields
     980        $table_name = buddypress()->profile->table_name_meta;
     981        $fields     = BP_XProfile_Field::get_fields( array(
     982                'meta_query' => array(
     983                        array(
     984                                'key'     => 'signup_position',
     985                                'object'  => 'field',
     986                                'compare' => '='
     987                        )
     988                ),
     989                'order_by' => "{$table_name}.meta_value"
     990        ) );
     991
     992        // No signup fields have been set, so query for all fields in the primary
     993        // group ID
     994        if ( empty( $fields ) ) {
     995                $fields = BP_XProfile_Field::get_fields( array(
     996                        'group_id' => '1'
     997                ) );
     998        }
     999
     1000        // Pluck the ID's from the fields
     1001        $field_ids = wp_list_pluck( $fields, 'id' );
     1002
     1003        return apply_filters( 'bp_xprofile_get_signup_field_ids', $field_ids, $fields );
     1004}
     1005
     1006/**
    9711007 * Return the field ID for the Full Name xprofile field.
    9721008 *
    9731009 * @since BuddyPress (2.0.0)
  • src/bp-xprofile/bp-xprofile-loader.php

     
    367367                wp_cache_add_global_groups( array(
    368368                        'bp_xprofile',
    369369                        'bp_xprofile_data',
     370                        'bp_xprofile_fields',
    370371                        'bp_xprofile_groups',
    371372                        'xprofile_meta'
    372373                ) );
  • src/bp-xprofile/classes/class-bp-xprofile-field.php

     
    110110        public $allow_custom_visibility = 'allowed';
    111111
    112112        /**
     113         * @since BuddyPress (2.3.0)
     114         *
     115         * @var int Position of field on user registration page
     116         */
     117        public $signup_position = null;
     118
     119        /**
    113120         * @since BuddyPress (2.0.0)
    114121         *
    115122         * @var BP_XProfile_Field_Type Field type object used for validation
     
    157164         * @param  bool   $get_data
    158165         */
    159166        public function populate( $id, $user_id = null, $get_data = true ) {
    160                 global $wpdb, $userdata;
    161167
    162                 if ( empty( $user_id ) ) {
    163                         $user_id = isset( $userdata->ID ) ? $userdata->ID : 0;
    164                 }
     168                // Check for cached field
     169                $field = wp_cache_get( $id, 'bp_xprofile_fields' );
    165170
    166                 $bp    = buddypress();
    167                 $field = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->profile->table_name_fields} WHERE id = %d", $id ) );
     171                // Field not cached, so query (and subsequently cache)
     172                if ( false === $field ) {
    168173
    169                 if ( ! empty( $field ) ) {
    170                         $this->id                = $field->id;
    171                         $this->group_id          = $field->group_id;
    172                         $this->parent_id         = $field->parent_id;
    173                         $this->type              = $field->type;
    174                         $this->name              = stripslashes( $field->name );
    175                         $this->description       = stripslashes( $field->description );
    176                         $this->is_required       = $field->is_required;
    177                         $this->can_delete        = $field->can_delete;
    178                         $this->field_order       = $field->field_order;
    179                         $this->option_order      = $field->option_order;
    180                         $this->order_by          = $field->order_by;
    181                         $this->is_default_option = $field->is_default_option;
     174                        // Setup args
     175                        $args = array(
     176                                'include' => $id
     177                        );
    182178
    183                         // Create the field type and store a reference back to this object.
    184                         $this->type_obj            = bp_xprofile_create_field_type( $field->type );
    185                         $this->type_obj->field_obj = $this;
    186 
    187                         if ( ! empty( $get_data ) && ! empty( $user_id ) ) {
    188                                 $this->data = $this->get_field_data( $user_id );
     179                        // Get data for the user ID
     180                        if ( ! empty( $user_id ) && ! empty( $get_data ) ) {
     181                                $args['fetch_data'] = $user_id;
    189182                        }
    190183
    191                         // Get metadata for field
    192                         $default_visibility       = bp_xprofile_get_meta( $id, 'field', 'default_visibility'      );
    193                         $allow_custom_visibility  = bp_xprofile_get_meta( $id, 'field', 'allow_custom_visibility' );
     184                        // Get the field and its data
     185                        $field = self::get_fields( $args );
    194186
    195                         // Setup default visibility
    196                         $this->default_visibility = ! empty( $default_visibility )
    197                                 ? $default_visibility
    198                                 : 'public';
     187                        // Use the first item if an array
     188                        if ( array( $field ) ) {
     189                                $field = reset( $field );
     190                        }
     191                }
    199192
    200                         // Allow members to customize visibilty
    201                         $this->allow_custom_visibility = ( 'disabled' === $allow_custom_visibility )
    202                                 ? 'disabled'
    203                                 : 'allowed';
     193                // Bail if field could not be found
     194                if ( empty( $field ) ) {
     195                        return;
    204196                }
    205         }
    206197
    207         /**
    208          * Delete a profile field
    209          *
    210          * @since BuddyPress (1.1.0)
    211          *
    212          * @global object  $wpdb
    213          * @param  boolean $delete_data
    214          * @return boolean
    215          */
    216         public function delete( $delete_data = false ) {
    217                 global $wpdb;
     198                // Setup this field
     199                $this->id                = $field->id;
     200                $this->group_id          = $field->group_id;
     201                $this->parent_id         = $field->parent_id;
     202                $this->type              = $field->type;
     203                $this->name              = stripslashes( $field->name );
     204                $this->description       = stripslashes( $field->description );
     205                $this->is_required       = $field->is_required;
     206                $this->is_default_option = $field->is_default_option;
     207                $this->field_order       = $field->field_order;
     208                $this->option_order      = $field->option_order;
     209                $this->order_by          = $field->order_by;
     210                $this->can_delete        = $field->can_delete;
    218211
    219                 // Prevent deletion if no ID is present
    220                 // Prevent deletion by url when can_delete is false.
    221                 // Prevent deletion of option 1 since this invalidates fields with options.
    222                 if ( empty( $this->id ) || empty( $this->can_delete ) || ( $this->parent_id && $this->option_order == 1 ) ) {
    223                         return false;
     212                // Set field data if it was requested
     213                if ( ! empty( $field->data ) ) {
     214                        $this->data = $field->data;
    224215                }
    225216
    226                 $bp  = buddypress();
    227                 $sql = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE id = %d OR parent_id = %d", $this->id, $this->id );
     217                // Create the field type and store a reference back to this object.
     218                $this->type_obj            = bp_xprofile_create_field_type( $field->type );
     219                $this->type_obj->field_obj = $this;
    228220
    229                 if ( ! $wpdb->query( $sql ) ) {
    230                         return false;
    231                 }
     221                // Get core metadata for field
     222                $default_visibility      = bp_xprofile_get_meta( $id, 'field', 'default_visibility'      );
     223                $allow_custom_visibility = bp_xprofile_get_meta( $id, 'field', 'allow_custom_visibility' );
     224                $signup_position         = bp_xprofile_get_meta( $id, 'field', 'signup_position'         );
    232225
    233                 // delete the data in the DB for this field
    234                 if ( true === $delete_data ) {
    235                         BP_XProfile_ProfileData::delete_for_field( $this->id );
    236                 }
     226                // Setup default visibility
     227                $this->default_visibility = ! empty( $default_visibility )
     228                        ? $default_visibility
     229                        : 'public';
    237230
    238                 return true;
     231                // Allow members to customize visibilty
     232                $this->allow_custom_visibility = ( 'disabled' === $allow_custom_visibility )
     233                        ? 'disabled'
     234                        : 'allowed';
     235
     236                // Is this field used on the registration page
     237                $this->signup_position = ( false !== $signup_position )
     238                        ? $signup_position
     239                        : null;
    239240        }
    240241
    241242        /**
     
    250251        public function save() {
    251252                global $wpdb;
    252253
    253                 $bp = buddypress();
     254                // Filter field variables before they are saved
     255                $this->group_id          = apply_filters( 'xprofile_field_group_id_before_save',          $this->group_id,          $this->id );
     256                $this->parent_id         = apply_filters( 'xprofile_field_parent_id_before_save',         $this->parent_id,         $this->id );
     257                $this->type              = apply_filters( 'xprofile_field_type_before_save',              $this->type,              $this->id );
     258                $this->name              = apply_filters( 'xprofile_field_name_before_save',              $this->name,              $this->id );
     259                $this->description       = apply_filters( 'xprofile_field_description_before_save',       $this->description,       $this->id );
     260                $this->is_required       = apply_filters( 'xprofile_field_is_required_before_save',       $this->is_required,       $this->id );
     261                $this->is_default_option = apply_filters( 'xprofile_field_is_default_option_before_save', $this->is_default_option, $this->id );
     262                $this->field_order       = apply_filters( 'xprofile_field_field_order_before_save',       $this->field_order,       $this->id );
     263                $this->option_order      = apply_filters( 'xprofile_field_option_order_before_save',      $this->option_order,      $this->id );
     264                $this->order_by          = apply_filters( 'xprofile_field_order_by_before_save',          $this->order_by,          $this->id );
     265                $this->can_delete        = apply_filters( 'xprofile_field_can_delete_before_save',        $this->can_delete,        $this->id );
    254266
    255                 $this->group_id     = apply_filters( 'xprofile_field_group_id_before_save',     $this->group_id,     $this->id );
    256                 $this->parent_id    = apply_filters( 'xprofile_field_parent_id_before_save',    $this->parent_id,    $this->id );
    257                 $this->type         = apply_filters( 'xprofile_field_type_before_save',         $this->type,         $this->id );
    258                 $this->name         = apply_filters( 'xprofile_field_name_before_save',         $this->name,         $this->id );
    259                 $this->description  = apply_filters( 'xprofile_field_description_before_save',  $this->description,  $this->id );
    260                 $this->is_required  = apply_filters( 'xprofile_field_is_required_before_save',  $this->is_required,  $this->id );
    261                 $this->order_by     = apply_filters( 'xprofile_field_order_by_before_save',     $this->order_by,     $this->id );
    262                 $this->field_order  = apply_filters( 'xprofile_field_field_order_before_save',  $this->field_order,  $this->id );
    263                 $this->option_order = apply_filters( 'xprofile_field_option_order_before_save', $this->option_order, $this->id );
    264                 $this->can_delete   = apply_filters( 'xprofile_field_can_delete_before_save',   $this->can_delete,   $this->id );
    265                 $this->type_obj     = bp_xprofile_create_field_type( $this->type );
    266 
    267267                /**
    268268                 * Fires before the current field instance gets saved.
    269269                 *
     
    275275                 */
    276276                do_action_ref_array( 'xprofile_field_before_save', array( $this ) );
    277277
    278                 if ( $this->id != null ) {
    279                         $sql = $wpdb->prepare( "UPDATE {$bp->profile->table_name_fields} SET group_id = %d, parent_id = 0, type = %s, name = %s, description = %s, is_required = %d, order_by = %s, field_order = %d, option_order = %d, can_delete = %d WHERE id = %d", $this->group_id, $this->type, $this->name, $this->description, $this->is_required, $this->order_by, $this->field_order, $this->option_order, $this->can_delete, $this->id );
     278                // Get the profile field table name
     279                $table_name = buddypress()->profile->table_name_fields;
     280
     281                // Existing field
     282                if ( ! empty( $this->id ) ) {
     283                        $sql = $wpdb->prepare( "UPDATE {$table_name} SET group_id = %d, parent_id = %d, type = %s, name = %s, description = %s, is_required = %d, is_default_option = %d, order_by = %s, field_order = %d, option_order = %d, can_delete = %d WHERE id = %d", $this->group_id, $this->parent_id, $this->type, $this->name, $this->description, $this->is_required, $this->is_default_option, $this->order_by, $this->field_order, $this->option_order, $this->can_delete, $this->id );
     284
     285                // New field
    280286                } else {
    281                         $sql = $wpdb->prepare( "INSERT INTO {$bp->profile->table_name_fields} (group_id, parent_id, type, name, description, is_required, order_by, field_order, option_order, can_delete ) VALUES (%d, %d, %s, %s, %s, %d, %s, %d, %d, %d )", $this->group_id, $this->parent_id, $this->type, $this->name, $this->description, $this->is_required, $this->order_by, $this->field_order, $this->option_order, $this->can_delete );
     287                        $sql = $wpdb->prepare( "INSERT INTO {$table_name} (group_id, parent_id, type, name, description, is_required, is_default_option, order_by, field_order, option_order, can_delete ) VALUES (%d, %d, %s, %s, %s, %d, %d, %s, %d, %d, %d )",             $this->group_id, $this->parent_id, $this->type, $this->name, $this->description, $this->is_required, $this->is_default_option, $this->order_by, $this->field_order, $this->option_order, $this->can_delete );
    282288                }
    283289
     290                // Attempt to update or insert
     291                $query = $wpdb->query( $sql );
     292
    284293                /**
    285                  * Check for null so field options can be changed without changing any
    286                  * other part of the field. The described situation will return 0 here.
     294                 * Check for `null` instead of `false` or `empty()` to allow child
     295                 * field-options to be modified even if no other field properties changed.
    287296                 */
    288                 if ( $wpdb->query( $sql ) !== null ) {
     297                if ( ( null === $query ) || is_wp_error( $query ) ) {
     298                        return false;
     299                }
    289300
    290                         if ( !empty( $this->id ) ) {
    291                                 $field_id = $this->id;
    292                         } else {
    293                                 $field_id = $wpdb->insert_id;
    294                         }
     301                // Set the field ID on INSERT
     302                if ( empty( $this->id ) ) {
     303                        $this->id = $wpdb->insert_id;
     304                }
    295305
    296                         // Only do this if we are editing an existing field
    297                         if ( $this->id != null ) {
     306                // Setup the field's field-type object immediately before cache actions
     307                // and child-field updates take place, to ensure all necessary field
     308                // data is available to these methods.
     309                $this->type_obj            = bp_xprofile_create_field_type( $this->type );
     310                $this->type_obj->field_obj = $this;
    298311
    299                                 /**
    300                                  * Remove any radio or dropdown options for this
    301                                  * field. They will be re-added if needed.
    302                                  * This stops orphan options if the user changes a
    303                                  * field from a radio button field to a text box.
    304                                  */
    305                                 $this->delete_children();
    306                         }
     312                // Delete the relevant caches
     313                $this->purge_caches();
    307314
    308                         /**
    309                          * Check to see if this is a field with child options.
    310                          * We need to add the options to the db, if it is.
    311                          */
    312                         if ( $this->type_obj->supports_options ) {
     315                // Maybe update child fields
     316                $this->update_field_options();
    313317
    314                                 if ( !empty( $this->id ) ) {
    315                                         $parent_id = $this->id;
    316                                 } else {
    317                                         $parent_id = $wpdb->insert_id;
    318                                 }
     318                /**
     319                 * Fires after the current field instance gets saved.
     320                 *
     321                 * @since BuddyPress (1.0.0)
     322                 *
     323                 * @param BP_XProfile_Field Current instance of the field being saved.
     324                 */
     325                do_action_ref_array( 'xprofile_field_after_save', array( $this ) );
    319326
    320                                 // Allow plugins to filter the field's child options (i.e. the items in a selectbox).
    321                                 $post_option  = ! empty( $_POST["{$this->type}_option"]           ) ? $_POST["{$this->type}_option"]           : '';
    322                                 $post_default = ! empty( $_POST["isDefault_{$this->type}_option"] ) ? $_POST["isDefault_{$this->type}_option"] : '';
    323 
    324                                 /**
    325                                  * Filters the submitted field option value before saved.
    326                                  *
    327                                  * @since BuddyPress (1.5.0)
    328                                  *
    329                                  * @param string            $post_option Submitted option value.
    330                                  * @param BP_XProfile_Field $type        Current field type being saved for.
    331                                  */
    332                                 $options      = apply_filters( 'xprofile_field_options_before_save', $post_option,  $this->type );
    333 
    334                                 /**
    335                                  * Filters the default field option value before saved.
    336                                  *
    337                                  * @since BuddyPress (1.5.0)
    338                                  *
    339                                  * @param string            $post_default Default option value.
    340                                  * @param BP_XProfile_Field $type         Current field type being saved for.
    341                                  */
    342                                 $defaults     = apply_filters( 'xprofile_field_default_before_save', $post_default, $this->type );
    343 
    344                                 $counter = 1;
    345                                 if ( !empty( $options ) ) {
    346                                         foreach ( (array) $options as $option_key => $option_value ) {
    347                                                 $is_default = 0;
    348 
    349                                                 if ( is_array( $defaults ) ) {
    350                                                         if ( isset( $defaults[ $option_key ] ) ) {
    351                                                                 $is_default = 1;
    352                                                         }
    353                                                 } else {
    354                                                         if ( (int) $defaults == $option_key ) {
    355                                                                 $is_default = 1;
    356                                                         }
    357                                                 }
    358 
    359                                                 if ( '' != $option_value ) {
    360                                                         $sql = $wpdb->prepare( "INSERT INTO {$bp->profile->table_name_fields} (group_id, parent_id, type, name, description, is_required, option_order, is_default_option) VALUES (%d, %d, 'option', %s, '', 0, %d, %d)", $this->group_id, $parent_id, $option_value, $counter, $is_default );
    361                                                         if ( ! $wpdb->query( $sql ) ) {
    362                                                                 return false;
    363                                                         }
    364                                                 }
    365 
    366                                                 $counter++;
    367                                         }
    368                                 }
    369                         }
    370 
    371                         /**
    372                          * Fires after the current field instance gets saved.
    373                          *
    374                          * @since BuddyPress (1.0.0)
    375                          *
    376                          * @param BP_XProfile_Field Current instance of the field being saved.
    377                          */
    378                         do_action_ref_array( 'xprofile_field_after_save', array( $this ) );
    379 
    380                         // Recreate type_obj in case someone changed $this->type via a filter
    381                         $this->type_obj            = bp_xprofile_create_field_type( $this->type );
    382                         $this->type_obj->field_obj = $this;
    383 
    384                         return $field_id;
    385                 } else {
    386                         return false;
    387                 }
     327                // Return the field ID
     328                return (int) $this->id;
    388329        }
    389330
    390331        /**
     
    404345         *
    405346         * @since BuddyPress (1.2.0)
    406347         *
    407          * @global object $wpdb
    408          *
    409348         * @param  bool  $for_editing
    410349         * @return array
    411350         */
    412351        public function get_children( $for_editing = false ) {
    413                 global $wpdb;
    414352
    415353                // This is done here so we don't have problems with sql injection
    416                 if ( empty( $for_editing ) && ( 'asc' === $this->order_by ) ) {
    417                         $sort_sql = 'ORDER BY name ASC';
    418                 } elseif ( empty( $for_editing ) && ( 'desc' === $this->order_by ) ) {
    419                         $sort_sql = 'ORDER BY name DESC';
     354                if ( ( false === $for_editing ) && in_array( strtoupper( $this->order_by ), array( 'ASC', 'DESC' ) ) ) {
     355                        $order_by = 'name';
     356                        $sort     = strtoupper( $this->order_by );
    420357                } else {
    421                         $sort_sql = 'ORDER BY option_order ASC';
     358                        $order_by = 'f.option_order';
     359                        $sort     = 'ASC';
    422360                }
    423361
    424                 // This eliminates a problem with getting all fields when there is no
    425                 // id for the object
    426                 if ( empty( $this->id ) ) {
    427                         $parent_id = -1;
    428                 } else {
    429                         $parent_id = $this->id;
     362                // Get children
     363                $children = self::get_fields( array(
     364                        'parent_id'         => $this->id,
     365                        'order_by'          => $order_by,
     366                        'sort'              => $sort,
     367                        'update_meta_cache' => false
     368                ) );
     369
     370                // Set children to false if empty or invalid
     371                if ( empty( $children ) || ! is_array( $children ) ) {
     372                        $children = false;
    430373                }
    431374
    432                 $bp  = buddypress();
    433                 $sql = $wpdb->prepare( "SELECT * FROM {$bp->profile->table_name_fields} WHERE parent_id = %d AND group_id = %d {$sort_sql}", $parent_id, $this->group_id );
    434 
    435                 $children = $wpdb->get_results( $sql );
    436 
    437375                /**
    438376                 * Filters the found children for a field.
    439377                 *
     
    446384        }
    447385
    448386        /**
     387         * Delete a profile field
     388         *
     389         * @since BuddyPress (1.1.0)
     390         *
     391         * @global object  $wpdb
     392         * @param  boolean $delete_data
     393         * @param  boolean $delete_children
     394         * @return boolean
     395         */
     396        public function delete( $delete_data = false, $delete_children = true ) {
     397                global $wpdb;
     398
     399                // Prevent deletion if no ID is present
     400                // Prevent deletion by url when can_delete is false.
     401                if ( empty( $this->id ) || empty( $this->can_delete ) ) {
     402                        return false;
     403                }
     404
     405                $bp = buddypress();
     406
     407                // Attempt to get fields to delete
     408                $sql      = $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE id = %d LIMIT 1", $this->id );
     409                $field_id = $wpdb->get_var( $sql );
     410
     411                // Bail if no children exist
     412                if ( empty( $field_id ) || is_wp_error( $field_id ) ) {
     413                        return false;
     414                }
     415
     416                // Attempt to delete
     417                $sql     = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE id = %d LIMIT 1", $this->id );
     418                $deleted = $wpdb->query( $sql );
     419
     420                // Bail if no children exist
     421                if ( empty( $deleted ) || is_wp_error( $deleted ) ) {
     422                        return false;
     423                }
     424
     425                // Delete cache
     426                // Delete the relevant caches
     427                $this->purge_caches();
     428
     429                // Maybe delete children
     430                if ( true === $delete_children ) {
     431                        $this->delete_children();
     432                }
     433
     434                // delete the data in the DB for this field
     435                if ( true === $delete_data ) {
     436                        BP_XProfile_ProfileData::delete_for_field( $this->id );
     437                }
     438
     439                return true;
     440        }
     441
     442        /**
    449443         * Delete all field children for this field
    450444         *
    451445         * @since BuddyPress (1.2.0)
     
    453447         * @global object $wpdb
    454448         */
    455449        public function delete_children() {
     450
     451                return BP_XProfile_Field::delete_fields( array(
     452                        'parent_id'   => $this->id,
     453                        'delete_data' => false
     454                ) );
     455        }
     456
     457        /** Static Methods ********************************************************/
     458
     459        /**
     460         * Get profile fields, as specified by parameters
     461         *
     462         * @see WP_Meta_Query::queries for a description of the 'meta_query'
     463         *      parameter format.
     464         *
     465         * @param array $args {
     466         *     An array of arguments. All items are optional.
     467
     468         * }
     469         */
     470        public static function get_fields( $args = '' ) {
    456471                global $wpdb;
    457472
    458                 $bp  = buddypress();
    459                 $sql = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE parent_id = %d", $this->id );
     473                $r = bp_parse_args( $args, array(
     474                        'include'           => false,
     475                        'exclude'           => false,
     476                        'group_id'          => false,
     477                        'group_not_in'      => false,
     478                        'parent_id'         => false,
     479                        'parent_not_in'     => false,
     480                        'type_in'           => false,
     481                        'type_not_in'       => false,
     482                        'name'              => '',
     483                        'is_required'       => false,
     484                        'is_default_option' => false,
     485                        'can_delete'        => 1,
     486                        'order_by'          => false,
     487                        'sort'              => 'ASC',
     488                        'fetch_data'        => false,
     489                        'meta_query'        => false,
     490                        'update_meta_cache' => true
     491                ), 'xprofile_get_fields' );
    460492
    461                 $wpdb->query( $sql );
     493                // Get the profile fields table name
     494                $table_name = buddypress()->profile->table_name_fields;
     495
     496                // METADATA
     497                $meta_query_sql = self::get_meta_query_sql( $r['meta_query'] );
     498
     499                // SELECT
     500                $select_sql = "SELECT DISTINCT f.id";
     501
     502                // FROM
     503                $from_sql   = " FROM {$table_name} f";
     504
     505                // JOIN
     506                $join_sql   = $meta_query_sql['join'];
     507
     508                // WHERE
     509                $where_sql  = self::get_where_sql( $r, $select_sql, $from_sql, $join_sql, $meta_query_sql );
     510
     511                // Bail if no `WHERE` conditions
     512                if ( empty( $where_sql ) ) {
     513                        return array();
     514                }
     515
     516                /** Order & Sort ******************************************************/
     517
     518                // Sorting
     519                $sort = $r['sort'];
     520                if ( ! in_array( $sort, array( 'ASC', 'DESC' ) ) ) {
     521                        $sort = 'ASC';
     522                }
     523
     524                // Ordering
     525                $order_by = 'f.field_order';
     526                if ( ! empty( $r['order_by'] ) ) {
     527                        $order_by = $r['order_by'];
     528                }
     529
     530                /** Index *************************************************************/
     531
     532                // Get the query index, if used
     533                $index_hint_sql = self::get_index_hint_sql( $where_sql );
     534
     535                /** Query *************************************************************/
     536
     537                // Query first for profile fields
     538                $field_ids_sql = "{$select_sql} {$from_sql} {$index_hint_sql} {$join_sql} {$where_sql} ORDER BY {$order_by} {$sort}";
     539                $field_ids     = $wpdb->get_col( $field_ids_sql );
     540
     541                // Bail if no field IDs
     542                if ( empty( $field_ids ) ) {
     543                        return array();
     544                }
     545
     546                // Get field data for all field IDs - handles cache analysis
     547                $fields = self::get_fields_data( $field_ids, $r['fetch_data'] );
     548
     549                // Maybe fetch user data for all queried fields
     550                if ( ! empty( $r['fetch_data'] ) ) {
     551                        $fields = self::get_data_for_field_ids( $field_ids, $r['fetch_data'] );
     552                }
     553
     554                // Update caches
     555                if ( ! empty( $r['update_meta_cache'] ) ) {
     556                        bp_xprofile_update_meta_cache( array( 'field' => $field_ids ) );
     557                }
     558
     559                return $fields;
    462560        }
    463561
    464         /** Static Methods ********************************************************/
     562        /**
     563         * Delete profile fields, as specified by parameters
     564         *
     565         * @see BP_Activity_Activity::get_filter_sql() for a description of the
     566         *      'filter' parameter.
     567         * @see WP_Meta_Query::queries for a description of the 'meta_query'
     568         *      parameter format.
     569         *
     570         * @param array $args {
     571         *     An array of arguments. All items are optional.
     572         *
     573         * }
     574         * @return array The array returned has two keys:
     575         *     - 'total' is the count of located activities
     576         *     - 'activities' is an array of the located activities
     577         */
     578        public static function delete_fields( $args = '' ) {
     579                global $wpdb;
    465580
    466         public static function get_type( $field_id = 0 ) {
     581                $r = wp_parse_args( $args, array(
     582                        'include'           => false,
     583                        'exclude'           => false,
     584                        'group_id'          => false,
     585                        'group_not_in'      => false,
     586                        'parent_id'         => false,
     587                        'parent_not_in'     => false,
     588                        'type_in'           => false,
     589                        'type_not_in'       => false,
     590                        'name'              => '',
     591                        'is_required'       => false,
     592                        'is_default_option' => false,
     593                        'delete_data'       => false,
     594                        'meta_query'        => false,
     595                        'update_meta_cache' => true
     596                ) );
     597
     598                // Get the profile fields table name
     599                $table_name = buddypress()->profile->table_name_fields;
     600
     601                // METADATA
     602                $meta_query_sql = self::get_meta_query_sql( $r['meta_query'] );
     603
     604                // SELECT
     605                $select_sql = "SELECT *";
     606
     607                // FROM
     608                $from_sql   = " FROM {$table_name} f";
     609
     610                // JOIN
     611                $join_sql   = $meta_query_sql['join'];
     612
     613                // WHERE
     614                $where_sql  = self::get_where_sql( $r, $select_sql, $from_sql, $join_sql, $meta_query_sql );
     615
     616                // Bail if no `WHERE` conditions
     617                if ( empty( $where_sql ) ) {
     618                        return false;
     619                }
     620
     621                // Fetch all activities being deleted so we can perform more actions
     622                $fields = $wpdb->get_results( "SELECT {$from_sql} {$join_sql} {$where_sql}" );
     623
     624                /**
     625                 * Action to allow intercepting xprofile fields to be deleted
     626                 *
     627                 * @since BuddyPress (2.3.0)
     628                 *
     629                 * @param array $activities Array of fields
     630                 * @param array $r          Array of parsed arguments
     631                 */
     632                do_action_ref_array( 'bp_xprofile_fields_before_delete', array( $fields, $r ) );
     633
     634                /** Query *************************************************************/
     635
     636                // Query first for profile fields
     637                // Attempt to delete activities from the database
     638                $delete_ids_sql = "DELETE {$from_sql} {$join_sql} {$where_sql}";
     639                $deleted = $wpdb->query( $delete_ids_sql );
     640
     641                // Bail if nothing was deleted
     642                if ( empty( $deleted ) ) {
     643                        return false;
     644                }
     645
     646                /**
     647                 * Action to allow intercepting profile fields just deleted
     648                 *
     649                 * @since BuddyPress (2.3.0)
     650                 *
     651                 * @param array $activities Array of fields
     652                 * @param array $r          Array of parsed arguments
     653                 */
     654                do_action_ref_array( 'bp_xprofile_fields_after_delete', array( $fields, $r ) );
     655
     656                // Pluck the activity IDs out of the $activities array
     657                $field_ids = wp_parse_id_list( wp_list_pluck( $fields, 'id' ) );
     658
     659                // Handle accompanying field options and meta deletion
     660                if ( ! empty( $field_ids ) ) {
     661
     662                        // Delete all profile field meta entries
     663                        //BP_XProfile_Field::delete_field_meta_entries( $field_ids );
     664
     665                        // Setup empty array for comments
     666                        $option_ids = array();
     667
     668                        // Loop through activity ids and attempt to delete comments
     669                        foreach ( $field_ids as $field_id ) {
     670
     671                                // Attempt to delete comments
     672                                $option_ids = BP_XProfile_Field::delete_fields( array(
     673                                        'parent_id'   => $field_id,
     674                                        'delete_data' => $r['delete_data']
     675                                ) );
     676
     677                                // Merge IDs together
     678                                if ( ! empty( $option_ids ) ) {
     679                                        $option_ids = array_merge( $field_ids, $option_ids );
     680                                }
     681                        }
     682
     683                        // Merge activity IDs with any deleted comment IDs
     684                        if ( ! empty( $option_ids ) ) {
     685                                $field_ids = array_unique( array_merge( $field_ids, $option_ids ) );
     686                        }
     687                }
     688
     689                // Maybe fetch user data for all queried fields
     690                if ( ! empty( $r['delete_data'] ) ) {
     691                        //$fields = self::delete_data_for_field_ids( $field_ids );
     692                }
     693
     694                // Update caches
     695                if ( ! empty( $r['update_meta_cache'] ) ) {
     696                        bp_xprofile_update_meta_cache( array( 'field' => $field_ids ) );
     697                }
     698
     699                return true;
     700        }
     701
     702        /**
     703         * Get the `WHERE` part of the MySQL query for profile fields
     704         *
     705         * @since BuddyPress (2.3.0)
     706         *
     707         * @global object $wpdb
     708         * @param  array  $r
     709         * @param  string $select_sql
     710         * @param  string $from_sql
     711         * @param  string $join_sql
     712         * @param  string $meta_query_sql
     713         *
     714         * @return mixed
     715         */
     716        private static function get_where_sql( $r = array(), $select_sql = '', $from_sql = '', $join_sql = '', $meta_query_sql = '' ) {
    467717                global $wpdb;
    468718
     719                // Setup empty array for `WHERE` conditions
     720                $where_conditions = array();
     721
     722                /** Field ID **********************************************************/
     723
     724                // Setup IN query for field IDs
     725                if ( ! empty( $r['include'] ) ) {
     726                        $include_in                  = implode( ',', wp_parse_id_list( $r['include'] ) );
     727                        $where_conditions['include'] = "id IN ({$include_in})";
     728                }
     729
     730                // Setup NOT IN query for field IDs
     731                if ( ! empty( $r['exclude'] ) ) {
     732                        $exclude_not_in              = implode( ',', wp_parse_id_list( $r['exclude'] ) );
     733                        $where_conditions['exclude'] = "id NOT IN ({$exclude_not_in})";
     734                }
     735
     736                /** Group ID **********************************************************/
     737
     738                // Setup IN query for field-group IDs
     739                if ( ! empty( $r['group_id'] ) ) {
     740                        $group_in                     = implode( ',', wp_parse_id_list( $r['group_id'] ) );
     741                        $where_conditions['group_in'] = "group_id IN ({$group_in})";
     742                }
     743
     744                // Setup NOT IN query for field-group IDs
     745                if ( ! empty( $r['group_not_in'] ) ) {
     746                        $group_not_in                     = implode( ',', wp_parse_id_list( $r['group_not_in'] ) );
     747                        $where_conditions['group_not_in'] = "group_id NOT IN ({$group_not_in})";
     748                }
     749
     750                /** Parent ID *********************************************************/
     751
     752                // Setup IN query for parent-field IDs
     753                if ( ! empty( $r['parent_id'] ) || ( '0' === $r['parent_id'] ) ) {
     754                        $parent_in                     = implode( ',', wp_parse_id_list( $r['parent_id'] ) );
     755                        $where_conditions['parent_in'] = "parent_id IN ({$parent_in})";
     756                }
     757
     758                // Setup NOT IN query for parent-field IDs
     759                if ( ! empty( $r['parent_not_in'] ) ) {
     760                        $parent_not_in                     = implode( ',', wp_parse_id_list( $r['parent_not_in'] ) );
     761                        $where_conditions['parent_not_in'] = "parent_id NOT IN ({$parent_not_in})";
     762                }
     763
     764                /** Types *************************************************************/
     765
     766                // Setup IN query for type-field IDs
     767                if ( ! empty( $r['type_in'] ) ) {
     768                        $type_in                     = implode( ',', wp_parse_id_list( $r['type_in'] ) );
     769                        $where_conditions['type_in'] = "type IN ({$type_in})";
     770                }
     771
     772                // Setup NOT IN query for type-field IDs
     773                if ( ! empty( $r['type_not_in'] ) ) {
     774                        $type_not_in                     = implode( ',', wp_parse_id_list( $r['type_not_in'] ) );
     775                        $where_conditions['type_not_in'] = "type NOT IN ({$type_not_in})";
     776                }
     777
     778                /** Name **************************************************************/
     779
     780                // Setup IN query for type-field IDs
     781                if ( ! empty( $r['name'] ) ) {
     782                        $where_conditions['name'] = $wpdb->prepare( "name = %s", $r['name'] );
     783                }
     784
     785                /** Meta **************************************************************/
     786
     787                // meta query
     788                if ( ! empty( $meta_query_sql['where'] ) ) {
     789                        $where_conditions['meta_query'] = $meta_query_sql['where'];
     790                }
     791
     792                /**
     793                 * Filters the MySQL WHERE conditions for the XProfile field get method.
     794                 *
     795                 * @since BuddyPress (2.3.0)
     796                 *
     797                 * @param array  $where_conditions Current conditions for MySQL WHERE statement.
     798                 * @param array  $r                Parsed arguments passed into method.
     799                 * @param string $select_sql       Current SELECT MySQL statement at point of execution.
     800                 * @param string $from_sql         Current FROM MySQL statement at point of execution.
     801                 * @param string $join_sql         Current INNER JOIN MySQL statement at point of execution.
     802                 */
     803                $where_conditions = apply_filters( 'bp_xprofile_get_where_conditions', $where_conditions, $r, $select_sql, $from_sql, $join_sql, $meta_query_sql );
     804
     805                // Return known WHERE conditions
     806                if ( ! empty( $where_conditions ) ) {
     807                        return 'WHERE ' . join( ' AND ', $where_conditions );
     808                }
     809
     810                // No where conditions
     811                return false;
     812        }
     813
     814        /**
     815         * Get the `USE INDEX` part of the profile field MySQL query
     816         *
     817         * @since BuddyPress (2.3.0)
     818         *
     819         * @return string
     820         */
     821        private static function get_index_hint_sql( $where_sql = '' ) {
     822
     823                /**
     824                 * Filters the preferred order of indexes for xprofile fields.
     825                 *
     826                 * @since BuddyPress (2.3.0)
     827                 *
     828                 * @param array Array of indexes in preferred order.
     829                 */
     830                $indexes = apply_filters( 'bp_xprofile_preferred_index_order', array( 'group_id', 'parent_id', 'field_order', 'can_delete', 'is_required' ) );
     831
     832                // Loop through possible indices and pick the first one. Not a very
     833                // precise indication, but currently does the job okay.
     834                foreach ( $indexes as $index ) {
     835                        if ( false !== strpos( $where_sql, $index ) ) {
     836                                $the_index = $index;
     837                                break; // Take the first one we find
     838                        }
     839                }
     840
     841                // Return known USE INDEX condition
     842                if ( ! empty( $the_index ) ) {
     843                        return "USE INDEX ({$the_index})";
     844                }
     845
     846                // No USE INDEX condition
     847                return '';
     848        }
     849
     850        /**
     851         * Get the SQL for the 'meta_query' param in BP_XProfile_Field::get_fields().
     852         *
     853         * We use WP_Meta_Query to do the heavy lifting of parsing the
     854         * meta_query array and creating the necessary SQL clauses. However,
     855         * since BP_XProfile_Field::get_fields() builds its SQL differently than
     856         * WP_Query, we have to alter the return value (stripping the leading
     857         * AND keyword from the 'where' clause).
     858         *
     859         * @since BuddyPress (2.3.0)
     860         *
     861         * @param array $meta_query An array of meta_query filters. See the
     862         *                          documentation for WP_Meta_Query for details.
     863         *
     864         * @return array $sql_array 'join' and 'where' clauses.
     865         */
     866        private static function get_meta_query_sql( $meta_query = array() ) {
     867                global $wpdb;
     868
     869                // Default meta sql
     870                $sql_array = array(
     871                        'join'  => '',
     872                        'where' => '',
     873                );
     874
     875                // Attempt to query using metadata
     876                if ( ! empty( $meta_query ) ) {
     877                        $fields_meta_query = new BP_XProfile_Meta_Query( $meta_query );
     878
     879                        // WP_Meta_Query expects the table name at $wpdb->xprofile_fieldmeta
     880                        $wpdb->xprofile_fieldmeta = buddypress()->profile->table_name_meta;
     881
     882                        // Attempt to get meta part of query
     883                        $meta_sql = $fields_meta_query->get_sql( 'xprofile_field', 'f', 'id' );
     884
     885                        // Strip the leading AND - handled it in get_fields()
     886                        $sql_array['where'] = preg_replace( '/^\sAND/', '', $meta_sql['where'] );
     887                        $sql_array['join']  = $meta_sql['join'];
     888                }
     889
     890                return $sql_array;
     891        }
     892
     893        /**
     894         * Get field-data for select field IDs for a specific user ID
     895         *
     896         * @since BuddyPress (2.3.0)
     897         *
     898         * @param array $field_ids
     899         * @param int   $user_id
     900         *
     901         * @return array
     902         */
     903        private static function get_data_for_field_ids( $field_ids = array(), $user_id = 0 ) {
     904
     905                // Setup the fields array
     906                $fields = array();
     907
     908                // Bail if no field IDs or user ID
     909                if ( empty( $field_ids ) || empty( $user_id ) ) {
     910                        return $fields;
     911                }
     912
     913                // Loop through fields, and get data for the user ID
     914                foreach ( $field_ids as $field_id ) {
     915                        $field       = new BP_XProfile_Field( $field_id );
     916                        $field->data = $field->get_field_data( $user_id );
     917                        $fields[]    = $field;
     918                }
     919
     920                // Return the fields and their data for the user ID
     921                return $fields;
     922        }
     923
     924        /**
     925         * Convert field IDs to field objects, as expected in template loop.
     926         *
     927         * @since BuddyPress (2.3.0)
     928         *
     929         * @param  array $field_ids Array of field IDs
     930         *
     931         * @return array
     932         */
     933        private static function get_fields_data( $field_ids = array() ) {
     934                global $wpdb;
     935
     936                // Declare empty fields array
     937                $fields = array();
     938
     939                // Bail if no field ID's passed
     940                if ( empty( $field_ids ) ) {
     941                        return $fields;
     942                }
     943
     944                // Determine if any queried field ID's are uncached
     945                $uncached_ids = bp_get_non_cached_ids( $field_ids, 'bp_xprofile_fields' );
     946
     947                // Prime caches as necessary
     948                if ( ! empty( $uncached_ids ) ) {
     949
     950                        // Format the field ID's for use in the query below
     951                        $uncached_ids_sql = implode( ',', wp_parse_id_list( $uncached_ids ) );
     952
     953                        // Fetch data from field table, preserving order
     954                        $table_name    = buddypress()->profile->table_name_fields;
     955                        $sql           = "SELECT * FROM {$table_name} WHERE id IN ({$uncached_ids_sql})";
     956                        $queried_fdata = $wpdb->get_results( $sql );
     957
     958                        // Cache the queried results
     959                        foreach ( (array) $queried_fdata as $fdata ) {
     960                                wp_cache_set( $fdata->id, $fdata, 'bp_xprofile_fields' );
     961                        }
     962                }
     963
     964                // Get all field data from the (now primed) cache
     965                foreach ( $field_ids as $field_id ) {
     966                        $fields[] = wp_cache_get( $field_id, 'bp_xprofile_fields' );
     967                }
     968
     969                // Return newly cached field IDs
     970                return $fields;
     971        }
     972
     973        /**
     974         * Get the type for a given field ID
     975         *
     976         * @since BuddyPress (1.1.0)
     977         *
     978         * @param  int $field_id
     979         *
     980         * @return boolean
     981         */
     982        public static function get_type( $field_id = 0 ) {
     983
    469984                // Bail if no field ID
    470985                if ( empty( $field_id ) ) {
    471986                        return false;
    472987                }
    473988
    474                 $bp   = buddypress();
    475                 $sql  = $wpdb->prepare( "SELECT type FROM {$bp->profile->table_name_fields} WHERE id = %d", $field_id );
    476                 $type = $wpdb->get_var( $sql );
     989                // Attempt to get the field
     990                $field = new BP_XProfile_Field( $field_id );
    477991
    478                 // Return field type
    479                 if ( ! empty( $type ) ) {
    480                         return $type;
     992                // Bail if no field found
     993                if ( empty( $field->type ) ) {
     994                        return false;
    481995                }
    482996
    483                 return false;
     997                return $field->type;
    484998        }
    485999
    4861000        /**
     
    4951009         * @return boolean
    4961010         */
    4971011        public static function delete_for_group( $group_id = 0 ) {
    498                 global $wpdb;
    4991012
    5001013                // Bail if no group ID
    5011014                if ( empty( $group_id ) ) {
    5021015                        return false;
    5031016                }
    5041017
    505                 $bp      = buddypress();
    506                 $sql     = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE group_id = %d", $group_id );
    507                 $deleted = $wpdb->get_var( $sql );
    508 
     1018                // Attempt to delete fields
     1019                $deleted = self::delete_fields( array(
     1020                        'group_id' => $group_id
     1021                ) );
     1022               
    5091023                // Return true if fields were deleted
    5101024                if ( false !== $deleted ) {
    5111025                        return true;
     
    5191033         *
    5201034         * @since BuddyPress (1.5.0)
    5211035         *
    522          * @global object $wpdb
    523          * @param  string $field_name
     1036         * @param string $field_name
    5241037         *
    5251038         * @return boolean
    5261039         */
    5271040        public static function get_id_from_name( $field_name = '' ) {
    528                 global $wpdb;
    5291041
    530                 $bp = buddypress();
     1042                // Bail if no field ID
     1043                if ( empty( $field_name ) ) {
     1044                        return false;
     1045                }
    5311046
    532                 if ( empty( $bp->profile->table_name_fields ) || empty( $field_name ) ) {
     1047                // Attempt to get the field
     1048                $fields = self::get_fields( array(
     1049                        'name'      => $field_name,
     1050                        'parent_id' => '0'
     1051                ) );
     1052
     1053                // Return false if not found
     1054                if ( empty( $fields ) ) {
    5331055                        return false;
    5341056                }
    5351057
    536                 $sql = $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s AND parent_id = 0", $field_name );
     1058                // Take the first field
     1059                $field = reset( $fields );
    5371060
    538                 return $wpdb->get_var( $sql );
     1061                // Return field ID if found
     1062                return $field->id;
    5391063        }
    5401064
    5411065        /**
     
    5431067         *
    5441068         * @since BuddyPress (1.5.0)
    5451069         *
    546          * @global object $wpdb
    547          *
    5481070         * @param  int $field_id
    5491071         * @param  int $position
    5501072         * @param  int $field_group_id
     
    5521074         * @return boolean
    5531075         */
    5541076        public static function update_position( $field_id, $position = null, $field_group_id = null ) {
    555                 global $wpdb;
    5561077
    5571078                // Bail if invalid position or field group
    5581079                if ( ! is_numeric( $position ) || ! is_numeric( $field_group_id ) ) {
     
    5601081                }
    5611082
    5621083                // Get table name and field parent
    563                 $table_name = buddypress()->profile->table_name_fields;
    564                 $sql        = $wpdb->prepare( "UPDATE {$table_name} SET field_order = %d, group_id = %d WHERE id = %d", $position, $field_group_id, $field_id );
    565                 $parent     = $wpdb->query( $sql );
     1084                $field              = new BP_XProfile_Field( $field_id );
     1085                $field->field_order = $position;
     1086                $field->group_id    = $field_group_id;
    5661087
    567                 // Update $field_id with new $position and $field_group_id
    568                 if ( ! empty( $parent ) && ! is_wp_error( $parent ) ) {
     1088                // Bail if field did not save - pass `true` to prevent option deletion
     1089                if ( ! $field->save() ) {
     1090                        return false;
     1091                }
    5691092
    570                         // Update any children of this $field_id
    571                         $sql = $wpdb->prepare( "UPDATE {$table_name} SET group_id = %d WHERE parent_id = %d", $field_group_id, $field_id );
    572                         $wpdb->query( $sql );
     1093                // Regather the field and its children
     1094                $field    = new BP_XProfile_Field( $field_id );
     1095                $children = $field->get_children();
    5731096
    574                         return $parent;
     1097                // If child fields exist, update them
     1098                if ( ! empty( $children ) ) {
     1099
     1100                        // Loop through children and update field group ID
     1101                        foreach ( $children as $option_field ) {
     1102                                $option           = new BP_XProfile_Field( $option_field->id );
     1103                                $option->group_id = $field->group_id;
     1104                                $option->save();
     1105                        }
    5751106                }
    5761107
    577                 return false;
     1108                return true;
    5781109        }
    5791110
    5801111        /**
     
    6071138        }
    6081139
    6091140        /**
    610          * This function populates the items for radio buttons checkboxes and drop
    611          * down boxes.
     1141         * Check if a field option has a default value
     1142         *
     1143         * This utility function is used by `update_field_options` and checks if a
     1144         * field-option should be checked or selected by default. Because different
     1145         * field types $_POST different results, we check for both array and integer
     1146         * values and compare between the two.
     1147         *
     1148         * @since BuddyPress (2.3.0)
     1149         *
     1150         * @param  mixed $defaults
     1151         * @param  mixed $option_key
     1152         *
     1153         * @return int
    6121154         */
    613         public function render_admin_form_children() {
    614                 foreach ( array_keys( bp_xprofile_get_field_types() ) as $field_type ) {
    615                         $type_obj = bp_xprofile_create_field_type( $field_type );
    616                         $type_obj->admin_new_field_html( $this );
     1155        private static function is_default_field_option( $defaults = '', $option_key = '' ) {
     1156                $is_default = 0;
     1157
     1158                // Check for multiple default values
     1159                if ( is_array( $defaults ) && isset( $defaults[ $option_key ] ) ) {
     1160                        $is_default = 1;
     1161
     1162                // Check for single default value
     1163                } elseif ( $defaults === $option_key ) {
     1164                        $is_default = 1;
    6171165                }
     1166
     1167                // Cast as int and return the result
     1168                return (int) $is_default;
    6181169        }
    6191170
     1171        /** Public Methods ********************************************************/
     1172
    6201173        /**
    6211174         * Oupput the admin form for this field
    6221175         *
     
    6251178         * @param type $message
    6261179         */
    6271180        public function render_admin_form( $message = '' ) {
     1181
     1182                // Adding a new field
    6281183                if ( empty( $this->id ) ) {
    6291184                        $title  = __( 'Add New Field', 'buddypress' );
    6301185                        $action = "users.php?page=bp-profile-setup&group_id=" . $this->group_id . "&mode=add_field#tabs-" . $this->group_id;
     
    6351190                                $this->description = $_POST['description'];
    6361191                                $this->is_required = $_POST['required'];
    6371192                                $this->type        = $_POST['fieldtype'];
    638                                 $this->order_by    = $_POST["sort_order_{$this->type}"];
     1193                                $this->order_by    = ! empty( $_POST["sort_order_{$this->type}"] ) ? $_POST["sort_order_{$this->type}"] : '';
    6391194                                $this->field_order = $_POST['field_order'];
    6401195                        }
     1196
     1197                // Editing an existing field
    6411198                } else {
    6421199                        $title  = __( 'Edit Field', 'buddypress' );
    6431200                        $action = "users.php?page=bp-profile-setup&mode=edit_field&group_id=" . $this->group_id . "&field_id=" . $this->id . "#tabs-" . $this->group_id;
     
    6801237                                                        // Output the required metabox
    6811238                                                        $this->required_metabox();
    6821239
    683                                                         // Output the field visibility metaboxes
     1240                                                        // Output the field visibility metabox
    6841241                                                        $this->visibility_metabox();
    6851242
     1243                                                        // Output the signup metabox
     1244                                                        $this->signup_metabox();
     1245
    6861246                                                        /**
    6871247                                                         * Fires after XProfile Field sidebar metabox.
    6881248                                                         *
     
    7311291        <?php
    7321292        }
    7331293
     1294        /** Private Methods *******************************************************/
     1295
    7341296        /**
    7351297         * Private method used to display the submit metabox
    7361298         *
     
    9021464        }
    9031465
    9041466        /**
     1467         * Output the metabox for enabling this field to appear on user registration
     1468         *
     1469         * @since BuddyPress (2.3.0)
     1470         */
     1471        private function signup_metabox() {
     1472        ?>
     1473
     1474                <div class="postbox">
     1475                        <h3><label for="signup-field"><?php _e( 'Sign Ups', 'buddypress' ); ?></label></h3>
     1476                        <div class="inside">
     1477                                <ul>
     1478                                        <li>
     1479                                                <input type="checkbox" id="signup-position" name="signup-position" value="1" <?php checked( $this->signup_position, ! null ); ?> />
     1480                                                <label for="signup-position"><?php esc_html_e( 'Display on Registration', 'buddypress' ); ?></label>
     1481                                        </li>
     1482                                </ul>
     1483                        </div>
     1484                </div>
     1485
     1486        <?php
     1487        }
     1488
     1489        /**
    9051490         * Output the metabox for setting what type of field this is
    9061491         *
    9071492         * @since BuddyPress (2.3.0)
     
    9381523        }
    9391524
    9401525        /**
     1526         * This function populates the items for radio buttons checkboxes and drop
     1527         * down boxes.
     1528         *
     1529         * @since BuddyPress (1.1.0)
     1530         */
     1531        private function render_admin_form_children() {
     1532                foreach ( array_keys( bp_xprofile_get_field_types() ) as $field_type ) {
     1533                        $type_obj = bp_xprofile_create_field_type( $field_type );
     1534                        $type_obj->admin_new_field_html( $this );
     1535                }
     1536        }
     1537
     1538        /**
    9411539         * Output hidden fields used by default field
    9421540         *
    9431541         * @since BuddyPress (2.3.0)
     
    9751573                // Compare & return
    9761574                return (bool) ( 1 === (int) $field_id );
    9771575        }
     1576
     1577        /**
     1578         * Purge caches relevant to field updates
     1579         *
     1580         * @since BuddyPress (2.3.0)
     1581         */
     1582        private function purge_caches() {
     1583
     1584                // Delete the cached value for this field
     1585                wp_cache_delete( $this->id, 'bp_xprofile_fields' );
     1586
     1587                // Bust cache of parent field
     1588                if ( ! empty( $this->parent_id ) ) {
     1589                        wp_cache_delete( $this->parent_id, 'bp_xprofile_fields' );
     1590                }
     1591
     1592                // Bust cache of parent group
     1593                if ( ! empty( $this->group_id ) ) {
     1594                        wp_cache_delete( 'all',           'bp_xprofile_groups' );
     1595                        wp_cache_delete( $this->group_id, 'bp_xprofile_groups' );
     1596                }
     1597        }
     1598
     1599        /**
     1600         * Update fields-options when a field is saved
     1601         *
     1602         * If a field type supports field-options, BuddyPress will resave all of
     1603         * them to ensure the integrity of their data. This is necessary and
     1604         * possible because field-data for each user is stored keyed to the parent
     1605         * field ID, and not to the child field-option ID.
     1606         *
     1607         * @since BuddyPress (2.3.0)
     1608         *
     1609         * @param type $updating_position
     1610         */
     1611        private function update_field_options() {
     1612
     1613                /**
     1614                 * Check to see if this is a field with child options, and that field
     1615                 * options are being passed in the $_POST request.
     1616                 */
     1617                if ( ! empty( $this->type_obj->supports_options ) && ( isset( $_POST["{$this->type}_option"] ) || isset( $_POST["isDefault_{$this->type}_option"] ) ) ) {
     1618
     1619                        /**
     1620                         * Remove any field-options for this field. They will be re-added if
     1621                         * needed. This prenets orphaned options if the user changes a field
     1622                         * from a radio-button with options to a textbox without.
     1623                         *
     1624                         * It's maybe a bit of a brute-force approach, but seems easier than
     1625                         * querying for items & updating/deleting/creating each time;
     1626                         * and 60% of the time, it works everytime, so that's nice.
     1627                         */
     1628                        $this->delete_children();
     1629
     1630                        // Allow plugins to filter the field's child options (i.e. the items in a selectbox).
     1631                        $post_options  = ! empty( $_POST["{$this->type}_option"]           ) ? $_POST["{$this->type}_option"]           : '';
     1632                        $post_defaults = ! empty( $_POST["isDefault_{$this->type}_option"] ) ? $_POST["isDefault_{$this->type}_option"] : '';
     1633
     1634                        /**
     1635                         * Filters the submitted field option value before saved.
     1636                         *
     1637                         * @since BuddyPress (1.5.0)
     1638                         *
     1639                         * @param string            $post_options Submitted option value.
     1640                         * @param BP_XProfile_Field $type         Current field type being saved for.
     1641                         */
     1642                        $options = apply_filters( 'xprofile_field_options_before_save', $post_options,  $this->type );
     1643
     1644                        /**
     1645                         * Filters the default field option value before saved.
     1646                         *
     1647                         * @since BuddyPress (1.5.0)
     1648                         *
     1649                         * @param string            $post_defaults Default option value.
     1650                         * @param BP_XProfile_Field $type          Current field type being saved for.
     1651                         */
     1652                        $defaults = apply_filters( 'xprofile_field_default_before_save', $post_defaults, $this->type );
     1653
     1654                        // There are options that need recreating
     1655                        if ( ! empty( $options ) ) {
     1656
     1657                                // Start counter at 1 to avoid 0 values
     1658                                $counter = 1;
     1659
     1660                                // Loop through options and re-create them
     1661                                foreach ( $options as $option_key => $option_name ) {
     1662
     1663                                        // Is this field a default option?
     1664                                        $is_default = self::is_default_field_option( $defaults, $option_key );
     1665
     1666                                        // Recreate the option based on value and other criteria
     1667                                        if ( ! empty( $option_name ) ) {
     1668                                                $option                    = new BP_XProfile_Field();
     1669                                                $option->group_id          = $this->group_id;
     1670                                                $option->parent_id         = $this->id;
     1671                                                $option->type              = 'option';
     1672                                                $option->name              = $option_name;
     1673                                                $option->description       = '';
     1674                                                $option->is_required       = 0;
     1675                                                $option->is_default_option = $is_default;
     1676                                                $option->option_order      = $counter;
     1677                                                $option->save();
     1678                                        }
     1679
     1680                                        $counter++;
     1681                                }
     1682                        }
     1683                }
     1684        }
    9781685}
  • src/bp-xprofile/classes/class-bp-xprofile-group.php

     
    301301                        return $groups;
    302302                }
    303303
     304                /** Fields ************************************************************/
     305
    304306                // Get the group ids from the groups we found
    305307                $group_ids = wp_list_pluck( $groups, 'id' );
    306308
     
    312314                        return $groups;
    313315                }
    314316
    315                 // Setup IN query from group IDs
    316                 $group_ids_in = implode( ',', (array) $group_ids );
    317 
    318317                // Support arrays and comma-separated strings
    319318                $exclude_fields_cs = wp_parse_id_list( $r['exclude_fields'] );
    320319
    321320                // Visibility - Handled here so as not to be overridden by sloppy use of the
    322321                // exclude_fields parameter. See bp_xprofile_get_hidden_fields_for_user()
    323322                $hidden_user_fields = bp_xprofile_get_hidden_fields_for_user( $r['user_id'] );
    324                 $exclude_fields_cs  = array_merge( $exclude_fields_cs, $hidden_user_fields );
    325                 $exclude_fields_cs  = implode( ',', $exclude_fields_cs );
     323                $exclude_field_ids  = array_filter( array_merge( $exclude_fields_cs, $hidden_user_fields ) );
    326324
    327                 // Setup IN query for field IDs
    328                 if ( ! empty( $exclude_fields_cs ) ) {
    329                         $exclude_fields_sql = "AND id NOT IN ({$exclude_fields_cs})";
    330                 } else {
    331                         $exclude_fields_sql = '';
    332                 }
    333 
     325                // Setup ::get() arguments
    334326                // Fetch the fields
    335                 $fields    = $wpdb->get_results( "SELECT id, name, description, type, group_id, is_required FROM {$bp->profile->table_name_fields} WHERE group_id IN ( {$group_ids_in} ) AND parent_id = 0 {$exclude_fields_sql} ORDER BY field_order" );
     327                $fields = BP_XProfile_Field::get_fields( array(
     328                        'exclude'           => $exclude_field_ids,
     329                        'group_id'          => $group_ids,
     330                        'parent_id'         => '0',
     331                        'update_meta_cache' => false, // for now
     332                        'fetch_data'        => false  // for now
     333                ) );
     334
     335                // Pluck the ID's
    336336                $field_ids = wp_list_pluck( $fields, 'id' );
    337337
    338338                // Store field IDs for meta cache priming
     
    343343                        return $groups;
    344344                }
    345345
     346                /** Field Data ********************************************************/
     347
    346348                // Maybe fetch field data
    347349                if ( ! empty( $r['fetch_field_data'] ) ) {
    348350
     
    361363                                        $maybe_value = maybe_unserialize( $data->value );
    362364
    363365                                        // Valid field values of 0 or '0' get caught by empty(), so we have an extra check for these. See #BP5731
    364                                         if ( ( ! empty( $maybe_value ) || '0' == $maybe_value ) && false !== $key = array_search( $data->field_id, $field_ids ) ) {
     366                                        if ( ( ! empty( $maybe_value ) || '0' === $maybe_value ) && false !== $key = array_search( $data->field_id, $field_ids ) ) {
    365367
    366368                                                // Fields that have data get removed from the list
    367369                                                unset( $field_ids[ $key ] );
     
    369371                                }
    370372
    371373                                // The remaining members of $field_ids are empty. Remove them.
    372                                 foreach( $fields as $field_key => $field ) {
     374                                foreach ( $fields as $field_key => $field ) {
    373375                                        if ( in_array( $field->id, $field_ids ) ) {
    374376                                                unset( $fields[ $field_key ] );
    375377                                        }
  • src/bp-xprofile/classes/class-bp-xprofile-meta-query.php

     
     1<?php
     2
     3/**
     4 * Class for generating SQL clauses that filter a primary query according to
     5 * XProfile metadata keys and values.
     6 *
     7 * `BP_XProfile_Meta_Query` is a helper that allows primary query classes, such
     8 * as {@see WP_Query} and {@see WP_User_Query}, to filter their results by object
     9 * metadata, by generating `JOIN` and `WHERE` subclauses to be attached
     10 * to the primary SQL query string.
     11 *
     12 * @since BuddyPress (2.3.0)
     13 */
     14class BP_XProfile_Meta_Query extends WP_Meta_Query {
     15
     16        /**
     17         * Determine whether a query clause is first-order.
     18         *
     19         * A first-order meta query clause is one that has either a 'key', 'value',
     20         * or 'object' array key.
     21         *
     22         * @since BuddyPress (2.3.0)
     23         * @access protected
     24         *
     25         * @param array $query Meta query arguments.
     26         * @return bool Whether the query clause is a first-order clause.
     27         */
     28        protected function is_first_order_clause( $query ) {
     29                return isset( $query['key'] ) || isset( $query['value'] ) || isset( $query['object'] );
     30        }
     31
     32        /**
     33         * Constructs a meta query based on 'meta_*' query vars
     34         *
     35         * @since BuddyPress (2.3.0)
     36         * @access public
     37         *
     38         * @param array $qv The query variables
     39         */
     40        public function parse_query_vars( $qv ) {
     41                $meta_query = array();
     42
     43                /*
     44                 * For orderby=meta_value to work correctly, simple query needs to be
     45                 * first (so that its table join is against an unaliased meta table) and
     46                 * needs to be its own clause (so it doesn't interfere with the logic of
     47                 * the rest of the meta_query).
     48                 */
     49                $primary_meta_query = array();
     50                foreach ( array( 'key', 'compare', 'type' ) as $key ) {
     51                        if ( ! empty( $qv[ "meta_$key" ] ) ) {
     52                                $primary_meta_query[ $key ] = $qv[ "meta_$key" ];
     53                        }
     54                }
     55
     56                // WP_Query sets 'meta_value' = '' by default.
     57                if ( isset( $qv['meta_value'] ) && ( '' !== $qv['meta_value'] ) && ( ! is_array( $qv['meta_value'] ) || $qv['meta_value'] ) ) {
     58                        $primary_meta_query['value'] = $qv['meta_value'];
     59                }
     60
     61                // BP_XProfile_Query sets 'object_type' = '' by default
     62                if ( isset( $qv[ 'object_type' ] ) && ( '' !== $qv[ 'object_type' ] ) && ( ! is_array( $qv[ 'object_type' ] ) || $qv[ 'object_type' ] ) ) {
     63                        $meta_query[0]['object'] = $qv[ 'object_type' ];
     64                }
     65
     66                $existing_meta_query = isset( $qv['meta_query'] ) && is_array( $qv['meta_query'] ) ? $qv['meta_query'] : array();
     67
     68                if ( ! empty( $primary_meta_query ) && ! empty( $existing_meta_query ) ) {
     69                        $meta_query = array(
     70                                'relation' => 'AND',
     71                                $primary_meta_query,
     72                                $existing_meta_query,
     73                        );
     74                } elseif ( ! empty( $primary_meta_query ) ) {
     75                        $meta_query = array(
     76                                $primary_meta_query,
     77                        );
     78                } elseif ( ! empty( $existing_meta_query ) ) {
     79                        $meta_query = $existing_meta_query;
     80                }
     81
     82                $this->__construct( $meta_query );
     83        }
     84
     85        /**
     86         * Generates SQL clauses to be appended to a main query.
     87         *
     88         * @since BuddyPress (2.3.0)
     89         * @access public
     90         *
     91         * @param string $type              Type of meta, eg 'user', 'post'.
     92         * @param string $primary_table     Database table where the object being filtered is stored (eg wp_users).
     93         * @param string $primary_id_column ID column for the filtered object in $primary_table.
     94         * @param object $context           Optional. The main query object.
     95         * @return array {
     96         *     Array containing JOIN and WHERE SQL clauses to append to the main query.
     97         *
     98         *     @type string $join  SQL fragment to append to the main JOIN clause.
     99         *     @type string $where SQL fragment to append to the main WHERE clause.
     100         * }
     101         */
     102        public function get_sql( $type, $primary_table, $primary_id_column, $context = null ) {
     103                if ( ! $meta_table = _get_meta_table( $type ) ) {
     104                        return false;
     105                }
     106
     107                $this->meta_table     = $meta_table;
     108                $this->meta_id_column = 'object_id';
     109
     110                $this->primary_table     = $primary_table;
     111                $this->primary_id_column = $primary_id_column;
     112
     113                $sql = $this->get_sql_clauses();
     114
     115                /*
     116                 * If any JOINs are LEFT JOINs (as in the case of NOT EXISTS), then all JOINs should
     117                 * be LEFT. Otherwise posts with no metadata will be excluded from results.
     118                 */
     119                if ( false !== strpos( $sql['join'], 'LEFT JOIN' ) ) {
     120                        $sql['join'] = str_replace( 'INNER JOIN', 'LEFT JOIN', $sql['join'] );
     121                }
     122
     123                /**
     124                 * Filter the meta query's generated SQL.
     125                 *
     126                 * @since BuddyPress (2.3.0)
     127                 *
     128                 * @param array $args {
     129                 *     An array of meta query SQL arguments.
     130                 *
     131                 *     @type array  $clauses           Array containing the query's JOIN and WHERE clauses.
     132                 *     @type array  $queries           Array of meta queries.
     133                 *     @type string $type              Type of meta.
     134                 *     @type string $primary_table     Primary table.
     135                 *     @type string $primary_id_column Primary column ID.
     136                 *     @type object $context           The main query object.
     137                 * }
     138                 */
     139                return apply_filters_ref_array( 'bp_xprofile_get_meta_sql', array( $sql, $this->queries, $type, $primary_table, $primary_id_column, $context ) );
     140        }
     141
     142        /**
     143         * Generate SQL JOIN and WHERE clauses for a first-order query clause.
     144         *
     145         * "First-order" means that it's an array with a 'key' or 'value'.
     146         *
     147         * @since BuddyPress (2.3.0)
     148         * @access public
     149         *
     150         * @param array  $clause       Query clause, passed by reference.
     151         * @param array  $parent_query Parent query array.
     152         * @param string $clause_key   Optional. The array key used to name the clause in the original `$meta_query`
     153         *                             parameters. If not provided, a key will be generated automatically.
     154         * @return array {
     155         *     Array containing JOIN and WHERE SQL clauses to append to a first-order query.
     156         *
     157         *     @type string $join  SQL fragment to append to the main JOIN clause.
     158         *     @type string $where SQL fragment to append to the main WHERE clause.
     159         * }
     160         */
     161        public function get_sql_for_clause( &$clause, $parent_query, $clause_key = '' ) {
     162                global $wpdb;
     163
     164                $sql_chunks = array(
     165                        'where' => array(),
     166                        'join'  => array(),
     167                );
     168
     169                if ( isset( $clause['compare'] ) ) {
     170                        $clause['compare'] = strtoupper( $clause['compare'] );
     171                } else {
     172                        $clause['compare'] = isset( $clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '=';
     173                }
     174
     175                if ( ! in_array( $clause['compare'], array(
     176                        '=', '!=', '>', '>=', '<', '<=',
     177                        'LIKE', 'NOT LIKE',
     178                        'IN', 'NOT IN',
     179                        'BETWEEN', 'NOT BETWEEN',
     180                        'EXISTS', 'NOT EXISTS',
     181                        'REGEXP', 'NOT REGEXP', 'RLIKE'
     182                ) ) ) {
     183                        $clause['compare'] = '=';
     184                }
     185
     186                $meta_compare = $clause['compare'];
     187
     188                // First build the JOIN clause, if one is required.
     189                $join = '';
     190
     191                // We prefer to avoid joins if possible. Look for an existing join compatible with this clause.
     192                $alias = $this->find_compatible_table_alias( $clause, $parent_query );
     193                if ( false === $alias ) {
     194                        $i = count( $this->table_aliases );
     195                        $alias = $i ? 'mt' . $i : $this->meta_table;
     196
     197                        // JOIN clauses for NOT EXISTS have their own syntax.
     198                        if ( 'NOT EXISTS' === $meta_compare ) {
     199                                $join .= " LEFT JOIN $this->meta_table";
     200                                $join .= $i ? " AS $alias" : '';
     201                                $join .= $wpdb->prepare( " ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column AND $alias.meta_key = %s )", $clause['key'] );
     202
     203                        // All other JOIN clauses.
     204                        } else {
     205                                $join .= " INNER JOIN $this->meta_table";
     206                                $join .= $i ? " AS $alias" : '';
     207                                $join .= " ON ( $this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column )";
     208                        }
     209
     210                        $this->table_aliases[] = $alias;
     211                        $sql_chunks['join'][]  = $join;
     212                }
     213
     214                // Save the alias to this clause, for future siblings to find.
     215                $clause['alias'] = $alias;
     216
     217                // Determine the data type.
     218                $_meta_type     = isset( $clause['type'] ) ? $clause['type'] : '';
     219                $meta_type      = $this->get_cast_for_type( $_meta_type );
     220                $clause['cast'] = $meta_type;
     221
     222                // Fallback for clause keys is the table alias.
     223                if ( ! $clause_key ) {
     224                        $clause_key = $clause['alias'];
     225                }
     226
     227                // Ensure unique clause keys, so none are overwritten.
     228                $iterator = 1;
     229                $clause_key_base = $clause_key;
     230                while ( isset( $this->clauses[ $clause_key ] ) ) {
     231                        $clause_key = $clause_key_base . '-' . $iterator;
     232                        $iterator++;
     233                }
     234
     235                // Store the clause in our flat array.
     236                $this->clauses[ $clause_key ] =& $clause;
     237
     238                // Next, build the WHERE clause.
     239
     240                // meta_key.
     241                if ( array_key_exists( 'key', $clause ) ) {
     242                        if ( 'NOT EXISTS' === $meta_compare ) {
     243                                $sql_chunks['where'][] = $alias . '.' . $this->meta_id_column . ' IS NULL';
     244                        } else {
     245                                $sql_chunks['where'][] = $wpdb->prepare( "$alias.meta_key = %s", trim( $clause['key'] ) );
     246                        }
     247                }
     248
     249                // meta_value.
     250                if ( array_key_exists( 'value', $clause ) ) {
     251                        $meta_value = $clause['value'];
     252
     253                        if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
     254                                if ( ! is_array( $meta_value ) ) {
     255                                        $meta_value = preg_split( '/[,\s]+/', $meta_value );
     256                                }
     257                        } else {
     258                                $meta_value = trim( $meta_value );
     259                        }
     260
     261                        switch ( $meta_compare ) {
     262                                case 'IN' :
     263                                case 'NOT IN' :
     264                                        $meta_compare_string = '(' . substr( str_repeat( ',%s', count( $meta_value ) ), 1 ) . ')';
     265                                        $where = $wpdb->prepare( $meta_compare_string, $meta_value );
     266                                        break;
     267
     268                                case 'BETWEEN' :
     269                                case 'NOT BETWEEN' :
     270                                        $meta_value = array_slice( $meta_value, 0, 2 );
     271                                        $where = $wpdb->prepare( '%s AND %s', $meta_value );
     272                                        break;
     273
     274                                case 'LIKE' :
     275                                case 'NOT LIKE' :
     276                                        $meta_value = '%' . $wpdb->esc_like( $meta_value ) . '%';
     277                                        $where = $wpdb->prepare( '%s', $meta_value );
     278                                        break;
     279
     280                                // EXISTS with a value is interpreted as '='.
     281                                case 'EXISTS' :
     282                                        $meta_compare = '=';
     283                                        $where = $wpdb->prepare( '%s', $meta_value );
     284                                        break;
     285
     286                                // 'value' is ignored for NOT EXISTS.
     287                                case 'NOT EXISTS' :
     288                                        $where = '';
     289                                        break;
     290
     291                                default :
     292                                        $where = $wpdb->prepare( '%s', $meta_value );
     293                                        break;
     294
     295                        }
     296
     297                        if ( $where ) {
     298                                $sql_chunks['where'][] = "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$where}";
     299                        }
     300                }
     301
     302                // object_type.
     303                if ( array_key_exists( 'object', $clause ) ) {
     304                        $object_type = $clause['object'];
     305
     306                        if ( in_array( $meta_compare, array( 'IN', 'NOT IN' ) ) ) {
     307                                if ( ! is_array( $object_type ) ) {
     308                                        $object_type = preg_split( '/[,\s]+/', $object_type );
     309                                }
     310                        } else {
     311                                $object_type = trim( $object_type );
     312                        }
     313
     314                        switch ( $meta_compare ) {
     315                                case 'IN' :
     316                                case 'NOT IN' :
     317                                        $meta_compare_string = '(' . substr( str_repeat( ',%s', count( $object_type ) ), 1 ) . ')';
     318                                        $object_where        = $wpdb->prepare( $meta_compare_string, $object_type );
     319                                        break;
     320
     321                                case 'LIKE' :
     322                                case 'NOT LIKE' :
     323                                        $object_type  = '%' . $wpdb->esc_like( $object_type ) . '%';
     324                                        $object_where = $wpdb->prepare( '%s', $object_type );
     325                                        break;
     326
     327                                // EXISTS with a value is interpreted as '='.
     328                                case 'EXISTS' :
     329                                        $meta_compare = '=';
     330                                        $object_where = $wpdb->prepare( '%s', $object_type );
     331                                        break;
     332
     333                                // 'value' is ignored for NOT EXISTS.
     334                                case 'NOT EXISTS' :
     335                                        $object_where = '';
     336                                        break;
     337
     338                                default :
     339                                        $object_where = $wpdb->prepare( '%s', $object_type );
     340                                        break;
     341                        }
     342
     343                        if ( ! empty( $object_where ) ) {
     344                                $sql_chunks['where'][] = "{$alias}.object_type {$meta_compare} {$object_where}";
     345                        }
     346                }
     347
     348                /*
     349                 * Multiple WHERE clauses (for meta_key and meta_value) should
     350                 * be joined in parentheses.
     351                 */
     352                if ( 1 < count( $sql_chunks['where'] ) ) {
     353                        $sql_chunks['where'] = array( '( ' . implode( ' AND ', $sql_chunks['where'] ) . ' )' );
     354                }
     355
     356                return $sql_chunks;
     357        }
     358}
  • tests/phpunit/testcases/xprofile/class-bp-xprofile-field.php

     
    5454                // cleanup!
    5555                unset( $_POST['checkbox_option'] );
    5656        }
     57
     58        /**
     59         * @group xprofile_fields_signup
     60         */
     61        public function test_get_signup_fields() {
     62                $g1 = $this->factory->xprofile_group->create();
     63                $f1 = $this->factory->xprofile_field->create( array(
     64                        'field_group_id' => $g1,
     65                        'type'           => 'textbox',
     66                ) );
     67                $f2 = $this->factory->xprofile_field->create( array(
     68                        'field_group_id' => $g1,
     69                        'type'           => 'textbox',
     70                ) );
     71                $f3 = $this->factory->xprofile_field->create( array(
     72                        'field_group_id' => $g1,
     73                        'type'           => 'textbox',
     74                ) );
     75
     76                bp_xprofile_update_meta( $f1, 'field', 'signup_position', '1' );
     77                bp_xprofile_update_meta( $f2, 'field', 'signup_position', '2' );
     78                bp_xprofile_update_meta( $f3, 'field', 'signup_position', '3' );
     79
     80                $field_ids        = array( $f1, $f2, $f3 );
     81                $signup_field_ids = bp_xprofile_get_signup_field_ids();
     82
     83                $this->assertEquals( $field_ids, $signup_field_ids );
     84        }
    5785}