Contents
Introducción
Este tutorial explica, con todo detalle y ejemplos completos en PHP y JavaScript, cómo añadir y mostrar un avatar y una biografía personalizados para los usuarios de WordPress. Incluye: campos en el perfil de usuario (URL o attachment ID), guardado seguro, integración con el Media Uploader, filtro para reemplazar get_avatar() y funciones de plantilla para mostrar avatar bio. Se asume que dispone de conocimientos básicos de WordPress y que trabajará en un child theme o en un plugin propio.
Resumen de la solución
- Añadir campos personalizados en la página de perfil (avatar personalizado y biografía).
- Guardar esos campos con comprobaciones de permisos y nonces.
- Opcional: integrar el Media Uploader para seleccionar imágenes desde la librería.
- Filtrar get_avatar() para devolver el avatar personalizado en vez del Gravatar.
- Proveer funciones de plantilla para mostrar avatar y biografía con escapes y fallback.
Recomendaciones previas
- Trabaje en un tema hijo o en un plugin para no perder cambios con actualizaciones.
- Haga backup antes de modificar archivos.
- Use siempre funciones de escape y saneamiento: esc_url(), esc_attr(), esc_html(), sanitize_text_field(), esc_textarea().
Paso 1 — Añadir campos al perfil de usuario
Agregar los campos en el formulario de perfil: campo para URL de avatar o para almacenar el ID del attachment (cuando use el Media Uploader), y un textarea para la biografía personalizada.
lt?php
// Añadir campos al perfil (show_user_profile y edit_user_profile)
add_action(show_user_profile, mi_perfil_campos_personalizados)
add_action(edit_user_profile, mi_perfil_campos_personalizados)
function mi_perfil_campos_personalizados(user) {
// Obtiene valores previos
custom_avatar_url = get_user_meta(user-gtID, custom_avatar_url, true)
custom_avatar_id = get_user_meta(user-gtID, custom_avatar_id, true) // opcional
custom_bio = get_user_meta(user-gtID, custom_bio, true)
// Nonce para verificar al guardar
wp_nonce_field(guardar_campos_perfil_personalizados, nonce_campos_perfil_personalizados)
?gt
ltpgtltlabel for=custom_avatar_urlgtltbgtAvatar personalizado (URL)lt/bgtlt/labelgtltbr /gt
ltinput type=url name=custom_avatar_url id=custom_avatar_url value=lt?php echo esc_attr(custom_avatar_url) ?gt class=regular-text /gt
ltspan class=descriptiongtDirección completa de la imagen. Alternativamente use el Media Uploader con el campo ID (debajo).lt/spangtlt/pgt
ltpgtltlabel for=custom_avatar_idgtltbgtAvatar personalizado (Attachment ID)lt/bgtlt/labelgtltbr /gt
ltinput type=text name=custom_avatar_id id=custom_avatar_id value=lt?php echo esc_attr(custom_avatar_id) ?gt class=regular-text /gt
ltspan class=descriptiongtID del attachment guardado por el Media Uploader (opcional).lt/spangtlt/pgt
ltpgtltlabel for=custom_biogtltbgtBiografía personalizadalt/bgtlt/labelgtltbr /gt
lttextarea name=custom_bio id=custom_bio rows=5 class=large-textgtlt?php echo esc_textarea(custom_bio) ?gtlt/textareagt
ltspan class=descriptiongtTexto corto que se mostrará en los perfiles/autorías.lt/spangtlt/pgt
lt?php
}
?gt
Paso 2 — Guardar los campos del perfil
Al guardar el formulario del perfil hay que validar nonce, permisos y sanear los datos antes de guardarlos con update_user_meta().
lt?php
add_action(personal_options_update, mi_guardar_campos_perfil_personalizados)
add_action(edit_user_profile_update, mi_guardar_campos_perfil_personalizados)
function mi_guardar_campos_perfil_personalizados(user_id) {
if (!isset(_POST[nonce_campos_perfil_personalizados])) {
return
}
if (!wp_verify_nonce(_POST[nonce_campos_perfil_personalizados], guardar_campos_perfil_personalizados)) {
return
}
if (!current_user_can(edit_user, user_id)) {
return
}
// Guardar URL de avatar
if (isset(_POST[custom_avatar_url])) {
url = trim(_POST[custom_avatar_url])
url = esc_url_raw(url)
update_user_meta(user_id, custom_avatar_url, url)
}
// Guardar attachment ID (solo números)
if (isset(_POST[custom_avatar_id])) {
id = intval(_POST[custom_avatar_id])
if (id gt 0) {
update_user_meta(user_id, custom_avatar_id, id)
} else {
delete_user_meta(user_id, custom_avatar_id)
}
}
// Guardar biografía personalizada
if (isset(_POST[custom_bio])) {
bio = sanitize_text_field(_POST[custom_bio])
update_user_meta(user_id, custom_bio, bio)
}
}
?gt
Paso 3 (opcional) — Integrar el Media Uploader
Para una mejor UX use la Librería de Medios para seleccionar imágenes. Esto requiere encolar scripts y añadir un botón que abra el uploader. A continuación un ejemplo breve de cómo encolar y el JS necesario.
lt?php
// Encolar scripts en el admin para el Media Uploader
add_action(admin_enqueue_scripts, mi_encolado_media_uploader)
function mi_encolado_media_uploader(hook_suffix) {
// Solo en la pantalla de perfil y edición de usuario
if (profile.php !== hook_suffix user-edit.php !== hook_suffix) {
return
}
wp_enqueue_media()
wp_enqueue_script(mi-uploader-profile-js, plugin_dir_url(__FILE__) . mi-uploader-profile.js, array(jquery), 1.0, true)
}
?gt
Archivo JavaScript (mi-uploader-profile.js):
(function(){
var frame
(document).on(click, #mi_seleccionar_avatar, function(e){
e.preventDefault()
// Si existe el frame, reusar
if (frame) {
frame.open()
return
}
frame = wp.media({
title: Seleccionar avatar,
button: { text: Usar este avatar },
multiple: false
})
frame.on(select, function(){
var attachment = frame.state().get(selection).first().toJSON()
(#custom_avatar_id).val(attachment.id)
(#custom_avatar_preview).attr(src, attachment.sizes attachment.sizes.thumbnail ? attachment.sizes.thumbnail.url : attachment.url).show()
})
frame.open()
})
})(jQuery)
En el HTML del perfil (ya mostrado en el Paso 1) puede añadir un botón y una etiqueta img para previsualizar:
ltbutton id=mi_seleccionar_avatar class=buttongtSeleccionar avatar desde la libreríalt/buttongt ltimg id=custom_avatar_preview src=lt?php echo esc_url(preview_url) ?gt style=max-width:100pxdisplay: alt=Preview /gt
Paso 4 — Filtrar get_avatar() para usar el avatar personalizado
Use el filtro get_avatar para devolver la imagen personalizada (attachment ID o URL). El ejemplo siguiente gestiona distintas entradas (WP_User, comment, email). Respeta el parámetro size y conserva el markup de avatar típico de WordPress.
lt?php
add_filter(get_avatar, mi_get_avatar_personalizado, 10, 6)
function mi_get_avatar_personalizado(avatar, id_or_email, size, default, alt, args) {
user = false
// Obtener el user object
if (is_numeric(id_or_email)) {
user = get_user_by(id, (int) id_or_email)
} elseif (is_object(id_or_email)) {
if (!empty(id_or_email->user_id)) {
user = get_user_by(id, (int) id_or_email->user_id)
}
} else {
user = get_user_by(email, id_or_email)
}
if (!user) {
return avatar // Mantener comportamiento por defecto si no hay usuario
}
// Priorizar attachment ID, luego URL, luego fallback a Gravatar
avatar_id = get_user_meta(user->ID, custom_avatar_id, true)
avatar_url = get_user_meta(user->ID, custom_avatar_url, true)
if (avatar_id) {
img = wp_get_attachment_image_src(avatar_id, array(size, size))
if (img) {
src = esc_url(img[0])
} else {
src =
}
} elseif (avatar_url) {
src = esc_url(avatar_url)
} else {
return avatar
}
if (empty(src)) {
return avatar
}
alt = alt ? alt : sprintf(esc_attr__(%s avatar, textdomain), user->display_name)
class = isset(args[class]) ? args[class] : avatar avatar- . (int)size . photo
custom_avatar = sprintf(
ltimg alt=%s src=%s class=%s width=%d height=%d /gt,
esc_attr(alt),
src,
esc_attr(class),
(int) size,
(int) size
)
// Mantener envoltorio por si otros plugins esperan estructura similar
return custom_avatar
}
?gt
Notas sobre el filtro
- Si necesita conservar exactamente el HTML que WordPress genera (p. ej. envoltorios con enlace a author), puede construirlo aquí o aplicar el filtro en otro momento.
- El ejemplo devuelve solo la etiqueta ltimggt. Si quiere el mismo markup completo de WP (con span), puede construirlo con sprintf usando clases de avatar.
Mostrar avatar y biografía en la plantilla
Puede crear una función reutilizable que muestre avatar y biografía de un usuario concreto en su tema.
lt?php
function mostrar_avatar_y_bio_personalizados(user_id = 0, size = 96) {
if (!user_id) {
user_id = get_the_author_meta(ID)
}
user = get_user_by(id, user_id)
if (!user) {
return
}
// Usar nuestro filtro get_avatar (se aplicará automáticamente)
avatar_html = get_avatar(user_id, size)
custom_bio = get_user_meta(user_id, custom_bio, true)
// Si no hay bio personalizada, usar description o user meta por defecto
if (empty(custom_bio)) {
custom_bio = get_the_author_meta(description, user_id)
}
echo ltdiv class=mi-avatar-biogt
echo ltdiv class=mi-avatargt . avatar_html . lt/divgt
echo ltdiv class=mi-biogtlth4gt . esc_html(user->display_name) . lt/h4gt
if (custom_bio) {
echo ltpgt . esc_html(custom_bio) . lt/pgt
}
echo lt/divgtlt/divgt
}
?gt
Ejemplo completo: plugin mínimo que implementa la funcionalidad
El siguiente bloque resume todo en un único archivo de plugin para que lo pegue en wp-content/plugins/mi-avatar-bio/mi-avatar-bio.php (añádale encabezado de plugin, activo y pruebe).
lt?php
/
Plugin Name: Avatar y Bio Personalizados
Description: Añade avatar personalizado (URL o attachment ID) y biografía por usuario, filtra get_avatar y provee funciones de plantilla.
Version: 1.0
Author: Tu Nombre
/
if (!defined(ABSPATH)) exit
// Añadir campos al perfil
add_action(show_user_profile, mi_perfil_campos_personalizados)
add_action(edit_user_profile, mi_perfil_campos_personalizados)
function mi_perfil_campos_personalizados(user) {
custom_avatar_url = get_user_meta(user-gtID, custom_avatar_url, true)
custom_avatar_id = get_user_meta(user-gtID, custom_avatar_id, true)
custom_bio = get_user_meta(user-gtID, custom_bio, true)
wp_nonce_field(guardar_campos_perfil_personalizados, nonce_campos_perfil_personalizados)
?>
ltpgtltlabel for=custom_avatar_urlgtltbgtAvatar personalizado (URL)lt/bgtlt/labelgtltbr /gt
ltinput type=url name=custom_avatar_url id=custom_avatar_url value=lt?php echo esc_attr(custom_avatar_url) ?gt class=regular-text /gtlt/pgt
ltpgtltlabel for=custom_avatar_idgtltbgtAvatar personalizado (Attachment ID)lt/bgtlt/labelgtltbr /gt
ltinput type=text name=custom_avatar_id id=custom_avatar_id value=lt?php echo esc_attr(custom_avatar_id) ?gt class=regular-text /gt
ltbutton id=mi_seleccionar_avatar class=buttongtSeleccionar desde libreríalt/buttongt
ltimg id=custom_avatar_preview src=lt?php echo esc_url(custom_avatar_url ? custom_avatar_url : ) ?gt style=max-width:80px alt=Preview /gtlt/pgt
ltpgtltlabel for=custom_biogtltbgtBiografía personalizadalt/bgtlt/labelgtltbr /gt
lttextarea name=custom_bio id=custom_bio rows=5 class=large-textgtlt?php echo esc_textarea(custom_bio) ?gtlt/textareagtlt/pgt
lt?php
}
// Guardar campos
add_action(personal_options_update, mi_guardar_campos_perfil_personalizados)
add_action(edit_user_profile_update, mi_guardar_campos_perfil_personalizados)
function mi_guardar_campos_perfil_personalizados(user_id) {
if (!isset(_POST[nonce_campos_perfil_personalizados])) return
if (!wp_verify_nonce(_POST[nonce_campos_perfil_personalizados], guardar_campos_perfil_personalizados)) return
if (!current_user_can(edit_user, user_id)) return
if (isset(_POST[custom_avatar_url])) {
update_user_meta(user_id, custom_avatar_url, esc_url_raw(trim(_POST[custom_avatar_url])))
}
if (isset(_POST[custom_avatar_id])) {
id = intval(_POST[custom_avatar_id])
if (id gt 0) update_user_meta(user_id, custom_avatar_id, id)
else delete_user_meta(user_id, custom_avatar_id)
}
if (isset(_POST[custom_bio])) {
update_user_meta(user_id, custom_bio, sanitize_text_field(_POST[custom_bio]))
}
}
// Filtrar avatar
add_filter(get_avatar, mi_get_avatar_personalizado, 10, 6)
function mi_get_avatar_personalizado(avatar, id_or_email, size, default, alt, args) {
user = false
if (is_numeric(id_or_email)) user = get_user_by(id, (int) id_or_email)
elseif (is_object(id_or_email) !empty(id_or_email->user_id)) user = get_user_by(id, (int) id_or_email->user_id)
else user = get_user_by(email, id_or_email)
if (!user) return avatar
avatar_id = get_user_meta(user->ID, custom_avatar_id, true)
avatar_url = get_user_meta(user->ID, custom_avatar_url, true)
src =
if (avatar_id) {
img = wp_get_attachment_image_src(avatar_id, array(size, size))
if (img) src = img[0]
} elseif (avatar_url) {
src = avatar_url
}
if (empty(src)) return avatar
alt = alt ? alt : sprintf(esc_attr__(%s avatar, textdomain), user->display_name)
class = isset(args[class]) ? args[class] : avatar avatar- . (int)size . photo
return sprintf(ltimg alt=%s src=%s class=%s width=%d height=%d /gt,
esc_attr(alt),
esc_url(src),
esc_attr(class),
(int) size,
(int) size
)
}
// Función de plantilla
function mostrar_avatar_y_bio_personalizados(user_id = 0, size = 96) {
if (!user_id) user_id = get_the_author_meta(ID)
user = get_user_by(id, user_id)
if (!user) return
avatar_html = get_avatar(user_id, size)
custom_bio = get_user_meta(user_id, custom_bio, true)
if (empty(custom_bio)) custom_bio = get_the_author_meta(description, user_id)
echo ltdiv class=mi-avatar-biogt
echo ltdiv class=mi-avatargt . avatar_html . lt/divgt
echo ltdiv class=mi-biogtlth4gt . esc_html(user->display_name) . lt/h4gt
if (custom_bio) echo ltpgt . esc_html(custom_bio) . lt/pgt
echo lt/divgtlt/divgt
}
?gt
Buenas prácticas y seguridad
- Use nonces y current_user_can al guardar para evitar CSRF y escalado de privilegios.
- Saneamiento: esc_url_raw para URLs, sanitize_text_field para textos y esc_textarea/esc_html al mostrar.
- Compruebe que el attachment ID corresponde a una imagen antes de usarlo (wp_get_attachment_image_src devuelve false si no existe).
- Si muestra HTML en front-end, evite imprimir contenido sin escapar para prevenir XSS.
- Cache: si su sitio carga muchos avatares personalizados, considere usar transients o un CDN para las imágenes.
Compatibilidad y consideraciones finales
- Si el usuario no tiene avatar personalizado, deje el comportamiento por defecto (Gravatar) o use una imagen por defecto alojada localmente.
- Respete tamaños y proporciones. Use funciones de imagen de WordPress para obtener tamaños generados: wp_get_attachment_image_src(id, size).
- Si tiene multi-site, verifique que las rutas y permisos de media sean correctos.
- Si otros plugins ya filtran get_avatar, ajuste prioridades o implemente condiciones para evitar conflictos.
Recursos útiles
|
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |
