
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.0
* Última actualización: 13-03-2025 17:54:00
*/
// 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;">
⚠ 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: ⚠
- 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".