
El código de hoy nos permitirá añadir un botón con un shortcode en cualquier post o página en WordPress. Al pincharlo abrirá una ventana modal en la que podremos añadir el contenido que queramos.
Puede usarse para mostrar un formulario de suscripción, de contacto, un aviso puntual, una imagen, una caja de información o, como en mi caso, un formulario para notificar errores.
Puedes probar el shortcode funcionando al pie de cualquier post.
[error_report_form]Aunque está funcionando correctamente desde hace semanas en este blog, se recuerda que se trata de la versión 1.0 y que siempre se puede mejorar y depurar. Se puede separar el CSS para facilitar los cambios e incluso, si te atreves, convertirlo en un plugin para añadir más ajustes y configuraciones de acceso fácil y rápido.
Bajo el código encontrarás la descripción de sus opciones y las instrucciones para probarlo.
Código
/*
* Versión: 1.1
* Última actualización: 17-03-2025 02:30:00 (Añadido icono SVG)
*/
// Shortcode para mostrar el botón y el formulario modal actualizado en jrmora.com
function error_report_form_shortcode() {
// Generar números aleatorios para la operación matemática
$num1 = rand(1, 9);
$num2 = rand(1, 9);
$sum = $num1 + $num2;
// Iniciar sesión si no está iniciada
if (!session_id()) {
session_start();
}
// Guardar la suma en una variable de sesión para validar después
$_SESSION['captcha_sum'] = $sum;
// HTML del botón y el modal
ob_start();
?>
<button id="openErrorModal" style="padding: 10px 20px; background-color: #ffcc00; color: black; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; display: flex; align-items: center; gap: 8px;">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 5.177l8.631 15.823h-17.262l8.631-15.823zm0-4.177l-12 22h24l-12-22zm-1 9h2v6h-2v-6zm1 9.75c-.689 0-1.25-.56-1.25-1.25s.561-1.25 1.25-1.25 1.25.56 1.25 1.25-.561 1.25-1.25 1.25z"/></svg>
Comunicar error
</button>
<div id="errorModalOverlay" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 999;">
<div id="errorModal" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: white; padding: 20px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); z-index: 1000; border-radius: 7px; width: 90%; max-width: 500px;">
<span id="closeModalBtn" style="position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 20px; background-color: black; color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; line-height: 30px;">×</span>
<h3 style="font-size: 18px;">Comunicar error (Para errores ortográficos o de malfuncionamiento del sitio)</h3>
<form id="errorForm" method="post">
<input type="hidden" name="action" value="submit_error_report">
<label for="name" style="font-size: 14px;">Nombre:</label>
<input type="text" id="name" name="name" required style="background-color: #f5f5f5; padding: 8px; border: 1px solid #ddd; border-radius: 4px; width: 100%; margin-bottom: 12px;">
<div id="nameError" style="color: red; display: none; font-size: 12px; margin-bottom: 12px;">No se permiten enlaces o dominios en el campo "Nombre".</div>
<label for="message" style="font-size: 14px;">Descripción del error:</label>
<textarea id="message" name="message" required style="background-color: #f5f5f5; padding: 8px; border: 1px solid #ddd; border-radius: 4px; width: 100%; margin-bottom: 12px;"></textarea>
<div id="messageError" style="color: red; display: none; font-size: 12px; margin-bottom: 12px;">No se permiten enlaces o dominios en el campo "Mensaje".</div>
<label for="captcha" style="font-size: 14px;">¿Eres humano? ¿Cuánto es <?php echo $num1; ?> + <?php echo $num2; ?>?</label>
<input type="text" id="captcha" name="captcha" required style="background-color: #f5f5f5; padding: 8px; border: 1px solid #ddd; border-radius: 4px; width: 100%; margin-bottom: 12px;">
<div id="captchaError" style="color: red; display: none; font-size: 12px; margin-bottom: 12px;">El resultado de la suma es incorrecto.</div>
<!-- Opcional: Añadir reCaptcha si se proporcionan las claves -->
<?php if (defined('RECAPTCHA_SITE_KEY') && defined('RECAPTCHA_SECRET_KEY')) : ?>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="<?php echo RECAPTCHA_SITE_KEY; ?>" style="margin-bottom: 12px;"></div>
<?php endif; ?>
<div style="text-align: center;">
<input type="submit" value="Enviar" style="background-color: #bc080e; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-weight: bold; display: inline-block;">
</div>
</form>
<div id="successMessage" style="color: green; display: none; margin-top: 10px; font-size: 14px; text-align: center;">Gracias por comunicar el error.</div>
</div>
</div>
<style>
/* Estilos para dispositivos móviles */
@media (max-width: 768px) {
#errorModal {
width: 90% !important; /* Ajusta el ancho del modal al 90% de la pantalla */
max-width: 90% !important; /* Asegura que no exceda el 90% del ancho */
padding: 15px !important; /* Reduce el padding para más espacio */
}
#errorForm input, #errorForm textarea {
font-size: 16px !important; /* Aumenta el tamaño de la fuente para mejor legibilidad */
}
#errorForm label {
font-size: 16px !important; /* Aumenta el tamaño de la fuente de las etiquetas */
}
}
</style>
<script>
// Abrir modal
document.getElementById('openErrorModal').addEventListener('click', function() {
document.getElementById('errorModalOverlay').style.display = 'block';
});
// Cerrar modal al hacer clic en el aspa
document.getElementById('closeModalBtn').addEventListener('click', function() {
document.getElementById('errorModalOverlay').style.display = 'none';
});
// Cerrar modal al hacer clic fuera de él
document.getElementById('errorModalOverlay').addEventListener('click', function(event) {
if (event.target === document.getElementById('errorModalOverlay')) {
document.getElementById('errorModalOverlay').style.display = 'none';
}
});
// Función para validar si hay enlaces o dominios en el texto
function containsUrlOrDomain(text) {
// Expresión regular para detectar enlaces o dominios
const urlPattern = /(http|https|www|\[url|\[link)|(\b\w+\.(com|org|net|io|co|edu|gov|mil|biz|info|xyz|me|tv|us|uk|ca|au|de|fr|es|it|nl|ru|jp|cn|in|br|mx|ar|cl|pe|ve|co\.uk|co\.jp|co\.in|ac\.uk|gov\.uk|edu\.au|com\.au|org\.uk|net\.au)\b)/i;
return urlPattern.test(text);
}
// Manejar el envío del formulario con AJAX
document.getElementById('errorForm').addEventListener('submit', function(e) {
e.preventDefault(); // Evitar envío tradicional del formulario
// Ocultar mensajes de error previos
document.getElementById('nameError').style.display = 'none';
document.getElementById('messageError').style.display = 'none';
document.getElementById('captchaError').style.display = 'none';
// Validar campos
var name = document.getElementById('name').value;
var message = document.getElementById('message').value;
var captcha = document.getElementById('captcha').value;
var hasError = false;
// Validar enlaces o dominios en el nombre
if (containsUrlOrDomain(name)) {
document.getElementById('nameError').style.display = 'block';
hasError = true;
}
// Validar enlaces o dominios en el mensaje
if (containsUrlOrDomain(message)) {
document.getElementById('messageError').style.display = 'block';
hasError = true;
}
// Validar el captcha
if (captcha !== '<?php echo $sum; ?>') {
document.getElementById('captchaError').style.display = 'block';
hasError = true;
}
// Si hay errores, detener el envío
if (hasError) {
return;
}
// Enviar el formulario si no hay errores
var formData = new FormData(this);
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(data => {
// Mostrar mensaje de éxito
document.getElementById('successMessage').style.display = 'block';
// Ocultar el formulario
document.getElementById('errorForm').style.display = 'none';
// Esperar 3 segundos y cerrar el modal
setTimeout(function() {
document.getElementById('errorModalOverlay').style.display = 'none';
document.getElementById('successMessage').style.display = 'none';
document.getElementById('errorForm').style.display = 'block'; // Restaurar el formulario
}, 3000);
})
.catch(error => console.error('Error:', error));
});
</script>
<?php
return ob_get_clean();
}
add_shortcode('error_report_form', 'error_report_form_shortcode');
// Manejar el envío del formulario
function handle_error_report_form() {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'submit_error_report') {
// Iniciar sesión si no está iniciada
if (!session_id()) {
session_start();
}
// Validar el captcha
if (!isset($_SESSION['captcha_sum']) || !isset($_POST['captcha']) || $_POST['captcha'] != $_SESSION['captcha_sum']) {
wp_die('Captcha incorrecto. Por favor, intenta de nuevo.');
}
// Validar que no haya enlaces o dominios en los campos
$name = isset($_POST['name']) ? sanitize_text_field($_POST['name']) : '';
$message = isset($_POST['message']) ? sanitize_text_field($_POST['message']) : '';
if (preg_match('/(http|https|www|\[url|\[link)|(\b\w+\.(com|org|net|io|co|edu|gov|mil|biz|info|xyz|me|tv|us|uk|ca|au|de|fr|es|it|nl|ru|jp|cn|in|br|mx|ar|cl|pe|ve|co\.uk|co\.jp|co\.in|ac\.uk|gov\.uk|edu\.au|com\.au|org\.uk|net\.au)\b)/i', $name . $message)) {
wp_die('No se permiten enlaces o dominios en los campos.');
}
// Validar reCaptcha (opcional)
if (defined('RECAPTCHA_SITE_KEY') && defined('RECAPTCHA_SECRET_KEY')) {
if (!isset($_POST['g-recaptcha-response'])) {
wp_die('Por favor, completa el reCaptcha.');
}
$recaptcha_response = $_POST['g-recaptcha-response'];
$recaptcha_secret = RECAPTCHA_SECRET_KEY;
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_data = [
'secret' => $recaptcha_secret,
'response' => $recaptcha_response,
'remoteip' => $_SERVER['REMOTE_ADDR']
];
$recaptcha_options = [
'http' => [
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($recaptcha_data)
]
];
$recaptcha_context = stream_context_create($recaptcha_options);
$recaptcha_result = file_get_contents($recaptcha_url, false, $recaptcha_context);
$recaptcha_json = json_decode($recaptcha_result);
if (!$recaptcha_json->success) {
wp_die('Por favor, completa el reCaptcha correctamente.');
}
}
// Obtener la URL de origen
$url = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : 'URL desconocida';
// Enviar correo electrónico
$to = get_option('admin_email'); // Correo del administrador
$subject = 'Comunicación de Error desde ' . $url;
$body = "Nombre: $name\n\nMensaje: $message\n\nURL: $url";
$headers = array('Content-Type: text/plain; charset=UTF-8');
if (wp_mail($to, $subject, $body, $headers)) {
echo 'success';
} else {
echo 'Hubo un error al enviar el mensaje.';
}
exit;
}
}
add_action('wp_ajax_submit_error_report', 'handle_error_report_form');
add_action('wp_ajax_nopriv_submit_error_report', 'handle_error_report_form');
// Definir claves de reCaptcha (opcional)
// define('RECAPTCHA_SITE_KEY', 'TU_SITE_KEY');
// define('RECAPTCHA_SECRET_KEY', 'TU_SECRET_KEY');Funcionalidad del formulario
- Shortcode:
[error_report_form]- Muestra un botón que abre un formulario modal para reportar errores.
- El formulario incluye campos para nombre, mensaje y un captcha matemático simple.
- Valida que no se ingresen enlaces o dominios en los campos.
- Envía un correo electrónico al administrador del blog con los detalles del error.
- Campos del formulario:
- Nombre: Campo de texto para ingresar el nombre del usuario.
- Mensaje: Área de texto para describir el error.
- Captcha: Pregunta matemática simple (suma de dos números aleatorios).
- Validaciones:
- No se permiten enlaces o dominios en los campos "Nombre" y "Mensaje". Se puede mejorar añadiendo extensiones de dominio faltantes.
- El captcha debe ser resuelto correctamente.
- Si se usa reCaptcha (opcional), se valida antes de enviar el formulario.
- Envío del formulario:
- Usa AJAX para enviar los datos sin recargar la página.
- Muestra un mensaje de éxito en verde si el envío es correcto.
- Muestra mensajes de error en rojo bajo cada campo en el que se falle.
- Cierra el modal automáticamente después de 3 segundos de ser enviado con éxito.
- La ventana modal se puede cerrar con un aspa de su esquina superior derecha o pinchando en cualquier lugar fuera de ella.
- Prevención de spam:
- Captcha matemático.
- Opcionalmente, se puede integrar reCaptcha de Google.
Estilos del formulario
- Botón "Comunicar error":
- Color de fondo: #ffcc00 (amarillo).
- Texto: Negro y en negrita.
- Esquinas redondeadas: 6px.
Símbolo de advertencia: ⚠- Se añade símbolo de adventencia con icono SVG (concretamente este)
- Modal (Ventana emergente):
- Fondo oscuro semitransparente.
- Contenedor blanco con esquinas redondeadas: 7px.
- Sombra suave alrededor del modal.
- Aspa de cierre (Botón para cerrar el modal su clase es id="closeModalBtn"):
- Fondo: Negro (#000000).
- Color del aspa: Blanco (#ffffff).
- Tamaño: 30px de ancho y 30px de alto.
- Forma: Círculo perfecto (border-radius: 50%).
- Posición: Superior derecha, con un margen de 10px desde el borde.
- Centrado del aspa: Usa display: flex, align-items: center, y justify-content: center para centrar el aspa (×) dentro del círculo.
- Tamaño del aspa: 20px.
- Campos del formulario:
- Fondo gris claro: #f5f5f5.
- Borde: 1px sólido #ddd.
- Esquinas redondeadas: 4px.
- Texto: Tamaño reducido en 2px (14px para etiquetas, 12px para mensajes de error).
- Botón "Enviar":
- Centrado en el formulario.
- Color de fondo: #bc080e (rojo oscuro).
- Texto: Blanco y en negrita.
- Esquinas redondeadas: 4px.
- Mensajes de error:
- Color: Rojo (#ff0000).
- Tamaño de fuente: 12px.
- Mensaje de éxito:
- Color: Verde (#00ff00).
- Tamaño de fuente: 14px.
- Centrado debajo del formulario.
Cómo modificar los estilos
- Modificar colores:
- Busca las propiedades background-color, color, y border-color en el código.
- Cambia los valores hexadecimales (#ffcc00, #bc080e, #f5f5f5, etc.) por los colores deseados.
- Modificar tamaños de fuente:
- Busca las propiedades font-size en el código.
- Ajusta los valores en píxeles (14px, 12px, etc.) según sea necesario.
- Modificar bordes y esquinas redondeadas:
- Busca las propiedades border-radius y border
- Cambia los valores en píxeles (6px, 7px, 4px, etc.) para ajustar el redondeo de esquinas.
- Modificar espaciado:
- Busca las propiedades padding y margin
- Ajusta los valores en píxeles (10px, 20px, 12px, etc.) para cambiar el espaciado interno y externo.
- Modificar el ancho de los campos:
- Busca las propiedades width en los campos de texto y textarea.
- Cambia el valor (100%, 50%, etc.) para ajustar el ancho de los campos.
Opciones adicionales
- reCaptcha:
- Para habilitar reCaptcha, descomenta las líneas al final del código y define las claves:
define('RECAPTCHA_SITE_KEY', 'TU_SITE_KEY');
define('RECAPTCHA_SECRET_KEY', 'TU_SECRET_KEY'); - Obtén las claves desde: https://www.google.com/recaptcha/admin/
- Para habilitar reCaptcha, descomenta las líneas al final del código y define las claves:
- Personalización del correo electrónico:
- El correo enviado al administrador incluye:
- Nombre del usuario que se haya añadido al formulario.
- Mensaje del error que se haya añadido en este campo.
- URL de la página desde donde se envió el formulario (Se asume que el error se encontró en la página donde se pulsó el botón).
- Al no recopilar, enviar, ni guardar ningún dato privado, el formulario es respetuoso con la privacidad.
- Para modificar el contenido del correo que se recibe, edita la variable $body en la función handle_error_report_form
Instrucciones de uso
- Copia y pega el código en el archivo functions.php de tu tema.
- Usa el shortcode en cualquier página o entrada.
- Personaliza los estilos según sea necesario.
- Si deseas usar reCaptcha, sigue las instrucciones en la sección "Opciones Adicionales".
Preguntas frecuentes
¿Qué ventajas tiene crear una ventana modal con código propio frente a un plugin?
La principal ventaja es el rendimiento y la limpieza del código. Los plugins de modales suelen cargar librerías pesadas y scripts en todas las páginas de tu sitio. Con un método manual, solo utilizas el HTML, CSS y el JS estrictamente necesario, lo que mejora la velocidad de carga (WPO) y evita conflictos con otros elementos de la web.
¿Es necesario usar librerías externas como jQuery para que el modal funcione?
No es necesario. El método descrito utiliza JavaScript "Vanilla" (puro), que es mucho más ligero y rápido. Al prescindir de jQuery u otras librerías externas, reduces las peticiones HTTP y aseguras que el modal sea compatible con las versiones más modernas de los navegadores y de WordPress.
¿Cómo se puede cerrar la ventana modal de forma sencilla?
Para una buena experiencia de usuario, el modal incluye tres formas de cierre: un botón de "Cerrar" (típicamente una "X"), la posibilidad de hacer clic fuera del contenido del modal (en el fondo oscurecido) y, para usuarios avanzados, la opción de cerrarlo pulsando la tecla "Esc" del teclado.
¿Dónde se debe añadir el código CSS y JS en WordPress?
El código CSS puede añadirse en el Personalizador de WordPress o en la hoja de estilos de tu tema hijo. El JavaScript y el HTML pueden insertarse mediante un bloque de "HTML personalizado" en el editor de bloques o, si prefieres que esté disponible en todo el sitio, a través del archivo functions.php o un plugin de snippets.
¿Es accesible el modal para personas con discapacidad visual?
Sí, al construirlo manualmente podemos asegurar la accesibilidad utilizando atributos ARIA. Es importante definir correctamente los roles (como role="dialog") y asegurar que el foco del teclado se mueva dentro de la ventana modal cuando esta se abre, cumpliendo así con los estándares de accesibilidad web.













Hey, esta bueno. Sigo al blog ahora