Contents
Introducción
Este artículo muestra paso a paso cómo añadir un metabox en el editor de WordPress y cómo guardar los metacampos de forma segura usando nonces y buenas prácticas de sanitización y capacidad de usuario. Se incluyen ejemplos completos en PHP listos para copiar y pegar en el archivo functions.php de un tema o en un plugin. Se explica cada comprobación de seguridad necesaria para evitar problemas con autosave, CSRF y permisos.
Resumen del flujo
- Registrar el metabox con add_meta_box.
- Renderizar el formulario del metabox, incluyendo un nonce con wp_nonce_field.
- Guardar los campos con el hook save_post, comprobando autosave, nonce y permisos del usuario.
- Sanitizar los datos antes de guardarlos con update_post_meta y escapar la salida cuando se muestre.
Motivación de las comprobaciones de seguridad
- Nonce: evita ataques CSRF verificando que el formulario viene de la interfaz de administración válida.
- DOING_AUTOSAVE: evita sobrescribir meta durante auto-guardados automáticos.
- current_user_can(edit_post, post_id): comprueba que el usuario actual tiene permisos para editar el post.
- Sanitización: garantiza que solo guardes datos fiables en la base de datos.
Ejemplo completo: campo de texto, textarea, checkbox y select
Ejemplo práctico con:
- Metabox en post type post.
- Campos: título corto (text), descripción (textarea), destacado (checkbox), origen (select).
- Nonce y todas las comprobaciones de seguridad en la función de guardado.
lt?php
// 1) Registrar el metabox
add_action(add_meta_boxes, mi_plugin_registrar_metabox)
function mi_plugin_registrar_metabox() {
add_meta_box(
mi_metabox_id, // ID
Datos extra del post, // Título
mi_plugin_metabox_callback, // Callback
post, // Pantalla (post type)
advanced, // Contexto
high // Prioridad
)
}
// 2) Renderizar el metabox (HTML del formulario)
function mi_plugin_metabox_callback(post) {
// Recuperar valores guardados
texto_corto = get_post_meta(post-gtID, _mi_texto_corto, true)
descripcion = get_post_meta(post-gtID, _mi_descripcion, true)
destacado = get_post_meta(post-gtID, _mi_destacado, true)
origen = get_post_meta(post-gtID, _mi_origen, true)
// Nonce para seguridad
wp_nonce_field(mi_plugin_nonce_action, mi_plugin_nonce_field)
?gt
ltpgt
ltlabel for=mi_texto_cortogtTexto corto:lt/labelgtltbrgt
ltinput type=text id=mi_texto_corto name=mi_texto_corto value=lt?php echo esc_attr(texto_corto) ?gt style=width:100% /gt
lt/pgt
ltpgt
ltlabel for=mi_descripciongtDescripción:lt/labelgtltbrgt
lttextarea id=mi_descripcion name=mi_descripcion rows=5 style=width:100%gtlt?php echo esc_textarea(descripcion) ?gtlt/textareagt
lt/pgt
ltpgt
ltlabelgt
ltinput type=checkbox name=mi_destacado value=1 lt?php checked(destacado, 1) ?gt /gt
Destacado
lt/labelgt
lt/pgt
ltpgt
ltlabel for=mi_origengtOrigen:lt/labelgtltbrgt
ltselect id=mi_origen name=mi_origengt
ltoption value= lt?php selected(origen, ) ?gtgt-- Seleccione --lt/optiongt
ltoption value=web lt?php selected(origen, web) ?gtgtWeblt/optiongt
ltoption value=newsletter lt?php selected(origen, newsletter) ?gtgtNewsletterlt/optiongt
ltoption value=otro lt?php selected(origen, otro) ?gtgtOtrolt/optiongt
lt/selectgt
lt/pgt
lt?php
}
// 3) Guardar los datos del metabox
add_action(save_post, mi_plugin_guardar_metabox_datos)
function mi_plugin_guardar_metabox_datos(post_id) {
// 3.1 Comprobar autosave
if (defined(DOING_AUTOSAVE) ampamp DOING_AUTOSAVE) {
return
}
// 3.2 Comprobar nonce
if (!isset(_POST[mi_plugin_nonce_field]) !wp_verify_nonce(_POST[mi_plugin_nonce_field], mi_plugin_nonce_action)) {
return
}
// 3.3 Comprobar permisos del usuario
if (!current_user_can(edit_post, post_id)) {
return
}
// 3.4 Sanitizar y guardar cada campo
if (isset(_POST[mi_texto_corto])) {
texto = sanitize_text_field(_POST[mi_texto_corto])
update_post_meta(post_id, _mi_texto_corto, texto)
} else {
delete_post_meta(post_id, _mi_texto_corto)
}
if (isset(_POST[mi_descripcion])) {
desc = sanitize_textarea_field(_POST[mi_descripcion])
update_post_meta(post_id, _mi_descripcion, desc)
} else {
delete_post_meta(post_id, _mi_descripcion)
}
// Checkbox: guardar 1 o eliminar meta
if (!empty(_POST[mi_destacado])) {
update_post_meta(post_id, _mi_destacado, 1)
} else {
delete_post_meta(post_id, _mi_destacado)
}
// Select: sanitizar contra valores permitidos
valores_permitidos = array(web, newsletter, otro, )
if (isset(_POST[mi_origen]) ampamp in_array(_POST[mi_origen], valores_permitidos, true)) {
update_post_meta(post_id, _mi_origen, _POST[mi_origen])
} else {
delete_post_meta(post_id, _mi_origen)
}
}
?gt
Explicación detallada del código
- add_meta_box: define el metabox indicando ID, título, callback, post type, contexto y prioridad.
- get_post_meta: obtiene el valor actual para prellenar los campos del formulario.
- wp_nonce_field: añade un campo oculto con nonce que usaremos más tarde para verificar el origen del formulario.
- En la función de guardado:
- Se evita el guardado durante autosave porque DOING_AUTOSAVE puede sobreescribir datos.
- Se verifica que la variable nonce existe y es válida con wp_verify_nonce.
- Se comprueba que el usuario pueda editar el post con current_user_can.
- Se sanitizan los datos con sanitize_text_field, sanitize_textarea_field y validación manual para selects y checkboxes.
- Se usa update_post_meta para escribir o delete_post_meta si el campo está vacío y no se quiere dejar meta vacío.
Cómo mostrar los metacampos en la plantilla (frontend)
Al mostrar datos en el frontend siempre escapar la salida según el contexto. Ejemplos:
lt?php
// Dentro del loop de WordPress o con un post definido
texto_corto = get_post_meta(get_the_ID(), _mi_texto_corto, true)
descripcion = get_post_meta(get_the_ID(), _mi_descripcion, true)
destacado = get_post_meta(get_the_ID(), _mi_destacado, true)
origen = get_post_meta(get_the_ID(), _mi_origen, true)
// Escapar para HTML simple
if (texto_corto) {
echo lth4gt . esc_html(texto_corto) . lt/h4gt
}
if (descripcion) {
echo ltpgt . wp_kses_post(wpautop(descripcion)) . lt/pgt
}
// Checkbox como etiqueta
if (destacado === 1) {
echo ltspan class=mi-destacadogtDestacadolt/spangt
}
// Origen
if (origen) {
echo ltsmallgtOrigen: . esc_html(origen) . lt/smallgt
}
?gt
Buenas prácticas y recomendaciones
- Prefijar las keys de meta (por ejemplo: _mi_texto_corto) para evitar colisiones con otros plugins/temas.
- Evitar guardar HTML sin sanitizar si se necesita HTML permitido usar wp_kses o funciones similares.
- No confiar en datos del cliente siempre validar y sanitizar en el servidor antes de guardar.
- Eliminar meta innecesaria con delete_post_meta para no dejar basura en la base de datos.
- Considerar usar una clase o namespace si se desarrolla un plugin para evitar conflictos y mantener el código organizado.
Errores comunes y cómo solucionarlos
- No verificar nonce: permite CSRF. Solución: incluir wp_nonce_field y verificar con wp_verify_nonce.
- No comprobar DOING_AUTOSAVE: guardados inesperados. Solución: retornar si DOING_AUTOSAVE es true.
- No comprobar permisos: usuarios sin permiso podrían modificar datos. Solución: usar current_user_can(edit_post, post_id).
- No sanitizar datos: riesgo de XSS o datos inválidos en la base de datos. Solución: usar sanitize_text_field, sanitize_textarea_field, esc_url_raw, intval, etc.
Puntos avanzados y extensiones
- Usar campos repetibles: para metadatos complejos se pueden guardar arrays serializados o usar meta con claves numéricas. Sanear cada elemento.
- WP REST API: exponer metadatos vía REST requiere registrar meta con register_post_meta y show_in_rest => true.
- CBX y select con opciones dinámicas: cargar opciones desde taxonomías o tablas externas y validarlas al guardar.
- Uso de clases para encapsular la lógica del metabox y permitir reusabilidad y pruebas unitarias.
Resumen final
Crear metaboxes y guardar meta campos en WordPress correctamente implica respetar varias comprobaciones de seguridad: nonce, autosave, permisos y sanitización de datos. El ejemplo proporcionado muestra un flujo completo con distintos tipos de campos y todas las comprobaciones necesarias para un desarrollo seguro y mantenible.
|
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |
