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?
"https://wordpress.stackexchange.com/",
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,
)
);
}
}