I think what you want is a custom rewrite rule — specifically, a custom endpoint.
This would have to live outside s2member.
To start, wrap everything in a class:
<?php
class WPSE71804
{
// post type key, whatever this happens to be.
const TYPE = 'customer';
// endpoint mask, 2 ^ 18
const EP = 262144;
// key prefix, used for options
const PREFIX = 'wpse71804_key_';
// container for the instance of this class
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'));
}
// add actions and such.
public function _setup()
{
// we'll add actions here later.
}
}
There are some constants there that we’ll use later.
You’ll need to modify your post type registration to include a custom endpoint mask in the rewrite
argument.
<?php
class WPSE71804
{
// snip snip
// add actions and such.
public function _setup()
{
add_action('init', array($this, 'register'));
}
// register the post type
public function register()
{
// rewrite is the args to pay attention to we need
// to set a custom endpoint mask
register_post_type(self::TYPE, array(
'label' => __('Customers', 'wpse'),
'public' => true,
'rewrite' => array(
'slug' => 'customer',
'ep_mask' => self::EP,
'with_front' => false,
),
));
}
}
From there, we can hook into init
and call add_rewrite_endpoint
.
This sets up a rewrite so we can go to yoursite.com/customers/the-post/key/some_key_here
.
<?php
class WPSE71804
{
// snip snip
// add actions and such.
public function _setup()
{
add_action('init', array($this, 'register'));
add_action('init', array($this, 'endpoint'), 11);
}
// snip snip
public function endpoint()
{
add_rewrite_endpoint('key', self::EP);
}
}
Now it’s just a matter of hooking into template_redirect
and validating the key.
<?php
class WPSE71804
{
// snip snip
public static function init()
{
add_action('plugins_loaded', array(self::instance(), '_setup'));
register_activation_hook(__FILE__, array(__CLASS__, 'activate'));
}
// add actions and such.
public function _setup()
{
add_action('init', array($this, 'register'));
add_action('init', array($this, 'endpoint'), 11);
add_action('template_redirect', array($this, 'validate_key'));
}
// snip snip
public function validate_key()
{
// not a a singular customer page? Or have an admin user? bail.
if(!is_singular(self::TYPE) || current_user_can('manage_options'))
return;
if(!($_key = get_query_var('key')) || !($key = self::get_key($_key)))
{
global $wp_query;
$wp_query->set_404();
}
// if we're here, the key is okay, let the request go through
}
}
It might also be helpful to create a nice API to use (the above bit of code uses one of those methods).
<?php
class WPSE71804
{
// snip snip
/********** API **********/
// create a new key
public static function create_key()
{
$k = wp_generate_password(24, false);
self::update_key($k, 'notdone');
return $k;
}
// update a key
public static function update_key($key, $val="done")
{
return update_option(self::PREFIX . $key, $val);
}
// delete a key
public static function delete_key($key)
{
return delete_option(self::PREFIX . $key);
}
public static function get_key($key)
{
return get_option(self::PREFIX . $key);
}
}
Now you can use the above something like…
<?php
// create a key
$k = WPSE71804::create_key();
// send mail with key here!
// after they submit the survey, you might want to make a note of that.
WPSE71804::update_key($k, 'done');
// or maybe just delete it and revoke access to the page
WPSE71804::delete_key($k);
Not sure how well that will play along with s2member, but essentially this will block all access to the pages without a key on the front end. You may not need to restrict access with s2member at all. Here is all that as a plugin.