Google reCAPTCHA en el formulario de comentarios de WordPress

Cómo añadir el reCAPTCHA de Google en el formulario de comentarios de WordPress

Hace unos meses decidí eliminar el sistema de comentarios de Disqus ya que, entre otras cosas, estaba perjudicándome seriamente el rendimiento de la página.

Además, últimamente (IMHO) estaban abusando de meter publicidad en la versión gratuita del mismo.

Esta «decisión» mejoró el performance de los artículos pero trajo consigo una consecuencia desagradable. Al utilizar el formulario de comentarios por defecto de WordPress me empezó a entrar mucho SPAM, así que pensé en incluir el reCaptcha de Google para evitarlo. No he conseguido evitarlo al 100%, sigue entrando algo de SPAM pero es poco (casi residual comparado a antes de ponerlo).

El plugin de ecommerce más fácil de usar para WordPress

Y decidí escribir sobre cómo lo hice y aquí van los pasos que seguí:

Añade la plantilla de comentarios en el detalle de tus posts

Simplemente ve a la plantilla single.php de tu tema y asegúrate de poner las siguientes líneas:

<?php

if (comments_open() || get_comments_number()) {
    comments_template();
}

Creo que se entiende bastante fácil, si tienes los comentarios abiertos o si tienes algún comentario, llama a la función de WordPress comments_template() que es la que se encargará de pintar el formulario de comentarios.

Por defecto va a cargar otra plantilla de tu tema (comments.php), pero podrías pasarle como argumento la ruta a un archivo personalizado si lo deseas.

Añade el campo reCaptcha de Google en la plantilla del formulario de comentarios

A continuación voy a pegar el código que he utilizado para el template comments.php en mi tema, que en su mayor parte está basado en el template por defecto de twentytwentyone, y a continuación explico línea por línea que hace cada cosa:

<?php
/**
 * The template for displaying comments.
 */
if (post_password_required()) {
    return;
}?>

<div id="comments" class="comments-area">
    <h3 class="comments-area-title">Comentarios</h3>

    <?php if (have_comments()) { ?>
        <h4 class="comments-title"><?php echo sprintf(_nx('%s comentario', '%s comentarios', get_comments_number(), 'comentarios', 'your_textdomain'), get_comments_number());?> en <?php echo get_the_title(); ?></h4>

        <ol class="comment-list">
            <?php wp_list_comments([
                'style' => 'ol',
                'short_ping' => true,
                'avatar_size' => 56,
            ]); ?>
        </ol>

        <?php if (get_comment_pages_count() > 1 && get_option('page_comments')) { ?>
            <ul class="pagination">
                <li class="previous"><?php echo get_previous_comments_link(__('Comentarios antiguos', 'your_textdomain')); ?></li>
                <li class="next"><?php echo get_next_comments_link(__('Comentarios recientes', 'your_textdomain')); ?></li>
            </ul>
        <?php }
    } // have_comments()

    if (! comments_open() && get_comments_number() && post_type_supports(get_post_type(), 'comments')) { ?>
        <p class="no-comments"><?php _e('Los comentarios están cerrados', 'your_textdomain'); ?></p>
    <?php }

    $comment_notes = '<p>Tu dirección de correo electrónico no será publicada... </p>';
    $field_author = '<label for="author">Nombre *</label><input type="text" id="author" name="author" class="form-control" required="required" placeholder="Nombre">';
    $field_email = '<label for="email">Email *</label><input type="email" id="email" name="email" class="form-control" required="required" placeholder="Email">';
    $field_comment = '<label for="comment">Mensaje *</label><textarea id="comment" name="comment" class="form-control" cols="45" rows="8" required="required" placeholder="Deja aquí tu comentario"></textarea>';
    $field_captcha = '<div class="g-recaptcha" data-sitekey="YOUR_SITE_KEY"></div>';
    $field_rgpd = '<label for="rgpd"><input type="checkbox" id="rgpd" name="rgpd" class="form-control" required="required" value="1"> He leído y acepto la ' . get_the_privacy_policy_link() . '</label>';

    $args = [
        'fields' => array(
            'author' => $field_author,
            'email' => $field_email,
            'captcha' => $field_captcha,
            'rgpd' => $field_rgpd,
        ),
        'comment_field' => $field_comment,
        'comment_notes_before' => $comment_notes,
        'id_form' => 'your-form-id',
        'id_submit' => 'your-submit-btn-id',
        'class_container' => 'comment-respond',
        'class_form' => 'contact-form',
        'class_submit' => 'btn btn-default',
        'name_submit' => 'submit',
        'title_reply' => esc_html__('Deja un comentario', 'your_textdomain'),
        'title_reply_to' => 'Leave a Reply to %s',
        'title_reply_before' => '<h3 id="reply-title" class="comment-reply-title">',
        'title_reply_after' => '</h3>',
        'label_submit' => 'Envía un comentario',
        'submit_button' => '<input type="submit" name="%1$s" id="%2$s" class="%3$s" value="%4$s"/>',
        'submit_field' => '<p class="form-submit">%1$s %2$s</p>',
    ];

    comment_form($args);?>
</div>

Lo primero es comprobar si el post está protegido con contraseña, y en caso de que el visitante no la haya introducido, no se cargan los comentarios.

A continuación se abre el HTML y se pinta un título para la sección de tus comentarios. Lo siguiente es comprobar si el post actual cuenta ya con comentarios a través de la función have_comments(), en caso afirmativo se pinta el listado de los mismos y la paginación si fuera necesario.

Y llegamos a la «chicha», la función de WordPress que pinta el formulario: comment_form(). Esta función acepta un array con multitud de argumentos, para información más detallada te recomiendo que visites el handbook.

Vamos a comentar algunos de ellos:

  • fields: Es un array que contiene los campos del formulario. Por defecto están author, email, url y cookies, aunque puedes añadir los que quieras. El valor de cada clave del array que introduzcas aquí debe contener el HTML del campo del formulario, por lo tanto, tienes total libertad para añadir las etiquetas HTML, classes, IDs, etc… que necesites para integrarlo perfectamente con tu diseño.
  • comment_field: Aquí podrás incluir el HTML necesario para crear el campo principal del formulario de comentarios, es decir, donde los usuarios dejarán su respuesta.
  • comment_notes_before: Aquí podrás incluir HTML para definir un texto (o lo que quieras) antes del formulario de contacto. Viene bien para advertir a tus usuarios sobre cómo funciona el sistema de comentarios, si tienes moderación, el tratamiento de los datos, etc…

El resto de opciones las puedes utilizar para establecer IDs y classes CSS, y markup HTML en las etiquetas de formulario, contenedor, botón de submit, etc…

Como puedes ver en este ejemplo, en uno de los campos del array de fields ($field_captcha) he añadido el HTML que te facilita el reCaptcha de Google (que no es más que una etiqueta div con la clase g-recaptcha y el sitekey que obtienes una vez te has dado de alta allí).

Sólo quedaría incluir el script que te facilita Google reCaptcha. Podrías concatenar la llamada al script en la variable $field_captcha, pero en mi opinión es mejor hacer uso del hook comment_form:

<?php

function load_recaptcha_js() {?>
    <script src="https://www.google.com/recaptcha/api.js" defer="defer"></script>
<?php }
add_action('comment_form', 'load_recaptcha_js');

Validando el reCaptcha en el formulario de comentarios de WordPress

Pues ya tenemos el campo captcha en el formulario de comentarios, ahora es necesario validarlo. Pero antes de validarlo, vamos a comprobar que el usuario ha rellenado todos los campos que son obligatorios y a comprobar que los datos tienen el formato que deseamos.

Para esto vamos a hacer una triple validación: HTML5, JavaScript y PHP.

Validación HTML5

Aquí pasa algo curioso, o al menos algo que no logro comprender porqué WordPress funciona así. Uno de los parámetros que podemos establecer en la función que vimos antes de comment_form() es el parámetro format, que según la documentación:

(string) The comment form format. Default ‘xhtml’. Accepts ‘xhtml’, ‘html5’.

WORDPRESS HANDBOOK

Sería lógico pensar que si lo establecemos como html5, el comportamiento esperado es que se validara el formulario con HTML5. Si no establecemos nada, la función comment_form() establecerá el valor en función de si nuestro tema tiene en los supports de html5 el valor comment-form.

El caso es que si de una forma u otra nuestro form lo establecemos como html5, esto agregará al formulario de comentarios el atributo novalidate, y el formulario no se validará por HTML5. Así que os propongo eliminar este atributo por JavaScript. Podemos utilizar el mismo hook donde hemos añadido anteriormente el script del reCaptcha, quedando de este modo:

<?php

function load_recaptcha_js() {?>
    <script src="https://www.google.com/recaptcha/api.js" defer="defer"></script>
    <script>
        let form = document.getElementById('your-form-id');
        form.removeAttribute('novalidate');
    </script>
<?php }
add_action('comment_form', 'load_recaptcha_js');

Sencillo. Simplemente obtenemos el formulario a través de su ID, y eliminamos el atributo novalidate. De este modo, ahora si tratamos de hacer submit y nuestro navegador soporta HTML5, no nos dejará enviarlo si no están rellenados los campos que hemos marcado como required, y tampoco si campos como el email (que están establecidos con type=»email») no tienen el formato correcto.

Validación JavaScript

Si el navegador del usuario no soporta HTML5, la validación que hemos visto anteriormente no sirve de nada, así que adicionalmente haremos una validación por JavaScript para al menos no enviar el formulario si los campos no están rellenos. Utilizaremos el mismo hook comment_form, quedando entonces de este modo:

<?php

function load_recaptcha_js() {?>
    <script src="https://www.google.com/recaptcha/api.js" defer="defer"></script>
    <script>
        let form = document.getElementById('your-form-id');
        form.removeAttribute('novalidate');

        form.addEventListener('submit', function(e) {
            let comment = form.querySelector('#comment').value;
            let author = form.querySelector('#author').value;
            let email = form.querySelector('#email').value;
            let response = grecaptcha.getResponse();

            if (typeof(comment) == "undefined" || !comment) {
                alert("El mensaje es obligatorio");
                e.preventDefault();
                return;
            }

            if (typeof(author) == "undefined" || !author) {
                alert("Tu nombre es obligatorio");
                e.preventDefault();
                return;
            }

            if (typeof(email) == "undefined" || !email) {
                alert("Tu email es obligatorio (no se publicará)");
                e.preventDefault();
                return;
            }

            if (response.length === 0) {
                alert("Debes validar el ReCaptcha");
                e.preventDefault();
                return;
            }
    });
    </script>
<?php }
add_action('comment_form', 'load_recaptcha_js');

Esto lo puedes tunear a tu gusto según tus necesidades, añadiendo cuantas validaciones/comprobaciones necesites. Simplemente en el evento submit del formulario, obtenemos los campos y si no están rellenos lanzamos un alert y cortamos la ejecución evitando que se envíe el formulario.

Validación PHP

Por último, haremos también la validación en el lado del servidor, y aquí además comprobaremos que el usuario ha pasado el reCaptcha correctamente. Para esto utilizaremos el hook pre_comment_on_post.

<?php

function validate_form(int $comment_post_ID)
{
    if (is_user_logged_in()) {
        return true;
    }

    $error_msg = '';
    $comment = esc_attr($_POST['comment']);
    $author = esc_attr($_POST['author']);
    $email = esc_attr($_POST['email']);
    $recaptcha = esc_attr($_POST['g-recaptcha-response']);
    $rgpd = esc_attr($_POST['rgpd']);

    // Check empty fields and incorrect format
    if (empty($comment)) {
        $error_msg .= "<p>El Campo 'Mensaje' es obligatorio</p>";
    }

    if (empty($author)) {
        $error_msg .= "<p>El Campo 'Nombre' es obligatorio</p>";
    }

    if (empty($email)) {
        $error_msg .= "<p>El Campo 'Email' es obligatorio</p>";
    }

    if (! is_email($email)) {
        $error_msg .= "<p>El Campo 'Email' no tiene un formato válido</p>";
    }

    if (empty($recaptcha)) {
        $error_msg .= '<p>Debes completar el Captcha</p>';
    }else {
        // Check Google ReCaptcha
        $response = wp_remote_post('https://www.google.com/recaptcha/api/siteverify', [
            'method' => 'POST',
            'timeout' => 45,
            'redirection' => 5,
            'httpversion' => '1.0',
            'blocking' => true,
            'headers' => [],
            'body' => [
                'secret' => 'YOUR_SECRET_KEY',
                'response' => $recaptcha,
            ],
            'cookies' => [],
        ]);

        if (is_wp_error($response)) {
            $error_msg .= '<p>Validación Captcha no válida</p>';
        }

        $g_response = json_decode(wp_remote_retrieve_body($response));

        if (false === $g_response->success) {
            $error_msg .= '<p>Validación Captcha no válida</p>';
        }
    }

    if ('1' !== $rgpd) {
        $error_msg .= '<p>Lee y acepta la política de privacidad</p>';
    }

    if (! empty($error_msg)) {
        $error_msg .= "<p><a href='javascript:history.back()'>« Volver</a></p>";

        wp_die($error_msg);
    }

    return true;
}
add_action('pre_comment_on_post', 'validate_form');

Este hook se dispara antes de crearse el comentario, por lo tanto podremos finalizar la ejecución si algo no es correcto.

En primer lugar, si el usuario está logueado devolveremos directamente un true para que se guarde el comentario. El formulario que ve un usuario logueado es reducido y no hace falta comprobar los campos nombre, email, etc…

A continuación obtendremos los valores que el usuario ha enviado a través del formulario, e iremos comprobando uno por uno que existen y no están vacíos. En este ejemplo además, verificamos que el mail sea válido con la función is_email(). Si algo no cumple con lo que queremos, vamos almacenando un mensaje de error en la variable $error_msg, y si una vez realizadas todas la comprobaciones, esta variable contiene algo (es decir, hay al menos un error), finalizaremos el proceso mostrando el/los mensaje/s de error.

Una de estas validaciones corresponde al reCaptcha, para esto es necesario hacer una petición a la URL de Google para verificarlo. Para esto utilizamos la función wp_remote_post(). En el body hay que enviar el secret (que te provee Google cuando te registras) y la response (con la respuesta del usuario al captcha). Si la respuesta es correcta significa que el usuario ha pasado la validación y por lo tanto retornaremos un true para que el comentario siga su flujo y se guarde correctamente.

Con esto estaría todo. De este modo puedes evitar en gran medida que te entre SPAM en forma de comentarios.

¿Te ha resultado útil esta información? 🍺

Si este post te ha resuelto un problema, invítame a un café o a una cerveza. Con este pequeño gesto me animas a seguir escribiendo.

Comentarios

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *:

  • El fin del tratamiento es únicamente la moderación de comentarios para evitar spam
  • La legitimación es tu consentimiento al comentar
  • No se comunicará ningún dato a terceros salvo por obligación legal
  • Tienes derecho al acceso, rectificación y eliminación de los comentarios