[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-xprofile/classes/ -> class-bp-xprofile-field.php (source)

   1  <?php
   2  /**
   3   * BuddyPress XProfile Classes.
   4   *
   5   * @package BuddyPress
   6   * @subpackage XProfileClasses
   7   * @since 1.0.0
   8   */
   9  
  10  // Exit if accessed directly.
  11  defined( 'ABSPATH' ) || exit;
  12  
  13  /**
  14   * Class to help set up XProfile fields.
  15   *
  16   * @since 1.0.0
  17   */
  18  class BP_XProfile_Field {
  19  
  20      /**
  21       * Field ID.
  22       *
  23       * @since 1.0.0
  24       * @var int ID of field.
  25       */
  26      public $id;
  27  
  28      /**
  29       * Field group ID.
  30       *
  31       * @since 1.0.0
  32       * @var int Field group ID for field.
  33       */
  34      public $group_id;
  35  
  36      /**
  37       * Field parent ID.
  38       *
  39       * @since 1.0.0
  40       * @var int Parent ID of field.
  41       */
  42      public $parent_id;
  43  
  44      /**
  45       * Field type.
  46       *
  47       * @since 1.0.0
  48       * @var string Field type.
  49       */
  50      public $type;
  51  
  52      /**
  53       * Field name.
  54       *
  55       * @since 1.0.0
  56       * @var string Field name.
  57       */
  58      public $name;
  59  
  60      /**
  61       * Field description.
  62       *
  63       * @since 1.0.0
  64       * @var string Field description.
  65       */
  66      public $description;
  67  
  68      /**
  69       * Required field?
  70       *
  71       * @since 1.0.0
  72       * @var bool Is field required to be filled out?
  73       */
  74      public $is_required;
  75  
  76      /**
  77       * Deletable field?
  78       *
  79       * @since 1.0.0
  80       * @var int Can field be deleted?
  81       */
  82      public $can_delete = '1';
  83  
  84      /**
  85       * Field position.
  86       *
  87       * @since 1.0.0
  88       * @var int Field position.
  89       */
  90      public $field_order;
  91  
  92      /**
  93       * Option order.
  94       *
  95       * @since 1.0.0
  96       * @var int Option order.
  97       */
  98      public $option_order;
  99  
 100      /**
 101       * Order child fields.
 102       *
 103       * @since 1.0.0
 104       * @var string Order child fields by.
 105       */
 106      public $order_by;
 107  
 108      /**
 109       * Is this the default option?
 110       *
 111       * @since 1.0.0
 112       * @var bool Is this the default option for this field?
 113       */
 114      public $is_default_option;
 115  
 116      /**
 117       * Field data visibility.
 118       *
 119       * @since 1.9.0
 120       * @since 2.4.0 Property marked protected. Now accessible by magic method or by `get_default_visibility()`.
 121       * @var string Default field data visibility.
 122       */
 123      protected $default_visibility;
 124  
 125      /**
 126       * Is the visibility able to be modified?
 127       *
 128       * @since 2.3.0
 129       * @since 2.4.0 Property marked protected. Now accessible by magic method or by `get_allow_custom_visibility()`.
 130       * @var string Members are allowed/disallowed to modify data visibility.
 131       */
 132      protected $allow_custom_visibility;
 133  
 134      /**
 135       * Whether values from this field are autolinked to directory searches.
 136       *
 137       * @since 2.5.0
 138       * @var bool
 139       */
 140      public $do_autolink;
 141  
 142      /**
 143       * The signup position of the field into the signups form.
 144       *
 145       * @since 8.0.0
 146       * @var int
 147       */
 148      public $signup_position;
 149  
 150      /**
 151       * Field type option.
 152       *
 153       * @since 2.0.0
 154       * @var BP_XProfile_Field_Type Field type object used for validation.
 155       */
 156      public $type_obj = null;
 157  
 158      /**
 159       * Field data for user ID.
 160       *
 161       * @since 2.0.0
 162       * @var BP_XProfile_ProfileData Field data for user ID.
 163       */
 164      public $data;
 165  
 166      /**
 167       * Member types to which the profile field should be applied.
 168       *
 169       * @since 2.4.0
 170       * @var array Array of member types.
 171       */
 172      protected $member_types;
 173  
 174      /**
 175       * Initialize and/or populate profile field.
 176       *
 177       * @since 1.1.0
 178       *
 179       * @param int|null $id Field ID.
 180       * @param int|null $user_id User ID.
 181       * @param bool     $get_data Get data.
 182       */
 183  	public function __construct( $id = null, $user_id = null, $get_data = true ) {
 184  
 185          if ( ! empty( $id ) ) {
 186              $this->populate( $id, $user_id, $get_data );
 187  
 188          // Initialize the type obj to prevent fatals when creating new profile fields.
 189          } else {
 190              $this->type_obj            = bp_xprofile_create_field_type( 'textbox' );
 191              $this->type_obj->field_obj = $this;
 192          }
 193  
 194          /**
 195           * Fires when the xProfile field object has been constructed.
 196           *
 197           * @since 8.0.0
 198           *
 199           * @param BP_XProfile_Field $this The xProfile field object.
 200           */
 201          do_action( 'bp_xprofile_field', $this );
 202      }
 203  
 204      /**
 205       * Populate a profile field object.
 206       *
 207       * @since 1.1.0
 208       *
 209       * @global object $wpdb
 210       * @global object $userdata
 211       *
 212       * @param int      $id Field ID.
 213       * @param int|null $user_id User ID.
 214       * @param bool     $get_data Get data.
 215       */
 216  	public function populate( $id, $user_id = null, $get_data = true ) {
 217          global $wpdb, $userdata;
 218  
 219          if ( empty( $user_id ) ) {
 220              $user_id = isset( $userdata->ID ) ? $userdata->ID : 0;
 221          }
 222  
 223          $field = wp_cache_get( $id, 'bp_xprofile_fields' );
 224          if ( false === $field ) {
 225              $bp = buddypress();
 226  
 227              $field = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->profile->table_name_fields} WHERE id = %d", $id ) );
 228  
 229              if ( ! $field ) {
 230                  return false;
 231              }
 232  
 233              wp_cache_add( $id, $field, 'bp_xprofile_fields' );
 234          }
 235  
 236          $this->fill_data( $field );
 237  
 238          if ( ! empty( $get_data ) && ! empty( $user_id ) ) {
 239              $this->data = $this->get_field_data( $user_id );
 240          }
 241      }
 242  
 243      /**
 244       * Retrieve a `BP_XProfile_Field` instance.
 245       *
 246       * @since 2.4.0
 247       * @since 2.8.0 Added `$user_id` and `$get_data` parameters.
 248       *
 249       * @static
 250       *
 251       * @param int      $field_id ID of the field.
 252       * @param int|null $user_id  Optional. ID of the user associated with the field.
 253       *                           Ignored if `$get_data` is false. If `$get_data` is
 254       *                           true, but no `$user_id` is provided, defaults to
 255       *                           logged-in user ID.
 256       * @param bool     $get_data Whether to fetch data for the specified `$user_id`.
 257       * @return BP_XProfile_Field|false Field object if found, otherwise false.
 258       */
 259  	public static function get_instance( $field_id, $user_id = null, $get_data = true ) {
 260          global $wpdb;
 261  
 262          $field_id = (int) $field_id;
 263          if ( ! $field_id ) {
 264              return false;
 265          }
 266  
 267          return new self( $field_id, $user_id, $get_data );
 268      }
 269  
 270      /**
 271       * Fill object vars based on data passed to the method.
 272       *
 273       * @since 2.4.0
 274       *
 275       * @param array|object $args Array or object representing the `BP_XProfile_Field` properties.
 276       *                           Generally, this is a row from the fields database table.
 277       */
 278  	public function fill_data( $args ) {
 279          if ( is_object( $args ) ) {
 280              $args = (array) $args;
 281          }
 282  
 283          $int_fields = array(
 284              'id', 'is_required', 'group_id', 'parent_id', 'is_default_option',
 285              'field_order', 'option_order', 'can_delete'
 286          );
 287  
 288          foreach ( $args as $k => $v ) {
 289              if ( 'name' === $k || 'description' === $k ) {
 290                  $v = stripslashes( $v );
 291              }
 292  
 293              // Cast numeric strings as integers.
 294              if ( true === in_array( $k, $int_fields ) ) {
 295                  $v = (int) $v;
 296              }
 297  
 298              $this->{$k} = $v;
 299          }
 300  
 301          // Create the field type and store a reference back to this object.
 302          $this->type_obj            = bp_xprofile_create_field_type( $this->type );
 303          $this->type_obj->field_obj = $this;
 304      }
 305  
 306      /**
 307       * Magic getter.
 308       *
 309       * @since 2.4.0
 310       *
 311       * @param string $key Property name.
 312       * @return string|null
 313       */
 314  	public function __get( $key ) {
 315          switch ( $key ) {
 316              case 'default_visibility' :
 317                  return $this->get_default_visibility();
 318                  break;
 319  
 320              case 'allow_custom_visibility' :
 321                  return $this->get_allow_custom_visibility();
 322                  break;
 323          }
 324      }
 325  
 326      /**
 327       * Magic issetter.
 328       *
 329       * @since 2.4.0
 330       *
 331       * @param string $key Property name.
 332       * @return bool
 333       */
 334  	public function __isset( $key ) {
 335          switch ( $key ) {
 336              // Backward compatibility for when these were public methods.
 337              case 'allow_custom_visibility' :
 338              case 'default_visibility' :
 339                  return true;
 340                  break;
 341          }
 342      }
 343  
 344      /**
 345       * Delete a profile field.
 346       *
 347       * @since 1.1.0
 348       *
 349       * @global object $wpdb
 350       *
 351       * @param boolean $delete_data Whether or not to delete data.
 352       * @return boolean
 353       */
 354  	public function delete( $delete_data = false ) {
 355          global $wpdb;
 356  
 357          // Prevent deletion if no ID is present.
 358          // Prevent deletion by url when can_delete is false.
 359          // Prevent deletion of option 1 since this invalidates fields with options.
 360          if ( empty( $this->id ) || empty( $this->can_delete ) || ( $this->parent_id && $this->option_order == 1 ) ) {
 361              return false;
 362          }
 363  
 364          /**
 365           * Fires before the current field instance gets deleted.
 366           *
 367           * @since 3.0.0
 368           *
 369           * @param BP_XProfile_Field $this        Current instance of the field being deleted. Passed by reference.
 370           * @param bool              $delete_data Whether or not to delete data.
 371           */
 372          do_action_ref_array( 'xprofile_field_before_delete', array( &$this, $delete_data ) );
 373  
 374          $bp  = buddypress();
 375          $sql = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE id = %d OR parent_id = %d", $this->id, $this->id );
 376  
 377          if ( ! $wpdb->query( $sql ) ) {
 378              return false;
 379          }
 380  
 381          // Delete all metadata for this field.
 382          bp_xprofile_delete_meta( $this->id, 'field' );
 383  
 384          // Delete the data in the DB for this field.
 385          if ( true === $delete_data ) {
 386              BP_XProfile_ProfileData::delete_for_field( $this->id );
 387          }
 388  
 389          /**
 390           * Fires after the current field instance gets deleted.
 391           *
 392           * @since 3.0.0
 393           *
 394           * @param BP_XProfile_Field $this        Current instance of the field being deleted. Passed by reference.
 395           * @param bool              $delete_data Whether or not to delete data.
 396           */
 397          do_action_ref_array( 'xprofile_field_after_delete', array( &$this, $delete_data ) );
 398  
 399          return true;
 400      }
 401  
 402      /**
 403       * Save a profile field.
 404       *
 405       * @since 1.1.0
 406       *
 407       * @global object $wpdb
 408       *
 409       * @return boolean
 410       */
 411  	public function save() {
 412          global $wpdb;
 413  
 414          $bp = buddypress();
 415  
 416          $this->group_id     = apply_filters( 'xprofile_field_group_id_before_save',     $this->group_id,     $this->id );
 417          $this->parent_id    = apply_filters( 'xprofile_field_parent_id_before_save',    $this->parent_id,    $this->id );
 418          $this->type         = apply_filters( 'xprofile_field_type_before_save',         $this->type,         $this->id );
 419          $this->name         = apply_filters( 'xprofile_field_name_before_save',         $this->name,         $this->id );
 420          $this->description  = apply_filters( 'xprofile_field_description_before_save',  $this->description,  $this->id );
 421          $this->is_required  = apply_filters( 'xprofile_field_is_required_before_save',  $this->is_required,  $this->id );
 422          $this->order_by        = apply_filters( 'xprofile_field_order_by_before_save',     $this->order_by,     $this->id );
 423          $this->field_order  = apply_filters( 'xprofile_field_field_order_before_save',  $this->field_order,  $this->id );
 424          $this->option_order = apply_filters( 'xprofile_field_option_order_before_save', $this->option_order, $this->id );
 425          $this->can_delete   = apply_filters( 'xprofile_field_can_delete_before_save',   $this->can_delete,   $this->id );
 426          $this->type_obj     = bp_xprofile_create_field_type( $this->type );
 427  
 428          /**
 429           * Fires before the current field instance gets saved.
 430           *
 431           * Please use this hook to filter the properties above. Each part will be passed in.
 432           *
 433           * @since 1.0.0
 434           *
 435           * @param BP_XProfile_Field $this Current instance of the field being saved.
 436           */
 437          do_action_ref_array( 'xprofile_field_before_save', array( $this ) );
 438  
 439          $is_new_field = is_null( $this->id );
 440  
 441          if ( ! $is_new_field ) {
 442              $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, is_default_option = %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->is_default_option, $this->id );
 443          } else {
 444              $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, is_default_option ) VALUES ( %d, %d, %s, %s, %s, %d, %s, %d, %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, $this->is_default_option );
 445          }
 446  
 447          /**
 448           * Check for null so field options can be changed without changing any
 449           * other part of the field. The described situation will return 0 here.
 450           */
 451          if ( $wpdb->query( $sql ) !== null ) {
 452  
 453              if ( $is_new_field ) {
 454                  $this->id = $wpdb->insert_id;
 455              }
 456  
 457              // Only do this if we are editing an existing field.
 458              if ( ! $is_new_field ) {
 459  
 460                  /**
 461                   * Remove any radio or dropdown options for this
 462                   * field. They will be re-added if needed.
 463                   * This stops orphan options if the user changes a
 464                   * field from a radio button field to a text box.
 465                   */
 466                  $this->delete_children();
 467              }
 468  
 469              /**
 470               * Check to see if this is a field with child options.
 471               * We need to add the options to the db, if it is.
 472               */
 473              if ( $this->type_obj->supports_options ) {
 474  
 475                  $parent_id = $this->id;
 476  
 477                  // Allow plugins to filter the field's child options (i.e. the items in a selectbox).
 478                  $post_option  = ! empty( $_POST["{$this->type}_option"]           ) ? $_POST["{$this->type}_option"]           : '';
 479                  $post_default = ! empty( $_POST["isDefault_{$this->type}_option"] ) ? $_POST["isDefault_{$this->type}_option"] : '';
 480  
 481                  /**
 482                   * Filters the submitted field option value before saved.
 483                   *
 484                   * @since 1.5.0
 485                   *
 486                   * @param string            $post_option Submitted option value.
 487                   * @param BP_XProfile_Field $type        Current field type being saved for.
 488                   */
 489                  $options      = apply_filters( 'xprofile_field_options_before_save', $post_option,  $this->type );
 490  
 491                  /**
 492                   * Filters the default field option value before saved.
 493                   *
 494                   * @since 1.5.0
 495                   *
 496                   * @param string            $post_default Default option value.
 497                   * @param BP_XProfile_Field $type         Current field type being saved for.
 498                   */
 499                  $defaults     = apply_filters( 'xprofile_field_default_before_save', $post_default, $this->type );
 500  
 501                  $counter = 1;
 502                  if ( !empty( $options ) ) {
 503                      foreach ( (array) $options as $option_key => $option_value ) {
 504                          $is_default = 0;
 505  
 506                          if ( is_array( $defaults ) ) {
 507                              if ( isset( $defaults[ $option_key ] ) ) {
 508                                  $is_default = 1;
 509                              }
 510                          } else {
 511                              if ( (int) $defaults == $option_key ) {
 512                                  $is_default = 1;
 513                              }
 514                          }
 515  
 516                          if ( '' != $option_value ) {
 517                              $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 );
 518                              if ( ! $wpdb->query( $sql ) ) {
 519                                  return false;
 520                              }
 521                          }
 522  
 523                          $counter++;
 524                      }
 525                  }
 526              }
 527  
 528              /**
 529               * Fires after the current field instance gets saved.
 530               *
 531               * @since 1.0.0
 532               *
 533               * @param BP_XProfile_Field $this Current instance of the field being saved.
 534               */
 535              do_action_ref_array( 'xprofile_field_after_save', array( $this ) );
 536  
 537              // Recreate type_obj in case someone changed $this->type via a filter.
 538              $this->type_obj            = bp_xprofile_create_field_type( $this->type );
 539              $this->type_obj->field_obj = $this;
 540  
 541              return $this->id;
 542          } else {
 543              return false;
 544          }
 545      }
 546  
 547      /**
 548       * Get field data for a user ID.
 549       *
 550       * @since 1.2.0
 551       *
 552       * @param int $user_id ID of the user to get field data for.
 553       * @return BP_XProfile_ProfileData
 554       */
 555  	public function get_field_data( $user_id = 0 ) {
 556          return new BP_XProfile_ProfileData( $this->id, $user_id );
 557      }
 558  
 559      /**
 560       * Get all child fields for this field ID.
 561       *
 562       * @since 1.2.0
 563       *
 564       * @global BuddyPress $bp The one true BuddyPress instance.
 565       * @global wpdb $wpdb WordPress database object.
 566       *
 567       * @param bool $for_editing Whether or not the field is for editing. Default to false.
 568       * @return array
 569       */
 570  	public function get_children( $for_editing = false ) {
 571          global $wpdb;
 572  
 573          // This is done here so we don't have problems with sql injection.
 574          if ( empty( $for_editing ) && in_array( $this->order_by, array( 'asc', 'desc' ), true ) ) {
 575              $sort_sql = sprintf( 'ORDER BY name %s', bp_esc_sql_order( $this->order_by ) );
 576          } else {
 577              $sort_sql = 'ORDER BY option_order ASC';
 578          }
 579  
 580          // This eliminates a problem with getting all fields when there is no
 581          // id for the object.
 582          if ( empty( $this->id ) ) {
 583              $parent_id = -1;
 584          } else {
 585              $parent_id = $this->id;
 586          }
 587  
 588          $bp       = buddypress();
 589          $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 );
 590          $children = $wpdb->get_results( $sql );
 591  
 592          /**
 593           * Filters the found children for a field.
 594           *
 595           * @since 1.2.5
 596           * @since 3.0.0 Added the `$this` parameter.
 597           *
 598           * @param array             $children     Found children for a field.
 599           * @param bool              $for_editing  Whether or not the field is for editing.
 600           * @param BP_XProfile_Field $field_object BP_XProfile_Field Field object.
 601           */
 602          return apply_filters( 'bp_xprofile_field_get_children', $children, $for_editing, $this );
 603      }
 604  
 605      /**
 606       * Delete all field children for this field.
 607       *
 608       * @since 1.2.0
 609       *
 610       * @global object $wpdb
 611       */
 612  	public function delete_children() {
 613          global $wpdb;
 614  
 615          $bp  = buddypress();
 616          $sql = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE parent_id = %d", $this->id );
 617  
 618          $wpdb->query( $sql );
 619      }
 620  
 621      /**
 622       * Gets the member types to which this field should be available.
 623       *
 624       * Will not return inactive member types, even if associated metadata is found.
 625       *
 626       * 'null' is a special pseudo-type, which represents users that do not have a member type.
 627       *
 628       * @since 2.4.0
 629       *
 630       * @return array Array of member type names.
 631       */
 632  	public function get_member_types() {
 633          if ( ! is_null( $this->member_types ) ) {
 634              return $this->member_types;
 635          }
 636  
 637          $raw_types = bp_xprofile_get_meta( $this->id, 'field', 'member_type', false );
 638  
 639          // If `$raw_types` is not an array, it probably means this is a new field (id=0).
 640          if ( ! is_array( $raw_types ) ) {
 641              $raw_types = array();
 642          }
 643  
 644          // If '_none' is found in the array, it overrides all types.
 645          $types = array();
 646          if ( ! in_array( '_none', $raw_types ) ) {
 647              $registered_types = bp_get_member_types();
 648  
 649              // Eliminate invalid member types saved in the database.
 650              foreach ( $raw_types as $raw_type ) {
 651                  // 'null' is a special case - it represents users without a type.
 652                  if ( 'null' === $raw_type || isset( $registered_types[ $raw_type ] ) ) {
 653                      $types[] = $raw_type;
 654                  }
 655              }
 656  
 657              // If no member types have been saved, interpret as *all* member types.
 658              if ( empty( $types ) ) {
 659                  $types = array_values( $registered_types );
 660  
 661                  // + the "null" type, ie users without a type.
 662                  $types[] = 'null';
 663              }
 664          }
 665  
 666          /**
 667           * Filters the member types to which an XProfile object should be applied.
 668           *
 669           * @since 2.4.0
 670           *
 671           * @param array             $types Member types.
 672           * @param BP_XProfile_Field $field Field object.
 673           */
 674          $this->member_types = apply_filters( 'bp_xprofile_field_member_types', $types, $this );
 675  
 676          return $this->member_types;
 677      }
 678  
 679      /**
 680       * Sets the member types for this field.
 681       *
 682       * @since 2.4.0
 683       *
 684       * @param array $member_types Array of member types. Can include 'null' (users with no type) in addition to any
 685       *                            registered types.
 686       * @param bool  $append       Whether to append to existing member types. If false, all existing member type
 687       *                            associations will be deleted before adding your `$member_types`. Default false.
 688       * @return array Member types for the current field, after being saved.
 689       */
 690  	public function set_member_types( $member_types, $append = false ) {
 691          // Unset invalid member types.
 692          $types = array();
 693          foreach ( $member_types as $member_type ) {
 694              // 'null' is a special case - it represents users without a type.
 695              if ( 'null' === $member_type || bp_get_member_type_object( $member_type ) ) {
 696                  $types[] = $member_type;
 697              }
 698          }
 699  
 700          // When `$append` is false, delete all existing types before adding new ones.
 701          if ( ! $append ) {
 702              bp_xprofile_delete_meta( $this->id, 'field', 'member_type' );
 703  
 704              /*
 705               * We interpret an empty array as disassociating the field from all types. This is
 706               * represented internally with the '_none' flag.
 707               */
 708              if ( empty( $types ) ) {
 709                  bp_xprofile_add_meta( $this->id, 'field', 'member_type', '_none' );
 710              }
 711          }
 712  
 713          /*
 714           * Unrestricted fields are represented in the database as having no 'member_type'.
 715           * We detect whether a field is being set to unrestricted by checking whether the
 716           * list of types passed to the method is the same as the list of registered types,
 717           * plus the 'null' pseudo-type.
 718           */
 719          $_rtypes  = bp_get_member_types();
 720          $rtypes   = array_values( $_rtypes );
 721          $rtypes[] = 'null';
 722  
 723          sort( $types );
 724          sort( $rtypes );
 725  
 726          // Only save if this is a restricted field.
 727          if ( $types !== $rtypes ) {
 728              // Save new types.
 729              foreach ( $types as $type ) {
 730                  bp_xprofile_add_meta( $this->id, 'field', 'member_type', $type );
 731              }
 732          }
 733  
 734          // Reset internal cache of member types.
 735          $this->member_types = null;
 736  
 737          /**
 738           * Fires after a field's member types have been updated.
 739           *
 740           * @since 2.4.0
 741           *
 742           * @param BP_XProfile_Field $this Field object.
 743           */
 744          do_action( 'bp_xprofile_field_set_member_type', $this );
 745  
 746          // Refetch fresh items from the database.
 747          return $this->get_member_types();
 748      }
 749  
 750      /**
 751       * Gets a label representing the field's member types.
 752       *
 753       * This label is displayed alongside the field's name on the Profile Fields Dashboard panel.
 754       *
 755       * @since 2.4.0
 756       *
 757       * @return string
 758       */
 759  	public function get_member_type_label() {
 760          // Field 1 is always displayed to everyone, so never gets a label.
 761          if ( 1 == $this->id ) {
 762              return '';
 763          }
 764  
 765          // Return an empty string if no member types are registered.
 766          $all_types = bp_get_member_types();
 767          if ( empty( $all_types ) ) {
 768              return '';
 769          }
 770  
 771          $member_types = $this->get_member_types();
 772  
 773          // If the field applies to all member types, show no message.
 774          $all_types[] = 'null';
 775          if ( array_values( $all_types ) == $member_types ) {
 776              return '';
 777          }
 778  
 779          $label = '';
 780          if ( ! empty( $member_types ) ) {
 781              $has_null = false;
 782              $member_type_labels = array();
 783              foreach ( $member_types as $member_type ) {
 784                  if ( 'null' === $member_type ) {
 785                      $has_null = true;
 786                      continue;
 787                  } else {
 788                      $mt_obj = bp_get_member_type_object( $member_type );
 789                      $member_type_labels[] = $mt_obj->labels['name'];
 790                  }
 791              }
 792  
 793              // Alphabetical sort.
 794              natcasesort( $member_type_labels );
 795              $member_type_labels = array_values( $member_type_labels );
 796  
 797              // Add the 'null' option to the end of the list.
 798              if ( $has_null ) {
 799                  $member_type_labels[] = __( 'Users with no member type', 'buddypress' );
 800              }
 801  
 802              /* translators: %s: comma separated list of member types */
 803              $label = sprintf( __( '(Member types: %s)', 'buddypress' ), implode( ', ', array_map( 'esc_html', $member_type_labels ) ) );
 804          } else {
 805              $label = '<span class="member-type-none-notice">' . __( '(Unavailable to all members)', 'buddypress' ) . '</span>';
 806          }
 807  
 808          return $label;
 809      }
 810  
 811      /**
 812       * Get the field's default visibility setting.
 813       *
 814       * Lazy-loaded to reduce overhead.
 815       *
 816       * Defaults to 'public' if no visibility setting is found in the database.
 817       *
 818       * @since 2.4.0
 819       *
 820       * @return string
 821       */
 822  	public function get_default_visibility() {
 823          if ( ! isset( $this->default_visibility ) ) {
 824              $this->default_visibility = 'public';
 825              $this->visibility         = '';
 826  
 827              if ( isset( $this->type_obj->visibility ) && $this->type_obj->visibility ) {
 828                  $this->visibility = $this->type_obj->visibility;
 829              }
 830  
 831              if ( $this->field_type_supports( 'allow_custom_visibility' ) ) {
 832                  $this->visibility = bp_xprofile_get_meta( $this->id, 'field', 'default_visibility' );
 833              }
 834  
 835              if ( $this->visibility ) {
 836                  $this->default_visibility = $this->visibility;
 837              }
 838          }
 839  
 840          return $this->default_visibility;
 841      }
 842  
 843      /**
 844       * Get whether the field's default visibility can be overridden by users.
 845       *
 846       * Lazy-loaded to reduce overhead.
 847       *
 848       * Defaults to 'allowed'.
 849       *
 850       * @since 4.4.0
 851       *
 852       * @return string 'disabled' or 'allowed'.
 853       */
 854  	public function get_allow_custom_visibility() {
 855          if ( ! isset( $this->allow_custom_visibility ) ) {
 856              $allow_custom_visibility = bp_xprofile_get_meta( $this->id, 'field', 'allow_custom_visibility' );
 857  
 858              if ( 'disabled' === $allow_custom_visibility ) {
 859                  $this->allow_custom_visibility = 'disabled';
 860              } else {
 861                  $this->allow_custom_visibility = 'allowed';
 862              }
 863          }
 864  
 865          return $this->allow_custom_visibility;
 866      }
 867  
 868      /**
 869       * Get the field's signup position.
 870       *
 871       * @since 8.0.0
 872       *
 873       * @return int the field's signup position.
 874       *             0 if the field has not been added to the signup form.
 875       */
 876  	public function get_signup_position() {
 877          if ( ! isset( $this->signup_position ) ) {
 878              $this->signup_position = (int) bp_xprofile_get_meta( $this->id, 'field', 'signup_position' );
 879          }
 880  
 881          return $this->signup_position;
 882      }
 883  
 884      /**
 885       * Get whether the field values should be auto-linked to a directory search.
 886       *
 887       * Lazy-loaded to reduce overhead.
 888       *
 889       * Defaults to true for multi and default fields, false for single fields.
 890       *
 891       * @since 2.5.0
 892       *
 893       * @return bool
 894       */
 895  	public function get_do_autolink() {
 896          if ( ! isset( $this->do_autolink ) ) {
 897              $do_autolink = bp_xprofile_get_meta( $this->id, 'field', 'do_autolink' );
 898  
 899              if ( '' === $do_autolink ) {
 900                  $this->do_autolink = $this->type_obj->supports_options;
 901              } else {
 902                  $this->do_autolink = 'on' === $do_autolink;
 903              }
 904          }
 905  
 906          /**
 907           * Filters the autolink property of the field.
 908           *
 909           * @since 6.0.0
 910           *
 911           * @param bool              $do_autolink The autolink property of the field.
 912           * @param BP_XProfile_Field $this Field object.
 913           */
 914          return apply_filters( 'bp_xprofile_field_do_autolink', $this->do_autolink, $this );
 915      }
 916  
 917      /* Static Methods ********************************************************/
 918  
 919      /**
 920       * Get the type for provided field ID.
 921       *
 922       * @param int $field_id Field ID to get type of.
 923       * @return bool|null|string
 924       */
 925  	public static function get_type( $field_id = 0 ) {
 926          global $wpdb;
 927  
 928          // Bail if no field ID.
 929          if ( empty( $field_id ) ) {
 930              return false;
 931          }
 932  
 933          $bp   = buddypress();
 934          $sql  = $wpdb->prepare( "SELECT type FROM {$bp->profile->table_name_fields} WHERE id = %d", $field_id );
 935          $type = $wpdb->get_var( $sql );
 936  
 937          // Return field type.
 938          if ( ! empty( $type ) ) {
 939              return $type;
 940          }
 941  
 942          return false;
 943      }
 944  
 945      /**
 946       * Delete all fields in a field group.
 947       *
 948       * @since 1.2.0
 949       *
 950       * @global object $wpdb
 951       *
 952       * @param int $group_id ID of the field group to delete fields from.
 953       * @return boolean
 954       */
 955  	public static function delete_for_group( $group_id = 0 ) {
 956          global $wpdb;
 957  
 958          // Bail if no group ID.
 959          if ( empty( $group_id ) ) {
 960              return false;
 961          }
 962  
 963          $bp      = buddypress();
 964          $sql     = $wpdb->prepare( "DELETE FROM {$bp->profile->table_name_fields} WHERE group_id = %d", $group_id );
 965          $deleted = $wpdb->get_var( $sql );
 966  
 967          // Return true if fields were deleted.
 968          if ( false !== $deleted ) {
 969              return true;
 970          }
 971  
 972          return false;
 973      }
 974  
 975      /**
 976       * Get field ID from field name.
 977       *
 978       * @since 1.5.0
 979       *
 980       * @global object $wpdb
 981       *
 982       * @param string $field_name Name of the field to query the ID for.
 983       * @return int|null Field ID on success; null on failure.
 984       */
 985  	public static function get_id_from_name( $field_name = '' ) {
 986          global $wpdb;
 987  
 988          $bp = buddypress();
 989  
 990          if ( empty( $bp->profile->table_name_fields ) || empty( $field_name ) ) {
 991              return false;
 992          }
 993  
 994          $id = bp_core_get_incremented_cache( $field_name, 'bp_xprofile_fields_by_name' );
 995          if ( false === $id ) {
 996              $sql = $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s AND parent_id = 0", $field_name );
 997              $id = $wpdb->get_var( $sql );
 998              bp_core_set_incremented_cache( $field_name, 'bp_xprofile_fields_by_name', $id );
 999          }
1000  
1001          return is_numeric( $id ) ? (int) $id : $id;
1002      }
1003  
1004      /**
1005       * Update field position and/or field group when relocating.
1006       *
1007       * @since 1.5.0
1008       *
1009       * @global object $wpdb
1010       *
1011       * @param int      $field_id       ID of the field to update.
1012       * @param int|null $position       Field position to update.
1013       * @param int|null $field_group_id ID of the field group.
1014       * @return boolean
1015       */
1016  	public static function update_position( $field_id, $position = null, $field_group_id = null ) {
1017          global $wpdb;
1018  
1019          // Bail if invalid position or field group.
1020          if ( ! is_numeric( $position ) || ! is_numeric( $field_group_id ) ) {
1021              return false;
1022          }
1023  
1024          // Get table name and field parent.
1025          $table_name = buddypress()->profile->table_name_fields;
1026          $sql        = $wpdb->prepare( "UPDATE {$table_name} SET field_order = %d, group_id = %d WHERE id = %d", $position, $field_group_id, $field_id );
1027          $parent     = $wpdb->query( $sql );
1028  
1029          $retval = false;
1030  
1031          // Update $field_id with new $position and $field_group_id.
1032          if ( ! empty( $parent ) && ! is_wp_error( $parent ) ) {
1033  
1034              // Update any children of this $field_id.
1035              $sql = $wpdb->prepare( "UPDATE {$table_name} SET group_id = %d WHERE parent_id = %d", $field_group_id, $field_id );
1036              $wpdb->query( $sql );
1037  
1038              // Invalidate profile field and group query cache.
1039              wp_cache_delete( $field_id, 'bp_xprofile_fields' );
1040  
1041              $retval = $parent;
1042          }
1043  
1044          bp_core_reset_incrementor( 'bp_xprofile_groups' );
1045  
1046          return $retval;
1047      }
1048  
1049      /**
1050       * Gets the IDs of fields applicable for a given member type or array of member types.
1051       *
1052       * @since 2.4.0
1053       *
1054       * @param string|array $member_types Member type or array of member types. Use 'any' to return unrestricted
1055       *                                   fields (those available for anyone, regardless of member type).
1056       * @return array Multi-dimensional array, with field IDs as top-level keys, and arrays of member types
1057       *               associated with each field as values.
1058       */
1059  	public static function get_fields_for_member_type( $member_types ) {
1060          global $wpdb;
1061  
1062          $fields = array();
1063  
1064          if ( empty( $member_types ) ) {
1065              $member_types = array( 'any' );
1066          } elseif ( ! is_array( $member_types ) ) {
1067              $member_types = array( $member_types );
1068          }
1069  
1070          $bp = buddypress();
1071  
1072          // Pull up all recorded field member type data.
1073          $mt_meta = wp_cache_get( 'field_member_types', 'bp_xprofile' );
1074          if ( false === $mt_meta ) {
1075              $mt_meta = $wpdb->get_results( "SELECT object_id, meta_value FROM {$bp->profile->table_name_meta} WHERE meta_key = 'member_type' AND object_type = 'field'" );
1076              wp_cache_set( 'field_member_types', $mt_meta, 'bp_xprofile' );
1077          }
1078  
1079          // Keep track of all fields with recorded member_type metadata.
1080          $all_recorded_field_ids = wp_list_pluck( $mt_meta, 'object_id' );
1081  
1082          // Sort member_type matches in arrays, keyed by field_id.
1083          foreach ( $mt_meta as $_mt_meta ) {
1084              if ( ! isset( $fields[ $_mt_meta->object_id ] ) ) {
1085                  $fields[ $_mt_meta->object_id ] = array();
1086              }
1087  
1088              $fields[ $_mt_meta->object_id ][] = $_mt_meta->meta_value;
1089          }
1090  
1091          /*
1092           * Filter out fields that don't match any passed types, or those marked '_none'.
1093           * The 'any' type is implicitly handled here: it will match no types.
1094           */
1095          foreach ( $fields as $field_id => $field_types ) {
1096              if ( ! array_intersect( $field_types, $member_types ) ) {
1097                  unset( $fields[ $field_id ] );
1098              }
1099          }
1100  
1101          // Any fields with no member_type metadata are available to all member types.
1102          if ( ! in_array( '_none', $member_types ) ) {
1103              if ( ! empty( $all_recorded_field_ids ) ) {
1104                  $all_recorded_field_ids_sql = implode( ',', array_map( 'absint', $all_recorded_field_ids ) );
1105                  $unrestricted_field_ids = $wpdb->get_col( "SELECT id FROM {$bp->profile->table_name_fields} WHERE id NOT IN ({$all_recorded_field_ids_sql})" );
1106              } else {
1107                  $unrestricted_field_ids = $wpdb->get_col( "SELECT id FROM {$bp->profile->table_name_fields}" );
1108              }
1109  
1110              // Append the 'null' pseudo-type.
1111              $all_member_types   = bp_get_member_types();
1112              $all_member_types   = array_values( $all_member_types );
1113              $all_member_types[] = 'null';
1114  
1115              foreach ( $unrestricted_field_ids as $unrestricted_field_id ) {
1116                  $fields[ $unrestricted_field_id ] = $all_member_types;
1117              }
1118          }
1119  
1120          return $fields;
1121      }
1122  
1123      /**
1124       * Validate form field data on submission.
1125       *
1126       * @since 2.2.0
1127       *
1128       * @global $message
1129       *
1130       * @return boolean
1131       */
1132  	public static function admin_validate() {
1133          global $message;
1134  
1135          // Check field name.
1136          if ( ! isset( $_POST['title'] ) || ( '' === $_POST['title'] ) ) {
1137              $message = esc_html__( 'Profile fields must have a name.', 'buddypress' );
1138              return false;
1139          }
1140  
1141          // Check field requirement.
1142          if ( ! isset( $_POST['required'] ) ) {
1143              $message = esc_html__( 'Profile field requirement is missing.', 'buddypress' );
1144              return false;
1145          }
1146  
1147          // Check field type.
1148          if ( empty( $_POST['fieldtype'] ) ) {
1149              $message = esc_html__( 'Profile field type is missing.', 'buddypress' );
1150              return false;
1151          }
1152  
1153          // Check that field is of valid type.
1154          if ( ! in_array( $_POST['fieldtype'], array_keys( bp_xprofile_get_field_types() ), true ) ) {
1155              /* translators: %s: field type name */
1156              $message = sprintf( esc_html__( 'The profile field type %s is not registered.', 'buddypress' ), '<code>' . esc_attr( $_POST['fieldtype'] ) . '</code>' );
1157              return false;
1158          }
1159  
1160          // Get field type so we can check for and validate any field options.
1161          $field_type = bp_xprofile_create_field_type( $_POST['fieldtype'] );
1162  
1163          // Field type requires options.
1164          if ( true === $field_type->supports_options ) {
1165  
1166              // Build the field option key.
1167              $option_name = sanitize_key( $_POST['fieldtype'] ) . '_option';
1168  
1169              // Check for missing or malformed options.
1170              if ( empty( $_POST[ $option_name ] ) || ! is_array( $_POST[ $option_name ] ) ) {
1171                  $message = esc_html__( 'These field options are invalid.', 'buddypress' );
1172                  return false;
1173              }
1174  
1175              // Trim out empty field options.
1176              $field_values  = array_values( $_POST[ $option_name ] );
1177              $field_options = array_map( 'sanitize_text_field', $field_values );
1178              $field_count   = count( $field_options );
1179  
1180              // Check for missing or malformed options.
1181              if ( 0 === $field_count ) {
1182                  /* translators: %s: field type name */
1183                  $message = sprintf( esc_html__( '%s require at least one option.', 'buddypress' ), $field_type->name );
1184                  return false;
1185              }
1186  
1187              // If only one option exists, it cannot be an empty string.
1188              if ( ( 1 === $field_count ) && ( '' === $field_options[0] ) ) {
1189                  /* translators: %s: field type name */
1190                  $message = sprintf( esc_html__( '%s require at least one option.', 'buddypress' ), $field_type->name );
1191                  return false;
1192              }
1193          }
1194  
1195          return true;
1196      }
1197  
1198      /**
1199       * Save miscellaneous settings for this field.
1200       *
1201       * Some field types have type-specific settings, which are saved here.
1202       *
1203       * @since 2.7.0
1204       *
1205       * @param array $settings Array of settings.
1206       */
1207  	public function admin_save_settings( $settings ) {
1208          return $this->type_obj->admin_save_settings( $this->id, $settings );
1209      }
1210  
1211      /**
1212       * Populates the items for radio buttons, checkboxes, and dropdown boxes.
1213       */
1214  	public function render_admin_form_children() {
1215          foreach ( array_keys( bp_xprofile_get_field_types() ) as $field_type ) {
1216              $type_obj = bp_xprofile_create_field_type( $field_type );
1217              $type_obj->admin_new_field_html( $this );
1218          }
1219      }
1220  
1221      /**
1222       * Oupput the admin form for this field.
1223       *
1224       * @since 1.9.0
1225       *
1226       * @param string $message Message to display.
1227       */
1228  	public function render_admin_form( $message = '' ) {
1229  
1230          // Users Admin URL.
1231          $users_url = bp_get_admin_url( 'users.php' );
1232  
1233          // Add New.
1234          if ( empty( $this->id ) ) {
1235              $title  = __( 'Add New Field', 'buddypress' );
1236              $button    = __( 'Save',          'buddypress' );
1237              $action = add_query_arg( array(
1238                  'page'     => 'bp-profile-setup',
1239                  'mode'     => 'add_field',
1240                  'group_id' => (int) $this->group_id
1241              ), $users_url . '#tabs-' . (int) $this->group_id );
1242  
1243              if ( !empty( $_POST['saveField'] ) ) {
1244                  $this->name        = $_POST['title'];
1245                  $this->description = $_POST['description'];
1246                  $this->is_required = $_POST['required'];
1247                  $this->type        = $_POST['fieldtype'];
1248                  $this->field_order = $_POST['field_order'];
1249  
1250                  if ( ! empty( $_POST["sort_order_{$this->type}"] ) ) {
1251                      $this->order_by = $_POST["sort_order_{$this->type}"];
1252                  }
1253              }
1254  
1255          // Edit.
1256          } else {
1257              $title  = __( 'Edit Field', 'buddypress' );
1258              $button    = __( 'Update',     'buddypress' );
1259              $action = add_query_arg( array(
1260                  'page'     => 'bp-profile-setup',
1261                  'mode'     => 'edit_field',
1262                  'group_id' => (int) $this->group_id,
1263                  'field_id' => (int) $this->id
1264              ), $users_url . '#tabs-' . (int) $this->group_id );
1265          } ?>
1266  
1267          <div class="wrap">
1268  
1269              <h1 class="wp-heading-inline"><?php echo esc_html( $title ); ?></h1>
1270              <hr class="wp-header-end">
1271  
1272              <?php if ( !empty( $message ) ) : ?>
1273  
1274                  <div id="message" class="error fade notice is-dismissible">
1275                      <p><?php echo esc_html( $message ); ?></p>
1276                  </div>
1277  
1278              <?php endif; ?>
1279  
1280              <form id="bp-xprofile-add-field" action="<?php echo esc_url( $action ); ?>" method="post">
1281                  <div id="poststuff">
1282                      <div id="post-body" class="metabox-holder columns-<?php echo ( 1 == get_current_screen()->get_columns() ) ? '1' : '2'; ?>">
1283                          <div id="post-body-content">
1284  
1285                              <?php
1286  
1287                              // Output the name & description fields.
1288                              $this->name_and_description(); ?>
1289  
1290                          </div><!-- #post-body-content -->
1291  
1292                          <div id="postbox-container-1" class="postbox-container">
1293  
1294                              <?php
1295  
1296                              // Output the sumbit metabox.
1297                              $this->submit_metabox( $button );
1298  
1299                              // Output the required metabox.
1300                              $this->required_metabox();
1301  
1302                              // Output signup position metabox.
1303                              $this->signup_position_metabox();
1304  
1305                              // Output the Member Types metabox.
1306                              $this->member_type_metabox();
1307  
1308                              // Output the field visibility metaboxes.
1309                              $this->visibility_metabox();
1310  
1311                              // Output the autolink metabox.
1312                              $this->autolink_metabox();
1313  
1314  
1315                              /**
1316                               * Fires after XProfile Field sidebar metabox.
1317                               *
1318                               * @since 2.2.0
1319                               *
1320                               * @param BP_XProfile_Field $this Current XProfile field.
1321                               */
1322                              do_action( 'xprofile_field_after_sidebarbox', $this ); ?>
1323  
1324                          </div>
1325  
1326                          <div id="postbox-container-2" class="postbox-container">
1327  
1328                              <?php
1329  
1330                              /**
1331                               * Fires before XProfile Field content metabox.
1332                               *
1333                               * @since 2.3.0
1334                               *
1335                               * @param BP_XProfile_Field $this Current XProfile field.
1336                               */
1337                              do_action( 'xprofile_field_before_contentbox', $this );
1338  
1339                              // Output the field attributes metabox.
1340                              $this->type_metabox();
1341  
1342                              // Output hidden inputs for default field.
1343                              $this->default_field_hidden_inputs();
1344  
1345                              /**
1346                               * Fires after XProfile Field content metabox.
1347                               *
1348                               * @since 2.2.0
1349                               *
1350                               * @param BP_XProfile_Field $this Current XProfile field.
1351                               */
1352                              do_action( 'xprofile_field_after_contentbox', $this ); ?>
1353  
1354                          </div>
1355                      </div><!-- #post-body -->
1356                  </div><!-- #poststuff -->
1357              </form>
1358          </div>
1359  
1360      <?php
1361      }
1362  
1363      /**
1364       * Gets field type supports.
1365       *
1366       * @since 8.0.0
1367       *
1368       * @return bool[] Supported features.
1369       */
1370  	public function get_field_type_supports() {
1371          $supports = array(
1372              'switch_fieldtype'        => true,
1373              'required'                => true,
1374              'do_autolink'             => true,
1375              'allow_custom_visibility' => true,
1376              'member_types'            => true,
1377              'signup_position'         => true,
1378          );
1379  
1380          if ( isset( $this->type_obj ) && $this->type_obj ) {
1381              $field_type = $this->type_obj;
1382  
1383              if ( isset( $field_type::$supported_features ) ) {
1384                  $supports = array_merge( $supports, $field_type::$supported_features );
1385              }
1386          }
1387  
1388          return $supports;
1389      }
1390  
1391      /**
1392       * Checks whether the field type supports the requested feature.
1393       *
1394       * @since 8.0.0
1395       *
1396       * @param string $support The name of the feature.
1397       * @return boolean True if the field type supports the feature. False otherwise.
1398       */
1399  	public function field_type_supports( $support = '' ) {
1400          $retval   = true;
1401          $features = $this->get_field_type_supports();
1402  
1403          if ( isset( $features[ $support ] ) ) {
1404              $retval = $features[ $support ];
1405          }
1406  
1407          return $retval;
1408      }
1409  
1410      /**
1411       * Private method used to display the submit metabox.
1412       *
1413       * @since 2.3.0
1414       *
1415       * @param string $button_text Text to put on button.
1416       */
1417  	private function submit_metabox( $button_text = '' ) {
1418  
1419          // Setup the URL for deleting
1420          $users_url  = bp_get_admin_url( 'users.php' );
1421          $cancel_url = add_query_arg( array(
1422              'page' => 'bp-profile-setup'
1423          ), $users_url );
1424  
1425  
1426          // Delete.
1427          if ( $this->can_delete ) {
1428              $delete_url = wp_nonce_url( add_query_arg( array(
1429                  'page'     => 'bp-profile-setup',
1430                  'mode'     => 'delete_field',
1431                  'field_id' => (int) $this->id
1432              ), $users_url ), 'bp_xprofile_delete_field-' . $this->id, 'bp_xprofile_delete_field' );
1433          }
1434          /**
1435           * Fires before XProfile Field submit metabox.
1436           *
1437           * @since 2.1.0
1438           *
1439           * @param BP_XProfile_Field $this Current XProfile field.
1440           */
1441          do_action( 'xprofile_field_before_submitbox', $this ); ?>
1442  
1443          <div id="submitdiv" class="postbox">
1444              <h2><?php esc_html_e( 'Submit', 'buddypress' ); ?></h2>
1445              <div class="inside">
1446                  <div id="submitcomment" class="submitbox">
1447                      <div id="major-publishing-actions">
1448  
1449                          <?php
1450  
1451                          /**
1452                           * Fires at the beginning of the XProfile Field publishing actions section.
1453                           *
1454                           * @since 2.1.0
1455                           *
1456                           * @param BP_XProfile_Field $this Current XProfile field.
1457                           */
1458                          do_action( 'xprofile_field_submitbox_start', $this ); ?>
1459  
1460                          <input type="hidden" name="field_order" id="field_order" value="<?php echo esc_attr( $this->field_order ); ?>" />
1461  
1462                          <?php if ( ! empty( $button_text ) ) : ?>
1463  
1464                              <div id="publishing-action">
1465                                  <input type="submit" name="saveField" value="<?php echo esc_attr( $button_text ); ?>" class="button-primary" />
1466                              </div>
1467  
1468                          <?php endif; ?>
1469  
1470                          <div id="delete-action">
1471                              <?php if ( ! empty( $this->id ) && isset( $delete_url ) ) : ?>
1472                                  <a href="<?php echo esc_url( $delete_url ); ?>" class="submitdelete deletion"><?php esc_html_e( 'Delete', 'buddypress' ); ?></a>
1473                              <?php endif; ?>
1474  
1475                              <div><a href="<?php echo esc_url( $cancel_url ); ?>" class="deletion"><?php esc_html_e( 'Cancel', 'buddypress' ); ?></a></div>
1476                          </div>
1477  
1478                          <?php wp_nonce_field( 'xprofile_delete_option' ); ?>
1479  
1480                          <div class="clear"></div>
1481                      </div>
1482                  </div>
1483              </div>
1484          </div>
1485  
1486          <?php
1487  
1488          /**
1489           * Fires after XProfile Field submit metabox.
1490           *
1491           * @since 2.1.0
1492           *
1493           * @param BP_XProfile_Field $this Current XProfile field.
1494           */
1495          do_action( 'xprofile_field_after_submitbox', $this );
1496      }
1497  
1498      /**
1499       * Private method used to output field name and description fields.
1500       *
1501       * @since 2.3.0
1502       */
1503  	private function name_and_description() {
1504      ?>
1505  
1506          <div id="titlediv">
1507              <div class="titlewrap">
1508                  <label id="title-prompt-text" for="title"><?php echo esc_html_x( 'Name (required)', 'XProfile admin edit field', 'buddypress' ); ?></label>
1509                  <input type="text" name="title" id="title" value="<?php echo esc_attr( $this->name ); ?>" autocomplete="off" />
1510              </div>
1511          </div>
1512  
1513          <div class="postbox">
1514              <h2><?php echo esc_html_x( 'Description', 'XProfile admin edit field', 'buddypress' ); ?></h2>
1515              <div class="inside">
1516                  <label for="description" class="screen-reader-text"><?php
1517                      /* translators: accessibility text */
1518                      esc_html_e( 'Add description', 'buddypress' );
1519                  ?></label>
1520                  <textarea name="description" id="description" rows="8" cols="60"><?php echo esc_textarea( $this->description ); ?></textarea>
1521              </div>
1522          </div>
1523  
1524      <?php
1525      }
1526  
1527      /**
1528       * Private method used to output field Member Type metabox.
1529       *
1530       * @since 2.4.0
1531       *
1532       * @return void If default field or if the field does not support the feature.
1533       */
1534  	private function member_type_metabox() {
1535  
1536          // The primary field is for all, so bail.
1537          if ( true === $this->is_default_field() || ! $this->field_type_supports( 'member_types' ) ) {
1538              return;
1539          }
1540  
1541          // Bail when no member types are registered.
1542          if ( ! $member_types = bp_get_member_types( array(), 'objects' ) ) {
1543              return;
1544          }
1545  
1546          $field_member_types = $this->get_member_types();
1547  
1548          ?>
1549  
1550          <div id="field-type-member-types" class="postbox">
1551              <h2><?php _e( 'Member Types', 'buddypress' ); ?></h2>
1552              <div class="inside">
1553                  <p class="description"><?php _e( 'This field should be available to:', 'buddypress' ); ?></p>
1554  
1555                  <ul>
1556                      <?php foreach ( $member_types as $member_type ) : ?>
1557                      <li>
1558                          <label for="member-type-<?php echo $member_type->labels['name']; ?>">
1559                              <input name="member-types[]" id="member-type-<?php echo $member_type->labels['name']; ?>" class="member-type-selector" type="checkbox" value="<?php echo $member_type->name; ?>" <?php checked( in_array( $member_type->name, $field_member_types ) ); ?>/>
1560                              <?php echo $member_type->labels['name']; ?>
1561                          </label>
1562                      </li>
1563                      <?php endforeach; ?>
1564  
1565                      <li>
1566                          <label for="member-type-none">
1567                              <input name="member-types[]" id="member-type-none" class="member-type-selector" type="checkbox" value="null" <?php checked( in_array( 'null', $field_member_types ) ); ?>/>
1568                              <?php _e( 'Users with no member type', 'buddypress' ); ?>
1569                          </label>
1570                      </li>
1571  
1572                  </ul>
1573                  <p class="description member-type-none-notice<?php if ( ! empty( $field_member_types ) ) : ?> hide<?php endif; ?>"><?php _e( 'Unavailable to all members.', 'buddypress' ) ?></p>
1574              </div>
1575  
1576              <input type="hidden" name="has-member-types" value="1" />
1577          </div>
1578  
1579          <?php
1580      }
1581  
1582      /**
1583       * Private method used to output field visibility metaboxes.
1584       *
1585       * @since 2.3.0
1586       *
1587       * @return void If default field or if the field does not support the feature.
1588       */
1589  	private function visibility_metabox() {
1590  
1591          // Default field and field types not supporting the feature cannot have custom visibility.
1592          if ( true === $this->is_default_field() || ! $this->field_type_supports( 'allow_custom_visibility' ) ) {
1593              return;
1594          } ?>
1595  
1596          <div class="postbox" id="field-type-visibiliy-metabox">
1597              <h2><label for="default-visibility"><?php esc_html_e( 'Visibility', 'buddypress' ); ?></label></h2>
1598              <div class="inside">
1599                  <div>
1600                      <select name="default-visibility" id="default-visibility">
1601  
1602                          <?php foreach( bp_xprofile_get_visibility_levels() as $level ) : ?>
1603  
1604                              <option value="<?php echo esc_attr( $level['id'] ); ?>" <?php selected( $this->get_default_visibility(), $level['id'] ); ?>>
1605                                  <?php echo esc_html( $level['label'] ); ?>
1606                              </option>
1607  
1608                          <?php endforeach ?>
1609  
1610                      </select>
1611                  </div>
1612  
1613                  <div>
1614                      <ul>
1615                          <li>
1616                              <input type="radio" id="allow-custom-visibility-allowed" name="allow-custom-visibility" value="allowed" <?php checked( $this->get_allow_custom_visibility(), 'allowed' ); ?> />
1617                              <label for="allow-custom-visibility-allowed"><?php esc_html_e( 'Allow members to override', 'buddypress' ); ?></label>
1618                          </li>
1619                          <li>
1620                              <input type="radio" id="allow-custom-visibility-disabled" name="allow-custom-visibility" value="disabled" <?php checked( $this->get_allow_custom_visibility(), 'disabled' ); ?> />
1621                              <label for="allow-custom-visibility-disabled"><?php esc_html_e( 'Enforce field visibility', 'buddypress' ); ?></label>
1622                          </li>
1623                      </ul>
1624                  </div>
1625              </div>
1626          </div>
1627  
1628          <?php
1629      }
1630  
1631      /**
1632       * Output the metabox for setting if field is required or not.
1633       *
1634       * @since 2.3.0
1635       *
1636       * @return void If default field or if the field does not support the feature.
1637       */
1638  	private function required_metabox() {
1639  
1640          // Default field and field types not supporting the feature cannot be required.
1641          if ( true === $this->is_default_field() || ! $this->field_type_supports( 'required' ) ) {
1642              return;
1643          } ?>
1644  
1645          <div class="postbox" id="field-type-required-metabox">
1646              <h2><label for="required"><?php esc_html_e( 'Requirement', 'buddypress' ); ?></label></h2>
1647              <div class="inside">
1648                  <select name="required" id="required">
1649                      <option value="0"<?php selected( $this->is_required, '0' ); ?>><?php esc_html_e( 'Not Required', 'buddypress' ); ?></option>
1650                      <option value="1"<?php selected( $this->is_required, '1' ); ?>><?php esc_html_e( 'Required',     'buddypress' ); ?></option>
1651                  </select>
1652              </div>
1653          </div>
1654  
1655      <?php
1656      }
1657  
1658      /**
1659       * Private method used to output autolink metabox.
1660       *
1661       * @since 2.5.0
1662       *
1663       * @return void If the field does not support the feature.
1664       */
1665  	private function autolink_metabox() {
1666  
1667          // Field types not supporting the feature cannot use autolink.
1668          if ( ! $this->field_type_supports( 'do_autolink' ) ) {
1669              return;
1670          } ?>
1671  
1672          <div class="postbox" id="field-type-autolink-metabox">
1673              <h2><?php esc_html_e( 'Autolink', 'buddypress' ); ?></h2>
1674              <div class="inside">
1675                  <p class="description"><?php esc_html_e( 'On user profiles, link this field to a search of the Members directory, using the field value as a search term.', 'buddypress' ); ?></p>
1676  
1677                  <p>
1678                      <label for="do-autolink" class="screen-reader-text"><?php
1679                          /* translators: accessibility text */
1680                          esc_html_e( 'Autolink status for this field', 'buddypress' );
1681                      ?></label>
1682                      <select name="do_autolink" id="do-autolink">
1683                          <option value="on" <?php selected( $this->get_do_autolink() ); ?>><?php esc_html_e( 'Enabled', 'buddypress' ); ?></option>
1684                          <option value="" <?php selected( $this->get_do_autolink(), false ); ?>><?php esc_html_e( 'Disabled', 'buddypress' ); ?></option>
1685                      </select>
1686                  </p>
1687              </div>
1688          </div>
1689  
1690          <?php
1691      }
1692  
1693      /**
1694       * Output the metabox for setting what type of field this is.
1695       *
1696       * @since 2.3.0
1697       *
1698       * @return void If default field.
1699       */
1700  	private function type_metabox() {
1701  
1702          // Default field cannot change type.
1703          if ( true === $this->is_default_field() ) {
1704              return;
1705          }
1706          ?>
1707  
1708          <div class="postbox">
1709              <h2><label for="fieldtype"><?php esc_html_e( 'Type', 'buddypress'); ?></label></h2>
1710              <div class="inside" aria-live="polite" aria-atomic="true" aria-relevant="all">
1711                  <?php if ( ! $this->field_type_supports( 'switch_fieldtype' ) ) : ?>
1712                      <input type="text" disabled="true" value="<?php echo esc_attr( $this->type_obj->name ); ?>">
1713                      <input type="hidden" name="fieldtype" id="fieldtype" value="<?php echo esc_attr( $this->type ); ?>">
1714  
1715                  <?php else : ?>
1716                      <select name="fieldtype" id="fieldtype" onchange="show_options(this.value)">
1717  
1718                          <?php bp_xprofile_admin_form_field_types( $this->type ); ?>
1719  
1720                      </select>
1721                  <?php endif; ?>
1722  
1723                  <?php
1724  
1725                  // Deprecated filter, don't use. Go look at {@link BP_XProfile_Field_Type::admin_new_field_html()}.
1726                  do_action( 'xprofile_field_additional_options', $this );
1727  
1728                  $this->render_admin_form_children(); ?>
1729  
1730              </div>
1731          </div>
1732  
1733      <?php
1734      }
1735  
1736      /**
1737       * Output the metabox for setting the field's position into the signup form.
1738       *
1739       * @since 8.0.0
1740       *
1741       * @return void If default field or if the field does not support the feature.
1742       */
1743  	private function signup_position_metabox() {
1744          // Field types not supporting the feature cannot be added to signups form.
1745          if ( ! $this->field_type_supports( 'signup_position' ) || true === $this->is_default_field() ) {
1746              return;
1747          }
1748  
1749          $next_signup_position = 1;
1750          $signup_position      = $this->get_signup_position();
1751  
1752          if ( 0 === $signup_position ) {
1753              $signup_fields_order = bp_xprofile_get_signup_field_ids();
1754              $next_signup_position = count( $signup_fields_order ) + 1;
1755          } else {
1756              $next_signup_position = $signup_position;
1757          }
1758          ?>
1759  
1760          <div class="postbox" id="field-signup-position-metabox">
1761              <h2><?php esc_html_e( 'Signups', 'buddypress' ); ?></h2>
1762              <div class="inside">
1763                  <div>
1764                      <ul>
1765                          <li>
1766                              <input type="checkbox" id="has-signup-position" name="signup-position" value="<?php echo esc_attr( $next_signup_position ); ?>" <?php checked( $signup_position, $next_signup_position ); ?> />
1767                              <label for="has-signup-position"><?php esc_html_e( 'Use this field in the site registration form.', 'buddypress' ); ?></label>
1768                          </li>
1769                      </ul>
1770                  </div>
1771              </div>
1772          </div>
1773          <?php
1774      }
1775  
1776      /**
1777       * Output hidden fields used by default field.
1778       *
1779       * @since 2.3.0
1780       *
1781       * @return void If not default field.
1782       */
1783  	private function default_field_hidden_inputs() {
1784  
1785          // Nonce.
1786          wp_nonce_field( 'bp_xprofile_admin_field', 'bp_xprofile_admin_field' );
1787  
1788          // Init default field hidden inputs.
1789          $default_field_hidden_inputs = array();
1790          $hidden_fields = array(
1791              'required' => array(
1792                  'name'  => 'required',
1793                  'id'    => 'required',
1794                  'value' => '0',
1795              ),
1796              'default_visibility' => array(
1797                  'name'  => 'default-visibility',
1798                  'id'    => 'default-visibility',
1799                  'value' => $this->get_default_visibility(),
1800              ),
1801              'allow_custom_visibility' => array(
1802                  'name'  => 'allow-custom-visibility',
1803                  'id'    => 'allow-custom-visibility',
1804                  'value' => 'disabled',
1805              ),
1806              'do_autolink' => array(
1807                  'name'  => 'do_autolink',
1808                  'id'    => 'do-autolink',
1809                  'value' => '',
1810              ),
1811          );
1812  
1813          // Field 1 is the fullname field, which is required.
1814          if ( true === $this->is_default_field() ) {
1815              $default_field_required          = $hidden_fields['required'];
1816              $default_field_required['value'] = '1';
1817  
1818              $default_field_hidden_inputs = array(
1819                  $default_field_required,
1820                  array(
1821                      'name'  => 'fieldtype',
1822                      'id'    => 'fieldtype',
1823                      'value' => 'textbox',
1824                  ),
1825                  array(
1826                      'name'  => 'signup-position',
1827                      'id'    => 'has-signup-position',
1828                      'value' => $this->get_signup_position(),
1829                  ),
1830              );
1831          }
1832  
1833          $supports = $this->get_field_type_supports();
1834          if ( $supports ) {
1835              foreach ( $supports as $feature => $support ) {
1836                  if ( true === $support || in_array( $feature, array( 'switch_fieldtype', 'member_types' ), true ) ) {
1837                      continue;
1838                  }
1839  
1840                  $default_field_hidden_inputs[] = $hidden_fields[ $feature ];
1841  
1842                  if ( 'allow_custom_visibility' === $feature ) {
1843                      $default_field_hidden_inputs[] = $hidden_fields['default_visibility'];
1844                  }
1845              }
1846          }
1847  
1848          if ( ! $default_field_hidden_inputs ) {
1849              return;
1850          }
1851  
1852          foreach ( $default_field_hidden_inputs as $default_field_hidden_input ) {
1853              printf(
1854                  '<input type="hidden" name="%1$s" id="%2$s" value="%3$s"/>',
1855                  esc_attr( $default_field_hidden_input['name'] ),
1856                  esc_attr( $default_field_hidden_input['id'] ),
1857                  esc_attr( $default_field_hidden_input['value'] )
1858              );
1859          }
1860      }
1861  
1862      /**
1863       * Return if a field ID is the default field.
1864       *
1865       * @since 2.3.0
1866       *
1867       * @param int $field_id ID of field to check.
1868       * @return bool
1869       */
1870  	private function is_default_field( $field_id = 0 ) {
1871  
1872          // Fallback to current field ID if none passed.
1873          if ( empty( $field_id ) ) {
1874              $field_id = $this->id;
1875          }
1876  
1877          // Compare & return.
1878          return (bool) ( 1 === (int) $field_id );
1879      }
1880  }


Generated: Sat Apr 27 01:00:55 2024 Cross-referenced by PHPXref 0.7.1