Ticket #6347: 6347.03.patch
| File 6347.03.patch, 42.6 KB (added by , 11 years ago) |
|---|
-
src/bp-xprofile/bp-xprofile-admin.php
449 449 // Check the nonce 450 450 check_admin_referer( 'bp_reorder_fields', '_wpnonce_reorder_fields' ); 451 451 452 if ( empty( $_POST['field_order'] ) ) { 452 // Bail if missing POST data 453 if ( empty( $_POST['field_order'] ) || empty( $_POST['field_group_id'] ) ) { 453 454 return false; 454 455 } 455 456 456 457 parse_str( $_POST['field_order'], $order ); 457 458 458 $field_group_id = $_POST['field_group_id'];459 460 459 foreach ( (array) $order['field'] as $position => $field_id ) { 461 xprofile_update_field_position( (int) $field_id, (int) $position, (int) $ field_group_id);460 xprofile_update_field_position( (int) $field_id, (int) $position, (int) $_POST['field_group_id'] ); 462 461 } 463 462 } 464 463 add_action( 'wp_ajax_xprofile_reorder_fields', 'xprofile_ajax_reorder_fields' ); -
src/bp-xprofile/bp-xprofile-functions.php
968 968 } 969 969 970 970 /** 971 * Get array of field IDs to show on member registration page 972 * 973 * @since BuddyPress (2.3.0) 974 * 975 * @global $wpdb $wpdb 976 * @return array 977 */ 978 function bp_xprofile_get_signup_field_ids() { 979 global $wpdb; 980 981 $field_ids = array(); 982 $fields = wp_cache_get( 'signup_fields', 'bp_xprofile' ); 983 if ( false === $fields ) { 984 $table_key = 'xprofile_fieldmeta'; 985 $table_name = $wpdb->{$table_key}; 986 $sql = $wpdb->prepare( "SELECT * FROM {$table_name} WHERE object_type = %s AND meta_key = %s", 'field', 'signup_position' ); 987 $fields = $wpdb->get_results( $sql ); 988 989 wp_cache_set( 'signup_fields', $fields, 'bp_xprofile' ); 990 } 991 992 // Bail if no signup fields 993 if ( ! empty( $fields ) ) { 994 $field_ids = wp_list_pluck( $fields, 'object_id' ); 995 } 996 997 return apply_filters( 'bp_xprofile_get_signup_field_ids', $field_ids, $fields ); 998 } 999 1000 /** 971 1001 * Return the field ID for the Full Name xprofile field. 972 1002 * 973 1003 * @since BuddyPress (2.0.0) -
src/bp-xprofile/bp-xprofile-loader.php
367 367 wp_cache_add_global_groups( array( 368 368 'bp_xprofile', 369 369 'bp_xprofile_data', 370 'bp_xprofile_fields', 370 371 'bp_xprofile_groups', 371 372 'xprofile_meta' 372 373 ) ); -
src/bp-xprofile/classes/class-bp-xprofile-field.php
110 110 public $allow_custom_visibility = 'allowed'; 111 111 112 112 /** 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 /** 113 120 * @since BuddyPress (2.0.0) 114 121 * 115 122 * @var BP_XProfile_Field_Type Field type object used for validation … … 157 164 * @param bool $get_data 158 165 */ 159 166 public function populate( $id, $user_id = null, $get_data = true ) { 160 global $wpdb, $userdata;161 167 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' ); 165 170 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 ) { 168 173 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 ); 182 178 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; 189 182 } 190 183 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( $args ); 194 186 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 } 199 192 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; 204 196 } 205 }206 197 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; 218 211 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; 224 215 } 225 216 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; 228 220 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' ); 232 225 233 // delete the data in the DB for this field234 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'; 237 230 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; 239 240 } 240 241 241 242 /** … … 245 246 * 246 247 * @global object $wpdb 247 248 * 249 * @param boolean $update_children Should child fields be updated? 250 * 248 251 * @return boolean 249 252 */ 250 public function save( ) {253 public function save( $updating_position = false ) { 251 254 global $wpdb; 252 255 253 256 $bp = buddypress(); 254 257 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 ); 258 $this->group_id = apply_filters( 'xprofile_field_group_id_before_save', $this->group_id, $this->id ); 259 $this->parent_id = apply_filters( 'xprofile_field_parent_id_before_save', $this->parent_id, $this->id ); 260 $this->type = apply_filters( 'xprofile_field_type_before_save', $this->type, $this->id ); 261 $this->name = apply_filters( 'xprofile_field_name_before_save', $this->name, $this->id ); 262 $this->description = apply_filters( 'xprofile_field_description_before_save', $this->description, $this->id ); 263 $this->is_required = apply_filters( 'xprofile_field_is_required_before_save', $this->is_required, $this->id ); 264 $this->is_default_option = apply_filters( 'xprofile_field_is_default_option_before_save', $this->is_default_option, $this->id ); 265 $this->field_order = apply_filters( 'xprofile_field_field_order_before_save', $this->field_order, $this->id ); 266 $this->option_order = apply_filters( 'xprofile_field_option_order_before_save', $this->option_order, $this->id ); 267 $this->order_by = apply_filters( 'xprofile_field_order_by_before_save', $this->order_by, $this->id ); 268 $this->can_delete = apply_filters( 'xprofile_field_can_delete_before_save', $this->can_delete, $this->id ); 269 $this->type_obj = bp_xprofile_create_field_type( $this->type ); 270 $this->type_obj->field_obj = $this; 266 271 267 272 /** 268 273 * Fires before the current field instance gets saved. … … 275 280 */ 276 281 do_action_ref_array( 'xprofile_field_before_save', array( $this ) ); 277 282 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 ); 283 // Existing field 284 if ( ! empty( $this->id ) ) { 285 $sql = $wpdb->prepare( "UPDATE {$bp->profile->table_name_fields} 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 ); 286 287 // New field 280 288 } 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 );289 $sql = $wpdb->prepare( "INSERT INTO {$bp->profile->table_name_fields} (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 ); 282 290 } 283 291 292 // Attempt to update or insert 293 $query = $wpdb->query( $sql ); 294 284 295 /** 285 * Check for null so field options can be changed without changing any286 * other part of the field. The described situation will return 0 here.296 * Check for `null` instead of `false` or `empty()` to allow child 297 * field-options to be modified even if no other field properties changed. 287 298 */ 288 if ( $wpdb->query( $sql ) !== null ) { 299 if ( ( null === $query ) || is_wp_error( $query ) ) { 300 return false; 301 } 289 302 290 if ( !empty( $this->id ) ) { 291 $field_id = $this->id; 292 } else { 293 $field_id = $wpdb->insert_id; 294 } 303 // Update existing field 304 if ( empty( $this->id ) ) { 305 $this->id = $wpdb->insert_id; 306 } 295 307 296 // Only do this if we are editing an existing field297 if ( $this->id != null ) {308 // Delete the relevant caches 309 $this->purge_caches(); 298 310 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 } 311 /** 312 * Check to see if this is a field with child options. 313 * We need to add the options to the db, if it is. 314 */ 315 if ( ( false === $updating_position ) && ! empty( $this->type_obj->supports_options ) ) { 307 316 308 317 /** 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. 318 * Remove any field-options for this field. They will be re-added if 319 * needed. This prenets orphaned options if the user changes a field 320 * from a radio-button with options to a textbox without. 321 * 322 * It's maybe a bit of a brute-force approach, but seems easier than 323 * querying for items & updating/deleting/creating each time; 324 * and 60% of the time, it works everytime, so that's nice. 311 325 */ 312 if ( $this->type_obj->supports_options ) {326 $this->delete_children(); 313 327 314 if ( !empty( $this->id ) ) { 315 $parent_id = $this->id; 316 } else { 317 $parent_id = $wpdb->insert_id; 318 } 328 // Allow plugins to filter the field's child options (i.e. the items in a selectbox). 329 $post_options = ! empty( $_POST["{$this->type}_option"] ) ? $_POST["{$this->type}_option"] : ''; 330 $post_defaults = ! empty( $_POST["isDefault_{$this->type}_option"] ) ? $_POST["isDefault_{$this->type}_option"] : ''; 319 331 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"] : ''; 332 /** 333 * Filters the submitted field option value before saved. 334 * 335 * @since BuddyPress (1.5.0) 336 * 337 * @param string $post_options Submitted option value. 338 * @param BP_XProfile_Field $type Current field type being saved for. 339 */ 340 $options = apply_filters( 'xprofile_field_options_before_save', $post_options, $this->type ); 323 341 324 /**325 * Filters the submittedfield option value before saved.326 *327 * @since BuddyPress (1.5.0)328 *329 * @param string $post_option Submittedoption value.330 * @param BP_XProfile_Field $typeCurrent field type being saved for.331 */332 $options = apply_filters( 'xprofile_field_options_before_save', $post_option,$this->type );342 /** 343 * Filters the default field option value before saved. 344 * 345 * @since BuddyPress (1.5.0) 346 * 347 * @param string $post_defaults Default option value. 348 * @param BP_XProfile_Field $type Current field type being saved for. 349 */ 350 $defaults = apply_filters( 'xprofile_field_default_before_save', $post_defaults, $this->type ); 333 351 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 ); 352 // There are options that need recreating 353 if ( ! empty( $options ) ) { 343 354 355 // Start counter at 1 to avoid 0 values 344 356 $counter = 1; 345 if ( !empty( $options ) ) {346 foreach ( (array) $options as $option_key => $option_value ) {347 $is_default = 0;348 357 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 // Loop through options and re-create them 359 foreach ( (array) $options as $option_key => $option_name ) { 358 360 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 } 361 // Determine if option is the default 362 if ( is_array( $defaults ) && isset( $defaults[ $option_key ] ) ) { 363 $is_default = 1; 364 } elseif ( (int) $defaults == $option_key ) { 365 $is_default = 1; 366 } else { 367 $is_default = 0; 368 } 365 369 366 $counter++; 370 // Recreate the option based on value and other criteria 371 if ( ! empty( $option_name ) ) { 372 $option = new BP_XProfile_Field(); 373 $option->group_id = $this->group_id; 374 $option->parent_id = $this->id; 375 $option->type = 'option'; 376 $option->name = $option_name; 377 $option->description = ''; 378 $option->is_required = 0; 379 $option->is_default_option = $is_default; 380 $option->option_order = $counter; 381 $option->save(); 367 382 } 383 384 $counter++; 368 385 } 369 386 } 387 } 370 388 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 ) );389 /** 390 * Fires after the current field instance gets saved. 391 * 392 * @since BuddyPress (1.0.0) 393 * 394 * @param BP_XProfile_Field Current instance of the field being saved. 395 */ 396 do_action_ref_array( 'xprofile_field_after_save', array( $this ) ); 379 397 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 } 398 return (int) $this->id; 388 399 } 389 400 390 401 /** … … 404 415 * 405 416 * @since BuddyPress (1.2.0) 406 417 * 407 * @global object $wpdb408 *409 418 * @param bool $for_editing 410 419 * @return array 411 420 */ 412 421 public function get_children( $for_editing = false ) { 413 global $wpdb;414 422 415 423 // 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'; 424 if ( ( false === $for_editing ) && in_array( strtoupper( $this->order_by ), array( 'ASC', 'DESC' ) ) ) { 425 $order_by = 'name'; 426 $sort = strtoupper( $this->order_by ); 420 427 } else { 421 $sort_sql = 'ORDER BY option_order ASC'; 428 $order_by = 'option_order'; 429 $sort = 'ASC'; 422 430 } 423 431 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; 432 // Get children 433 $children = self::get( array( 434 'parent_id' => $this->id, 435 'order_by' => $order_by, 436 'sort' => $sort, 437 'update_meta_cache' => false 438 ) ); 439 440 // Set children to false if empty or invalid 441 if ( empty( $children ) || ! is_array( $children ) ) { 442 $children = false; 430 443 } 431 444 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 437 445 /** 438 446 * Filters the found children for a field. 439 447 * … … 446 454 } 447 455 448 456 /** 457 * Delete a profile field 458 * 459 * @since BuddyPress (1.1.0) 460 * 461 * @global object $wpdb 462 * @param boolean $delete_data 463 * @param boolean $delete_children 464 * @return boolean 465 */ 466 public function delete( $delete_data = false, $delete_children = true ) { 467 global $wpdb; 468 469 // Prevent deletion if no ID is present 470 // Prevent deletion by url when can_delete is false. 471 if ( empty( $this->id ) || empty( $this->can_delete ) ) { 472 return false; 473 } 474 475 $bp = buddypress(); 476 477 // Attempt to get fields to delete 478 $sql = $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE id = %d LIMIT 1", $this->id ); 479 $field_id = $wpdb->get_var( $sql ); 480 481 // Bail if no children exist 482 if ( empty( $field_id ) || is_wp_error( $field_id ) ) { 483 return false; 484 } 485 486 // Attempt to delete 487 $sql = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE id = %d LIMIT 1", $this->id ); 488 $deleted = $wpdb->query( $sql ); 489 490 // Bail if no children exist 491 if ( empty( $deleted ) || is_wp_error( $deleted ) ) { 492 return false; 493 } 494 495 // Delete cache 496 // Delete the relevant caches 497 $this->purge_caches(); 498 499 // Maybe delete children 500 if ( true === $delete_children ) { 501 $this->delete_children(); 502 } 503 504 // delete the data in the DB for this field 505 if ( true === $delete_data ) { 506 BP_XProfile_ProfileData::delete_for_field( $this->id ); 507 } 508 509 return true; 510 } 511 512 /** 449 513 * Delete all field children for this field 450 514 * 451 515 * @since BuddyPress (1.2.0) … … 455 519 public function delete_children() { 456 520 global $wpdb; 457 521 458 $bp = buddypress(); 459 $sql = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE parent_id = %d", $this->id ); 522 $bp = buddypress(); 460 523 461 $wpdb->query( $sql ); 524 // Attempt to get fields to delete 525 $sql = $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE parent_id = %d", $this->id ); 526 $field_ids = $wpdb->get_col( $sql ); 527 528 // Bail if no children exist 529 if ( empty( $field_ids ) || is_wp_error( $field_ids ) ) { 530 return false; 531 } 532 533 // Delete child fields 534 foreach ( $field_ids as $field_id ) { 535 $field = new BP_XProfile_Field( $field_id ); 536 $field->delete( false, false ); 537 } 538 539 // We care less about actual option deletion at this point 540 return true; 462 541 } 463 542 464 543 /** Static Methods ********************************************************/ 465 544 466 public static function get_type( $field_id = 0 ) { 545 /** 546 * Get profile fields, as specified by parameters 547 * 548 * @see BP_Activity_Activity::get_filter_sql() for a description of the 549 * 'filter' parameter. 550 * @see WP_Meta_Query::queries for a description of the 'meta_query' 551 * parameter format. 552 * 553 * @param array $args { 554 * An array of arguments. All items are optional. 555 * 556 * @type int $page Which page of results to fetch. Using page=1 without per_page will result 557 * in no pagination. Default: 1. 558 * @type int|bool $per_page Number of results per page. Default: 25. 559 * @type int|bool $max Maximum number of results to return. Default: false (unlimited). 560 * @type string $sort ASC or DESC. Default: 'DESC'. 561 * @type array $exclude Array of activity IDs to exclude. Default: false. 562 * @type array $in Array of ids to limit query by (IN). Default: false. 563 * @type array $meta_query Array of meta_query conditions. See WP_Meta_Query::queries. 564 * @type array $date_query Array of date_query conditions. See first parameter of 565 * WP_Date_Query::__construct(). 566 * @type array $filter_query Array of advanced query conditions. See BP_Activity_Query::__construct(). 567 * @type string|array $scope Pre-determined set of activity arguments. 568 * @type array $filter See BP_Activity_Activity::get_filter_sql(). 569 * @type string $search_terms Limit results by a search term. Default: false. 570 * @type bool $display_comments Whether to include activity comments. Default: false. 571 * @type bool $show_hidden Whether to show items marked hide_sitewide. Default: false. 572 * @type string $spam Spam status. Default: 'ham_only'. 573 * @type bool $update_meta_cache Whether to pre-fetch metadata for queried activity items. Default: true. 574 * @type string|bool $count_total If true, an additional DB query is run to count the total activity items 575 * for the query. Default: false. 576 * } 577 * @return array The array returned has two keys: 578 * - 'total' is the count of located activities 579 * - 'activities' is an array of the located activities 580 */ 581 public static function get( $args = '' ) { 467 582 global $wpdb; 468 583 584 $r = wp_parse_args( $args, array( 585 'include' => false, 586 'exclude' => false, 587 'group_id' => false, 588 'group_not_in' => false, 589 'parent_id' => false, 590 'parent_not_in' => false, 591 'type_in' => false, 592 'type_not_in' => false, 593 'name' => '', 594 'is_required' => false, 595 'is_default_option' => false, 596 'can_delete' => 1, 597 'order_by' => false, 598 'sort' => 'ASC', 599 'fetch_data' => false, 600 'meta_query' => false, // @todo implement 601 'update_meta_cache' => true 602 ) ); 603 604 // Get the profile fields table name 605 $table_name = buddypress()->profile->table_name_fields; 606 607 // Select conditions 608 $select_sql = "SELECT *"; 609 610 $from_sql = " FROM {$table_name} f"; 611 612 $join_sql = ''; 613 614 // Where conditions 615 $where_conditions = array(); 616 617 /** Field ID **********************************************************/ 618 619 // Setup IN query for field IDs 620 if ( ! empty( $r['include'] ) ) { 621 $include_in = implode( ',', wp_parse_id_list( $r['include'] ) ); 622 $where_conditions['include'] = "id IN ({$include_in})"; 623 } 624 625 // Setup NOT IN query for field IDs 626 if ( ! empty( $r['exclude'] ) ) { 627 $exclude_not_in = implode( ',', wp_parse_id_list( $r['exclude'] ) ); 628 $where_conditions['exclude'] = "id NOT IN ({$exclude_not_in})"; 629 } 630 631 /** Group ID **********************************************************/ 632 633 // Setup IN query for field-group IDs 634 if ( ! empty( $r['group_id'] ) ) { 635 $group_in = implode( ',', wp_parse_id_list( $r['group_id'] ) ); 636 $where_conditions['group_in'] = "group_id IN ({$group_in})"; 637 } 638 639 // Setup NOT IN query for field-group IDs 640 if ( ! empty( $r['group_not_in'] ) ) { 641 $group_not_in = implode( ',', wp_parse_id_list( $r['group_not_in'] ) ); 642 $where_conditions['group_not_in'] = "group_id NOT IN ({$group_not_in})"; 643 } 644 645 /** Parent ID *********************************************************/ 646 647 // Setup IN query for parent-field IDs 648 if ( ! empty( $r['parent_id'] ) || ( '0' === $r['parent_id'] ) ) { 649 $parent_in = implode( ',', wp_parse_id_list( $r['parent_id'] ) ); 650 $where_conditions['parent_in'] = "parent_id IN ({$parent_in})"; 651 } 652 653 // Setup NOT IN query for parent-field IDs 654 if ( ! empty( $r['parent_not_in'] ) ) { 655 $parent_not_in = implode( ',', wp_parse_id_list( $r['parent_not_in'] ) ); 656 $where_conditions['parent_not_in'] = "parent_id NOT IN ({$parent_not_in})"; 657 } 658 659 /** Types *************************************************************/ 660 661 // Setup IN query for type-field IDs 662 if ( ! empty( $r['type_in'] ) ) { 663 $type_in = implode( ',', wp_parse_id_list( $r['type_in'] ) ); 664 $where_conditions['type_in'] = "type IN ({$type_in})"; 665 } 666 667 // Setup NOT IN query for type-field IDs 668 if ( ! empty( $r['type_not_in'] ) ) { 669 $type_not_in = implode( ',', wp_parse_id_list( $r['type_not_in'] ) ); 670 $where_conditions['type_not_in'] = "type NOT IN ({$type_not_in})"; 671 } 672 673 /** Name **************************************************************/ 674 675 // Setup IN query for type-field IDs 676 if ( ! empty( $r['name'] ) ) { 677 $where_conditions['name'] = $wpdb->prepare( "name = %s", $r['name'] ); 678 } 679 680 /** Other *************************************************************/ 681 682 // Sorting 683 $sort = $r['sort']; 684 if ( ! in_array( $sort, array( 'ASC', 'DESC' ) ) ) { 685 $sort = 'ASC'; 686 } 687 688 // Ordering 689 $order_by = 'field_order'; 690 if ( ! empty( $r['order_by'] ) ) { 691 $order_by = $r['order_by']; 692 } 693 694 // Process meta_query into SQL 695 // $meta_query_sql = self::get_meta_query_sql( $r['meta_query'] ); 696 // 697 // if ( ! empty( $meta_query_sql['join'] ) ) { 698 // $join_sql .= $meta_query_sql['join']; 699 // } 700 // 701 // if ( ! empty( $meta_query_sql['where'] ) ) { 702 // $where_conditions[] = $meta_query_sql['where']; 703 // } 704 705 /** 706 * Filters the MySQL WHERE conditions for the XProfile field get method. 707 * 708 * @since BuddyPress (2.3.0) 709 * 710 * @param array $where_conditions Current conditions for MySQL WHERE statement. 711 * @param array $r Parsed arguments passed into method. 712 * @param string $select_sql Current SELECT MySQL statement at point of execution. 713 * @param string $from_sql Current FROM MySQL statement at point of execution. 714 * @param string $join_sql Current INNER JOIN MySQL statement at point of execution. 715 */ 716 $where_conditions = apply_filters( 'bp_xprofile_get_where_conditions', $where_conditions, $r, $select_sql, $from_sql, $join_sql ); 717 718 // Bail if no WHERE conditions 719 if ( empty( $where_conditions ) ) { 720 return array(); 721 } 722 723 // Join the where conditions together 724 $where_sql = 'WHERE ' . join( ' AND ', $where_conditions ); 725 726 /** 727 * Filters the preferred order of indexes for xprofile fields. 728 * 729 * @since BuddyPress (2.3.0) 730 * 731 * @param array Array of indexes in preferred order. 732 */ 733 $indexes = apply_filters( 'bp_xprofile_preferred_index_order', array( 'group_id', 'parent_id', 'field_order', 'can_delete', 'is_required' ) ); 734 735 foreach ( $indexes as $key => $index ) { 736 if ( false !== strpos( $where_sql, $index ) ) { 737 $the_index = $index; 738 break; // Take the first one we find 739 } 740 } 741 742 if ( ! empty( $the_index ) ) { 743 $index_hint_sql = "USE INDEX ({$the_index})"; 744 } else { 745 $index_hint_sql = ''; 746 } 747 748 // Query first for activity IDs 749 $field_ids_sql = "{$select_sql} {$from_sql} {$index_hint_sql} {$join_sql} {$where_sql} ORDER BY f.{$order_by} {$sort}"; 750 $fields = $wpdb->get_results( $field_ids_sql ); 751 $field_ids = wp_list_pluck( $fields, 'id' ); 752 753 // Bail if no field IDs 754 if ( empty( $field_ids ) ) { 755 return array(); 756 } 757 758 // Get fields from queried IDs 759 self::update_field_caches( $field_ids ); 760 761 // Maybe update meta cache 762 if ( ! empty( $field_ids ) ) { 763 764 // Maybe fetch data for all fields 765 if ( ! empty( $r['fetch_data'] ) ) { 766 767 // Wipe out the fields array 768 $fields = array(); 769 770 // Loop through fields and get data for them 771 foreach ( $field_ids as $field_id ) { 772 $field = new BP_XProfile_Field( $field_id ); 773 $field->data = $field->get_field_data( $r['fetch_data'] ); 774 $fields[] = $field; 775 } 776 } 777 778 // Update caches 779 if ( ! empty( $r['update_meta_cache'] ) ) { 780 bp_xprofile_update_meta_cache( array( 'field' => $field_ids ) ); 781 } 782 } 783 784 return $fields; 785 } 786 787 /** 788 * Convert field IDs to field objects, as expected in template loop. 789 * 790 * @since BuddyPress (2.3.0) 791 * 792 * @param array $field_ids Array of field IDs. 793 * @return array 794 */ 795 protected static function update_field_caches( $field_ids = array() ) { 796 global $wpdb; 797 798 // Bail if no field ID's passed 799 if ( empty( $field_ids ) ) { 800 return array(); 801 } 802 803 // Get BuddyPress 804 $uncached_ids = bp_get_non_cached_ids( $field_ids, 'bp_xprofile_fields' ); 805 806 // Prime caches as necessary 807 if ( ! empty( $uncached_ids ) ) { 808 809 // Format the field ID's for use in the query below 810 $uncached_ids_sql = implode( ',', wp_parse_id_list( $uncached_ids ) ); 811 812 // Fetch data from field table, preserving order 813 $table_name = buddypress()->profile->table_name_fields; 814 $sql = "SELECT * FROM {$table_name} WHERE id IN ({$uncached_ids_sql})"; 815 $queried_fdata = $wpdb->get_results( $sql ); 816 817 // Reset the field IDs array 818 $field_ids = array(); 819 820 // Put that data into the placeholders created earlier, 821 // and add it to the cache 822 if ( ! empty( $queried_fdata ) ) { 823 foreach ( $queried_fdata as $fdata ) { 824 $field_ids[] = $fdata->id; 825 wp_cache_set( $fdata->id, $fdata, 'bp_xprofile_fields' ); 826 } 827 } 828 } 829 830 // Return newly cached field IDs 831 return $field_ids; 832 } 833 834 public static function get_type( $field_id = 0 ) { 835 469 836 // Bail if no field ID 470 837 if ( empty( $field_id ) ) { 471 838 return false; 472 839 } 473 840 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 ); 841 // Attempt to get the field 842 $field = new BP_XProfile_Field( $field_id ); 477 843 478 // Return field type479 if ( ! empty( $type ) ) {480 return $type;844 // Bail if no field found 845 if ( empty( $field->type ) ) { 846 return false; 481 847 } 482 848 483 return false;849 return $field->type; 484 850 } 485 851 486 852 /** … … 519 885 * 520 886 * @since BuddyPress (1.5.0) 521 887 * 522 * @global object $wpdb 523 * @param string $field_name 888 * @param string $field_name 524 889 * 525 890 * @return boolean 526 891 */ 527 892 public static function get_id_from_name( $field_name = '' ) { 528 global $wpdb;529 893 530 $bp = buddypress(); 894 // Bail if no field ID 895 if ( empty( $field_name ) ) { 896 return false; 897 } 531 898 532 if ( empty( $bp->profile->table_name_fields ) || empty( $field_name ) ) { 899 // Attempt to get the field 900 $fields = self::get( array( 901 'name' => $field_name, 902 'parent_id' => '0' 903 ) ); 904 905 // Return field if found 906 if ( empty( $fields ) ) { 533 907 return false; 534 908 } 535 909 536 $sql = $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s AND parent_id = 0", $field_name ); 910 // Take the first field 911 $field = reset( $fields ); 537 912 538 return $wpdb->get_var( $sql ); 913 // Return false if not found 914 return $field->id; 539 915 } 540 916 541 917 /** … … 552 928 * @return boolean 553 929 */ 554 930 public static function update_position( $field_id, $position = null, $field_group_id = null ) { 555 global $wpdb;556 931 557 932 // Bail if invalid position or field group 558 933 if ( ! is_numeric( $position ) || ! is_numeric( $field_group_id ) ) { … … 560 935 } 561 936 562 937 // 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 );938 $field = new BP_XProfile_Field( $field_id ); 939 $field->field_order = $position; 940 $field->group_id = $field_group_id; 566 941 567 // Update $field_id with new $position and $field_group_id 568 if ( ! empty( $parent ) && ! is_wp_error( $parent ) ) { 942 // Bail if field did not save - pass `true` to prevent option deletion 943 if ( ! $field->save( true ) ) { 944 return false; 945 } 569 946 570 // Update any children of this $field_id571 $sql = $wpdb->prepare( "UPDATE {$table_name} SET group_id = %d WHERE parent_id = %d", $field_group_id,$field_id );572 $wpdb->query( $sql);947 // Regather the field and its children 948 $field = new BP_XProfile_Field( $field_id ); 949 $children = $field->get_children(); 573 950 574 return $parent; 951 // If child fields exist, update them 952 if ( ! empty( $children ) ) { 953 954 // Loop through children and update field group ID 955 foreach ( $children as $option_field ) { 956 $option = new BP_XProfile_Field( $option_field->id ); 957 $option->group_id = $field->group_id; 958 $option->save( true ); 959 } 575 960 } 576 961 577 return false;962 return true; 578 963 } 579 964 580 965 /** … … 635 1020 $this->description = $_POST['description']; 636 1021 $this->is_required = $_POST['required']; 637 1022 $this->type = $_POST['fieldtype']; 638 $this->order_by = $_POST["sort_order_{$this->type}"];1023 $this->order_by = ! empty( $_POST["sort_order_{$this->type}"] ) ? $_POST["sort_order_{$this->type}"] : ''; 639 1024 $this->field_order = $_POST['field_order']; 640 1025 } 641 1026 } else { … … 680 1065 // Output the required metabox 681 1066 $this->required_metabox(); 682 1067 683 // Output the field visibility metabox es1068 // Output the field visibility metabox 684 1069 $this->visibility_metabox(); 685 1070 1071 // Output the signup metabox 1072 $this->signup_metabox(); 1073 686 1074 /** 687 1075 * Fires after XProfile Field sidebar metabox. 688 1076 * … … 902 1290 } 903 1291 904 1292 /** 1293 * Output the metabox for enabling this field to appear on user registration 1294 * 1295 * @since BuddyPress (2.3.0) 1296 */ 1297 private function signup_metabox() { 1298 ?> 1299 1300 <div class="postbox"> 1301 <h3><label for="signup-field"><?php _e( 'Sign Ups', 'buddypress' ); ?></label></h3> 1302 <div class="inside"> 1303 <ul> 1304 <li> 1305 <input type="checkbox" id="signup-position" name="signup-position" value="1" <?php checked( $this->signup_position, ! null ); ?> /> 1306 <label for="signup-position"><?php esc_html_e( 'Display on Registration', 'buddypress' ); ?></label> 1307 </li> 1308 </ul> 1309 </div> 1310 </div> 1311 1312 <?php 1313 } 1314 1315 /** 905 1316 * Output the metabox for setting what type of field this is 906 1317 * 907 1318 * @since BuddyPress (2.3.0) … … 975 1386 // Compare & return 976 1387 return (bool) ( 1 === (int) $field_id ); 977 1388 } 1389 1390 /** 1391 * Purge caches relevant to field updates 1392 * 1393 * @since BuddyPress (2.3.0) 1394 */ 1395 private function purge_caches() { 1396 1397 // Delete the cached value for this field 1398 wp_cache_delete( $this->id, 'bp_xprofile_fields' ); 1399 1400 // Bust cache of parent field 1401 if ( ! empty( $this->parent_id ) ) { 1402 wp_cache_delete( $this->parent_id, 'bp_xprofile_fields' ); 1403 } 1404 1405 // Bust cache of parent group 1406 if ( ! empty( $this->group_id ) ) { 1407 wp_cache_delete( 'all', 'bp_xprofile_groups' ); 1408 wp_cache_delete( $this->group_id, 'bp_xprofile_groups' ); 1409 } 1410 1411 } 978 1412 } -
src/bp-xprofile/classes/class-bp-xprofile-group.php
301 301 return $groups; 302 302 } 303 303 304 /** Fields ************************************************************/ 305 304 306 // Get the group ids from the groups we found 305 307 $group_ids = wp_list_pluck( $groups, 'id' ); 306 308 … … 312 314 return $groups; 313 315 } 314 316 315 // Setup IN query from group IDs316 $group_ids_in = implode( ',', (array) $group_ids );317 318 317 // Support arrays and comma-separated strings 319 318 $exclude_fields_cs = wp_parse_id_list( $r['exclude_fields'] ); 320 319 321 320 // Visibility - Handled here so as not to be overridden by sloppy use of the 322 321 // exclude_fields parameter. See bp_xprofile_get_hidden_fields_for_user() 323 322 $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 ) ); 326 324 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 334 326 // 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( 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 336 336 $field_ids = wp_list_pluck( $fields, 'id' ); 337 337 338 338 // Store field IDs for meta cache priming … … 343 343 return $groups; 344 344 } 345 345 346 /** Field Data ********************************************************/ 347 346 348 // Maybe fetch field data 347 349 if ( ! empty( $r['fetch_field_data'] ) ) { 348 350 … … 361 363 $maybe_value = maybe_unserialize( $data->value ); 362 364 363 365 // 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 ) ) { 365 367 366 368 // Fields that have data get removed from the list 367 369 unset( $field_ids[ $key ] ); … … 369 371 } 370 372 371 373 // The remaining members of $field_ids are empty. Remove them. 372 foreach ( $fields as $field_key => $field ) {374 foreach ( $fields as $field_key => $field ) { 373 375 if ( in_array( $field->id, $field_ids ) ) { 374 376 unset( $fields[ $field_key ] ); 375 377 }