Specifying Word Limits for Textareas in Contact Form 7

Wordpress is a popular CMS with thousands of awesome free plugins. Contact Form 7 is a great free plugin that allows you to create forms for users to fill in and submit their information.  It's very flexible and allows you to highly customize each form.  It also has various built in field types such as email, phone number, and date fields. Recently I needed something I thought was rather simple - limiting the textarea field to X amount of words. CF7 allows you to specify a character limit, but not a word limit. I've searched for another Wordpress plugin to add in this functionality, but the only plugin I've come across is outdated and doesn't work. I decided to create my own plugin to add in this functionality, and I figured I would share it with you!

Removing Contact Form 7's default filters

The first thing we need to do is remove the existing filters that are validating the textarea.  If we don't do this, Contact Form 7 will still be trying to validate our field for a character limit and our custom validation won't do anything.

remove_filter( 'wpcf7_validate_textarea', 'wpcf7_textarea_validation_filter', 10 );
remove_filter( 'wpcf7_validate_textarea*', 'wpcf7_textarea_validation_filter', 10 );

Adding to the field shortcodes using filters

Contact Form 7 has built in functionality to limit fields to the number of characters by using the minlength and maxlength parameters. We need a way to specify that a field should be limited by the number of words and not characters.  I decided that adding maxlengthwords:true or minlengthwords:true would be the way I want to specify that.  We can then still use the existing minlength and maxlength attributes to specify the number of words we want as the minimum or maximum.

To add support for our new shortcode attribute, we need to add a filter to the textarea shortcode.  Contact Form 7 offers filters in the following format: wpcf7_validate_[TAGNAME] for non-required fields and wpcf7_validate_[TAGNAME]* for required fields.

add_filter( 'wpcf7_validate_textarea', 'cf7wl_textarea_validation_filter', 10, 2 );
add_filter( 'wpcf7_validate_textarea*', 'cf7wl_textarea_validation_filter', 10, 2 );
/**
 * Runs when validating a CF7 field.  We use this to override
 * the default textarea validation and replace it with our
 * custom word count validation
 *
 * @param $result
 * @param $tag
 * @return mixed
 */
function cf7wl_textarea_validation_filter( $result, $tag ) {

    $tag = new WPCF7_Shortcode( $tag );
    $name = $tag->name;
    $value = isset( $_POST[$name] ) ? (string) $_POST[$name] : '';

    // If this is a required field, make sure we
    // have a value.  If not, throw an error
    if ( $tag->is_required() && empty($value) ) {
        $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
    }

    // If we have a value, let's start the validation
    if ( ! empty( $value ) ) {
        $maxlengthwords = false;
        $minlengthwords = false;

        $maxlength = $tag->get_maxlength_option();
        $minlength = $tag->get_minlength_option();
        $inputlength = str_word_count( $value );

        $characterlength = strlen( $value );

        // If someone mistakenly set the minlength greater than the
        // maxlength, set both variables to null.
        if ( $maxlength && $minlength && $maxlength < $minlength ) {
            $maxlength = $minlength = null;
        }

        // Loop through each of the options/attributes in this tag
        // and check for our maxlengthwords and minlengthwords option/attribute
        foreach($tag->options as $key => $option ) {
            if ( stristr($option, 'maxlengthwords:true') ) {
                $maxlengthwords = true;
            }

            if ( stristr($option, 'minlengthwords:true') ) {
                $minlengthwords = true;
            }
        }

        // If this field needs to be validated for a maximum word length, check the
        // length of the input and if the length is greater than the maximum
        // length, throw an error.
        if ( $maxlengthwords === true ) {
            if ( $inputlength > intval($maxlength) ) {
                $result->invalidate( $tag, "Your input is too long ({$inputlength}/{$maxlength} maximum words)" );
            }
        }
        // If there's no maximum word length but a maximum length is set,
        // validate the character length
        else if ( $maxlength && $maxlength < $characterlength ) {
            $result->invalidate( $tag, wpcf7_get_message( 'invalid_too_long' ) );
        }

        // If this field needs to be validated for a minimum word length, check the
        // length of the input and if the length is less than the minimum length,
        // throw an error
        if ( $minlengthwords === true ) {
            if ( intval($inputlength) < intval($minlength) ) {
                $result->invalidate( $tag, "Your input is too short ({$inputlength}/{$minlength} minimum words)" );
            }
        }
        // If there's no minimum word length but a minimum length is set,
        // validate the character length
        else if ( $minlength && $characterlength < $minlength ) {
            $result->invalidate( $tag, wpcf7_get_message( 'invalid_too_short' ) );
        }
    }

    return $result;

}

 

Overriding the default textarea output

Contact Form 7 has character limits by default, and on the front end it will output the maxlength property in the textarea. That will cause issues with our word count, so we need to figure out how to remove that on fields with a word limit.  To do that, we're going to override the output of the [textarea] shortcode.

wpcf7_remove_shortcode( 'textarea' );
wpcf7_add_shortcode( array( 'textarea', 'textarea*' ), 'cf7wl_textarea_shortcode_handler', true );

/**
 * Outputs the HTML for the textarea shortcode.  We're replacing the default
 * CF7 textarea shortcode.  If the user wants a word limit instead of a character
 * limit, we need to remove the maxlength attribute from the element.
 *
 * @param $tag
 * @return string
 */
function textarea_shortcode_handler( $tag ) {

    $tag = new WPCF7_Shortcode( $tag );

    if ( empty( $tag->name ) )
        return '';

    $validation_error = wpcf7_get_validation_error( $tag->name );

    $class = wpcf7_form_controls_class( $tag->type );

    if ( $validation_error )
        $class .= ' wpcf7-not-valid';

    $atts = array();

    $atts['cols'] = $tag->get_cols_option( '40' );
    $atts['rows'] = $tag->get_rows_option( '10' );
    $atts['maxlength'] = $tag->get_maxlength_option();
    $atts['minlength'] = $tag->get_minlength_option();

    if ( $atts['maxlength'] &amp;&amp; $atts['minlength'] &amp;&amp; $atts['maxlength'] &lt; $atts['minlength'] ) {
        unset( $atts['maxlength'], $atts['minlength'] );
    }

    $maxlengthwords = false;
    $minlengthwords = false;

    // Check whether the field has a min or max word length
    foreach($tag->options as $key => $option ) {
        if ( stristr($option, 'maxlengthwords:true') ) {
            $maxlengthwords = true;
        }

        if ( stristr($option, 'minlengthwords:true') ) {
            $minlengthwords = true;
        }
    }

    // If this field has either the min or max word length validation,
    // remove the maxlength and minlength variables because we aren't
    // validating for character length
    if ( $maxlengthwords === true || $minlengthwords === true ) {
        unset( $atts['maxlength'], $atts['minlength'] );
    }

    $atts['class'] = $tag->get_class_option( $class );
    $atts['id'] = $tag->get_id_option();
    $atts['tabindex'] = $tag->get_option( 'tabindex', 'int', true );

    if ( $tag->has_option( 'readonly' ) ) {
        $atts['readonly'] = 'readonly';
    }

    if ( $tag->is_required() ) {
        $atts['aria-required'] = 'true';
    }

    $atts['aria-invalid'] = $validation_error ? 'true' : 'false';

    $value = empty( $tag->content )
        ? (string) reset( $tag->values )
        : $tag->content;

    if ( $tag->has_option( 'placeholder' ) || $tag->has_option( 'watermark' ) ) {
        $atts['placeholder'] = $value;
        $value = '';
    }

    $value = $tag->get_default_option( $value );

    $value = wpcf7_get_hangover( $tag->name, $value );

    $atts['name'] = $tag->name;

    $atts = wpcf7_format_atts( $atts );

    $html = sprintf(
        '<span class="wpcf7-form-control-wrap %1$s"><textarea s="">%3$s</textarea>%4$s</span>',
        sanitize_html_class( $tag->name ), $atts,
        esc_textarea( $value ), $validation_error );

    return $html;

}

Putting it all together

Because this functionality isn't theme related, it should be integrated as a Wordpress plugin. I've created a Github repo with a fully working version of this code. I also plan on submitting the plugin to Wordpress.org, and I'll update this post as soon as the plugin is approved.

Did you find this walkthrough useful? Would you have approached this differently? Feel free to comment below and let me know!

Comments

The content of this field is kept private and will not be shown publicly.