LLMS_Admin_User_Custom_Fields
Add custom user fields to user admin panel screens
Description Description
Applies to edit-user.php, user-new.php, & profile.php.
Source Source
File: includes/admin/class.llms.admin.user.custom.fields.php
class LLMS_Admin_User_Custom_Fields { private $fields = array(); /** * Constructor * * @since 2.7.0 * @since 3.13.0 Unknown. * @since 4.14.0 Add personal options hook. * @since 5.0.0 Custom fields (legacy), are now printed with priority 11 instead of 10. * @return void */ public function __construct() { // Output custom fields on edit screens. $field_actions = array( 'show_user_profile', 'edit_user_profile', 'user_new_form', ); foreach ( $field_actions as $action ) { add_action( $action, array( $this, 'output_custom_fields' ), 11, 1 ); add_action( $action, array( $this, 'output_instructors_assistant_fields' ), 10, 1 ); } // Allow errors to be output before saving field data. // Save the data if no errors are encountered. add_action( 'user_profile_update_errors', array( $this, 'add_errors' ), 10, 3 ); // Save data when a new user is created. add_action( 'edit_user_created_user', array( $this, 'save' ) ); // Add personal options. add_action( 'personal_options', array( $this, 'output_personal_options' ) ); } /** * Validate custom fields * * During updates will save data, creation is saved during a different action. * * @since 2.7.0 * @since 3.13.0 Unknown. * @since 3.37.15 Correctly pass `$user` to `$this->save()`. * * @param obj $errors Instance of WP_Error, passed by reference. * @param bool $update `true` if updating a profile, `false` if a new user. * @param WP_User $user Instance of WP_User for the user being updated. * @return void */ public function add_errors( &$errors, $update, $user ) { $this->get_fields(); $error = $this->validate_fields( $user ); if ( $error ) { $errors->add( '', $error, '' ); if ( $update ) { $this->save( $user ); } // Don't save. remove_action( 'edit_user_created_user', array( $this, 'save' ) ); return; } // If updating, save here since there's no other save specific admin action (that I could find). if ( $update ) { $this->save( $user ); } } /** * Retrieve an associative array of custom fields and custom field data * * @since 2.7.0 * @since 3.13.0 Unknown. * @since 5.0.0 Removed LLMS core fields and deprecate the filter usage. * * @return array */ public function get_fields() { $this->fields = apply_filters_deprecated( 'lifterlms_get_user_custom_fields', array( array(), ), '5.0.0', 'llms_admin_profile_fields' ); return $this->fields; } /** * Load usermeta data into the array of fields retrieved from $this->get_fields * * Meta data is added to the array under the key "value" for each field. * * If no data is found for a particular field the value is still added as an empty string. * * @since 2.7.0 * * @param WP_User|int $user Instance of WP_User or WP User ID * @return array */ public function get_fields_with_data( $user ) { if ( is_numeric( $user ) ) { $user = new WP_User( $user ); } $this->get_fields(); foreach ( $this->fields as $field => $data ) { $this->fields[ $field ]['value'] = apply_filters( 'lifterlms_get_user_custom_field_value_' . $field, $user->get( $field ), $user, $field ); } return $this->fields; } /** * Output custom field data fields as HTML inputs * * @since 2.7.0 * @since 3.24.0 Unknown. * @since 5.0.0 Do not include user-edit template if no fields to show. * * @param WP_User|int $user Instance of WP_User or WP User ID. * @return void */ public function output_custom_fields( $user ) { if ( is_numeric( $user ) || is_a( $user, 'WP_User' ) ) { $this->get_fields_with_data( $user ); } else { $this->get_fields(); } if ( empty( $this->fields ) ) { return; } llms_get_template( 'admin/user-edit.php', array( 'section_title' => __( 'LifterLMS Profile (legacy fields)', 'lifterlms' ), 'fields' => $this->fields, ) ); } /** * Output personal option fields * * Currently adds a single option row for controlling auto-save behavior on the course builder. * * @since 4.14.0 * * @param WP_User $user Viewed user object. * @return void */ public function output_personal_options( $user ) { if ( ! user_can( $user, 'edit_courses' ) ) { return; } $autosave = get_user_option( 'llms_builder_autosave', $user->ID ); $autosave = empty( $autosave ) ? 'yes' : $autosave; ?> <tr class="llms-builder-autosave llms-builder-autosave-wrap"> <th scope="row"><?php _e( 'Course Builder Autosave', 'lifterlms' ); ?></th> <td> <label for="llms_builder_autosave"> <input name="llms_builder_autosave" type="checkbox" id="llms_builder_autosave" value="yes"<?php checked( 'yes', $autosave ); ?>> <?php _e( 'Automatically save changes when using the course builder', 'lifterlms' ); ?> </label><br> </td> </tr> <?php } /** * Add instructor parent fields for use when creating instructor's assistants * * @since 3.13.0 * @since 3.23.0 Unknown. * @since 3.37.15 Use strict comparisons. * * @param WP_User|int $user Instance of WP_User or WP User ID * @return void */ public function output_instructors_assistant_fields( $user ) { if ( is_numeric( $user ) || is_a( $user, 'WP_User' ) ) { $instructor = llms_get_instructor( $user ); $selected = $instructor->get( 'parent_instructors' ); if ( empty( $selected ) && ! is_array( $selected ) ) { $selected = array(); } } else { $selected = array( get_current_user_id() ); } $selected = array_map( 'absint', $selected ); // Only let admins & lms managers select the parent for an instructor's assistant. if ( current_user_can( 'manage_lifterlms' ) ) { $users = get_users( array( 'role__in' => array( 'administrator', 'lms_manager', 'instructor' ), ) ); ?> <table class="form-table" id="llms-parent-instructors-table" style="display:none;"> <tr class="form-field"> <th scope="row"><label for="llms-parent-instructors"><?php _e( 'Parent Instructor(s)', 'lifterlms' ); ?></label></th> <td> <select class="regular-text" id="llms-parent-instructors" name="llms_parent_instructors[]" multiple="multiple"> <?php foreach ( $users as $user ) : ?> <option value="<?php echo $user->ID; ?>"<?php selected( in_array( $user->ID, $selected, true ) ); ?>> <?php echo $user->display_name; ?> </option> <?php endforeach; ?> </select> </td> </tr> </table> <?php add_action( 'admin_print_footer_scripts', array( $this, 'output_instructors_assistant_scripts' ) ); } elseif ( 'add-new-user' === $user ) { /** * This will be the case for Instructors only: * * Show a hidden field with the current user's info * * When saving it will only save if the created user's role is instructor's assistant. */ echo '<input type="hidden" name="llms_parent_instructors[]" value="' . get_current_user_id() . '">'; } } /** * Output JS to handle user interaction with the instructor's parent field * * Display custom field ONLY when creating/editing an instructor's assistant. * * @since 3.13.0 * * @return void */ public function output_instructors_assistant_scripts() { ?> <script> ( function( $ ) { var $role = $( '#role' ), $parent = $( '#llms-parent-instructors-table' ); $role.closest( '.form-table' ).after( $parent ); $role.on( 'change', function() { if ( 'instructors_assistant' === $( this ).val() ) { $parent.show(); } else { $parent.hide(); } } ).trigger( 'change' ); } )( jQuery ); </script> <?php } /** * Save custom field data for a user * * @since 3.13.0 * @since 3.35.0 Sanitize input data. * @since 3.37.15 Use strict comparisons. * @since 4.14.0 Save builder autosave personal options. * @since 5.9.0 Stop using deprecated `FILTER_SANITIZE_STRING`. * * @param WP_User|int|obj $user User object or id. * @return void */ public function save( $user ) { if ( is_numeric( $user ) ) { // Numeric ID is passed in during creations. $user = new WP_User( $user ); $action = 'create'; } elseif ( isset( $user->ID ) ) { // An object that's not a WP_User gets passed in during updates. $user = new WP_User( $user->ID ); $action = 'update'; } // Saves custom fields. foreach ( $this->fields as $field => $data ) { $value = apply_filters( 'lifterlms_save_custom_user_field_' . $field, llms_filter_input_sanitize_string( INPUT_POST, $field ), $user, $field ); update_user_meta( $user->ID, $field, $value ); } // Save instructor assistant's parent instructor. if ( in_array( 'instructors_assistant', $user->roles, true ) && ! empty( $_POST['llms_parent_instructors'] ) ) { // phpcs:disable WordPress.Security.NonceVerification.Missing $instructor = llms_get_instructor( $user ); $instructor->add_parent( llms_filter_input( INPUT_POST, 'llms_parent_instructors', FILTER_SANITIZE_NUMBER_INT, FILTER_REQUIRE_ARRAY ) ); } // Save personal options. if ( user_can( $user, 'edit_courses' ) && 'create' !== $action ) { $autosave = empty( $_POST['llms_builder_autosave'] ) ? 'no' : 'yes'; update_user_meta( $user->ID, 'llms_builder_autosave', $autosave ); } } /** * Validate custom fields * * By default only checks for valid as core fields don't have any special validation. * * If adding custom fields, hook into the action run after required validation * to add special validation rules for your field. * * @since 2.7.0 * * @param WP_User|int $user Instance of WP_User or WP User ID. * @return string|bool `false` if no validation errors or the error message (as a sttring) if validation errors occurred. */ public function validate_fields( $user ) { // Ensure there's no missing required fields. foreach ( $this->fields as $field => $data ) { // Return an error message for empty required fields. if ( empty( $_POST[ $field ] ) && $data['required'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing return sprintf( __( 'Required field "%s" is missing.', 'lifterlms' ), $data['label'] ); } else { /** * Run custom validation against the field * * If filter function returns a truthy, validation will stop, fields will not be saved, * and an error message will be displayed on screen. * * This should return `false` or a string which will be used as the error message. * * @since 2.7.0 * * @param boolean $error_message The error message when validation issues are encountered. Return `false` when no validation issues. * @param string $field Field id. * @param WP_User|int $user Instance of WP_User or WP User ID. */ $error_msg = apply_filters( "lifterlms_validate_custom_user_field_{$field}", false, $field, $user ); if ( $error_msg ) { return $error_msg; } } } return false; } }
Expand full source code Collapse full source code View on GitHub
Methods Methods
- __construct — Constructor
- add_errors — Validate custom fields
- get_fields — Retrieve an associative array of custom fields and custom field data
- get_fields_with_data — Load usermeta data into the array of fields retrieved from $this->get_fields
- output_custom_fields — Output custom field data fields as HTML inputs
- output_instructors_assistant_fields — Add instructor parent fields for use when creating instructor's assistants
- output_instructors_assistant_scripts — Output JS to handle user interaction with the instructor's parent field
- output_personal_options — Output personal option fields
- save — Save custom field data for a user
- validate_fields — Validate custom fields
Changelog Changelog
Version | Description |
---|---|
3.37.15 | Fix error encountered when errors encountered validating custom fields. |
3.35.0 | Sanitize input data. |
2.7.0 | Introduced. |