tools/wordpress-user-contact-methods-generator
WordPress User Contact Methods Generator
Add fields to the Contact Info section of the profile screen with the user_contactmethods filter. WordPress renders and saves them itself — optionally expose them in the REST API too.
snippet.php
/**
* Adds fields to the Profile screen's "Contact Info" section.
*
* The second parameter has a default because add_filter() is told to pass two
* arguments: core always does, but a plugin calling
* apply_filters( 'user_contactmethods', $methods ) with one would be an
* ArgumentCountError on PHP 8 against a callback that requires both.
*/
function wp_skills_user_contact_methods( $methods, $user = null ) {
$methods[ 'linkedin' ] = __( 'LinkedIn', 'text-domain' );
return $methods;
}
add_filter( 'user_contactmethods', 'wp_skills_user_contact_methods', 10, 2 );
/*
* No save routine, deliberately.
*
* WordPress saves these itself. edit_user() walks
* wp_get_user_contact_methods() and assigns each posted value with
* sanitize_text_field(), which is why a contact method needs no
* personal_options_update hook, no nonce of its own, and no capability check —
* the profile form's own checks already ran. Verified in Playground: with this
* filter in place and nothing else, posting a field to edit_user() stored the
* sanitized value, and a field left out of the request kept the value it had.
*
* Two consequences worth knowing. Core renders every contact method as a plain
* text input, so these cannot be typed as url, email or date — the field type
* is not yours to choose. And sanitize_text_field() strips tags but does not
* validate, so a field meant to hold a URL can hold anything; escape it at the
* point of output rather than trusting what is stored.
*/
// Reading the values in a template, inside the loop or with an explicit user id:
//
// $linkedin = get_the_author_meta( 'linkedin', $user_id );
// if ( $linkedin ) {
// printf( '<a href="%s">%s</a>', esc_url( $linkedin ), esc_html( $linkedin ) );
// }
//
// get_the_author_meta() returns the raw stored string, so it is escaped at the
// point of output: esc_url() for anything that becomes an href, esc_html()
// otherwise. A profile field is user input like any other.