Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I want to set up a page with a form the visitor needs to fill out and accept terms before going on to another page. Is there a plugin to do this? If no plug in, is there another way?

Any help would be appreciated!

Greg

share|improve this question
add comment

2 Answers

I would use a shortcode for this. Wrap the content you want behind the "terms_required" shortcode, if the user hasn't agreed to the terms, show them the form.

The form would submit to a custom rewrite rule (url endpoint) that validates the form and sets a cookie if everything checks out.

Let's start by wrapping everything in a class -- there are a few class constants here that we'll use later.

<?php
class WPSE_52793
{
    /**
     * Query variable we'll use for rewrites and catching the form handler
     *
     */
    const Q_VAR = 'wpse52793_handler';

    /**
     * Form key field name.
     *
     */
    const F_KEY = 'wpse52793_accepted';

    /**
     * Form field nonce.
     *
     */
    const NONCE = 'wpse52793_fnonce';

    /**
     * Cookie key.
     *
     */
    const COOKIE = 'wpse52793_agreed';

    /********** Basic setup fluff **********/

    private static $ins = null;

    public static function instance()
    {
        is_null(self::$ins) && self::$ins = new self;
        return self::$ins;
    }

    public static function init()
    {
        add_action('plugins_loaded', array(self::instance(), '_setup'));
    }

    public function _setup()
    {
         // we'll add actions here later
    }
}

We can add the shortcode: hook into init, call add shortcode and register the callback. The callback just checks for the cookie. If it's there, display the content -- if not display the terms and the form.

<?php
class WPSE_52793
{
    // snip snip

    public function _setup()
    {
        add_action('init', array($this, 'shortcode'));

    }

    public function shortcode()
    {
        add_shortcode('terms_required', array($this, 'shortcode_cb'));
    }

    public function shortcode_cb($opts, $content=null)
    {
        if(!empty($_COOKIE[self::COOKIE]))
            return $content;

        // if we're here, they haven't agreed. Show the terms.
        ob_start();
        // Your terms here!
        ?>
        <form method="post" action="<?php echo home_url('/terms-handler/'); ?>">
            <?php wp_nonce_field(self::NONCE, self::NONCE, false); ?>
            <input type="hidden" name="page_id" value="<?php echo get_queried_object_id(); ?>" />
            <label for="agree">
                <input type="checkbox" name="<?php echo esc_attr(self::F_KEY); ?>" value="agree" id="agree" />
                <?php esc_html_e('I agree to these terms and conditions.', 'wpse'); ?>
            </label>
            <p><input type="submit" value="<?php esc_attr_e('Submit', 'wpse'); ?>" /></p>
        </form>
        <?php
        return ob_get_clean();
    }
}

The rewrite rule is a bit more complex: hook into init, call add_rewrite_rule. We also need to filter query_vars and add a custom query var that we can use later.

<?php
class WPSE_52793
{
    // snip snip

    public function _setup()
    {
        add_action('init', array($this, 'rewrite'));
        add_filter('query_vars', array($this, 'add_var'));
        add_action('init', array($this, 'shortcode'));
    }

    public function rewrite()
    {
        add_rewrite_rule(
            '^terms-handler/?$',
            'index.php?' . self::Q_VAR . '=1',
            'top'
        );
    }

    public function add_var($v)
    {
        $v[] = self::Q_VAR;
        return $v;
    }

    // snip snip
}

The final piece of the puzzle handling the form submit. To do that, hook into template_redirect and check for our custom query variable. If it's there, validate the form: check the nonces, make sure we have a page to redirect them to, and ensure that they actually agreed. If everything is okay, set a cookie and send them back to the original page.

<?php
class WPSE_52793
{
    // snip snip

    public function _setup()
    {
        add_action('init', array($this, 'rewrite'));
        add_filter('query_vars', array($this, 'add_var'));
        add_action('template_redirect', array($this, 'catch_handler'));
        add_action('init', array($this, 'shortcode'));
    }

    // snip snip

    public function catch_handler()
    {
        if(!get_query_var(self::Q_VAR))
            return;

        // only allow post request, check for empty post, and make sure we
        // have a page_id
        if(
            'POST' != $_SERVER['REQUEST_METHOD'] ||
            empty($_POST) ||
            empty($_POST['page_id'])
        ) {
            wp_redirect(home_url());
            exit;
        }

        // if we're here everything should be good.
        // fetch the permalink
        $r = get_permalink(absint($_POST['page_id']));

        if(!$r)
        {
            // bad permalink for some reason, bail
            wp_redirect(home_url());
            exit;
        }

        if(
            !isset($_POST[self::NONCE]) ||
            !wp_verify_nonce($_POST[self::NONCE], self::NONCE) ||
            empty($_POST[self::F_KEY])
        ) {
            // bad nonce or they didn't check the box, try again
            wp_redirect($r);
            exit;
        }

        // whew, they've agreed.  Set a cookie, and send them back to the page.
        setcookie(
            self::COOKIE,
            '1',
            strtotime('+30 Days'), // might want to change this?
            '/',
            COOKIE_DOMAIN, // WP constant
            false,
            true
        );

        wp_redirect($r);
        exit;
    }
}

The other improve that would be worth making is using the settings API to add a field in the admin area where you can the terms that end up displaying on the front end.

To do that, hook into admin_init, use register_setting along with add_settings_section and add_settings_field. We also need to modify the shortcode callback a bit to use the new option. The result is below, and all of this mess is in plugin form for you to checkout and modify to suit your needs.

<?php
class WPSE_52793
{
    // snip snip

    /**
     * Settings key.
     *
     */
    const SETTING = 'wpse52793_options';

    // snip snip

    public function _setup()
    {
        add_action('init', array($this, 'rewrite'));
        add_filter('query_vars', array($this, 'add_var'));
        add_action('template_redirect', array($this, 'catch_handler'));
        add_action('init', array($this, 'shortcode'));
        add_action('admin_init', array($this, 'settings'));
    }

    // snip snip

    public function shortcode_cb($opts, $content=null)
    {
        if(!empty($_COOKIE[self::COOKIE]))
            return $content;

        // if we're here, they haven't agreed. Show the terms.
        ob_start();
        ?>
        <div class="terms-and-conditions">
            <?php echo apply_filters('wpse52793_terms', get_option(self::SETTING, '')); ?>
        </div>
        <form method="post" action="<?php echo home_url('/terms-handler/'); ?>">
            <?php wp_nonce_field(self::NONCE, self::NONCE, false); ?>
            <input type="hidden" name="page_id" value="<?php echo get_queried_object_id(); ?>" />
            <label for="agree">
                <input type="checkbox" name="<?php echo esc_attr(self::F_KEY); ?>" value="agree" id="agree" />
                <?php esc_html_e('I agree to these terms and conditions.', 'wpse'); ?>
            </label>
            <p><input type="submit" value="<?php esc_attr_e('Submit', 'wpse'); ?>" /></p>
        </form>
        <?php
        return ob_get_clean();
    }

    /**
     * Hooked into `admin_init` -- registers the custom setting and adds a new
     * field and section to the Options > Reading page.
     *
     * @access  public
     * @uses    register_setting
     * @uses    add_settings_section
     * @uses    add_settings_field
     * @return  void
     */
    public function settings()
    {
        register_setting(
            'reading',
            self::SETTING,
            array($this, 'validate_cb')
        );

        add_settings_section(
            'terms-conditions',
            __('Terms and Conditions', 'wpse'),
            array($this, 'section_cb'),
            'reading'
        );

        add_settings_field(
            'terms-conditions',
            __('Terms & Conditions', 'wpse'),
            array($this, 'field_cb'),
            'reading',
            'terms-conditions',
            array('label_for' => self::SETTING)
        );
    }

    /**
     * Settings validation callback.  Checks to see if the user can post
     * unfiltered html and return the raw text or a kses filter string
     * where appropriate.
     *
     * @access  public
     * @uses    current_user_can
     * @uses    wp_filter_post_kses
     * @return  string
     */
    public function validate_cb($dirty)
    {
        return current_user_can('unfiltered_html') ?
            $dirty : wp_filter_post_kses($dirty);
    }

    /**
     * Callback for the settings section.  Displays some help text.
     *
     * @access  public
     * @uses    esc_html__
     * @return  void
     */
    public function section_cb()
    {
        echo '<p class="description">',
            esc_html__('The terms and conditions that get displayed when a user '.
                'visits a page protected by the `terms_required` shortcode.', 'wpse'),
            '</p>';
    }

    /**
     * Callback for the settings field.  Creates a new editor on the screen.
     *
     * @access  public
     * @uses    wp_editor
     * @uses    get_option
     * @return  void
     */
    public function field_cb($args)
    {
        wp_editor(
            get_option(self::SETTING, ''),
            self::SETTING,
            array(
                'wpautop'       => false,
                'textarea_rows' => 10,
            )
        );
    }
}
share|improve this answer
add comment

Place this into your functions.php file

function redirect_to($location, $status) {
    if(is_page('slug-name-here') ) {

    if ( !isset($_POST['terms-accepted'])) {
                $location =  get_bloginfo('url') . '/terms/';
    }
    return $location;
  }

}
add_filter('wp_redirect', 'redirect_to', 10, 2);
  • slug-name-here = the page to access after accepting your terms
  • $location = the path to your terms page
  • $_POST['terms-accepted'] = a hidden input field with the name="terms-accepted"

    example: <input type="hidden" name="action" value="" />
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.