register_default_options();
// require the picatcha library
$this->require_library();
// register the hooks
$this->register_actions();
$this->register_filters();
}
function register_actions() {
// load the plugin's textdomain for localization
add_action('init', array(&$this, 'load_textdomain'));
// styling
add_action('wp_head', array(&$this, 'register_stylesheets')); // make unnecessary: instead, inform of classes for styling
add_action('admin_head', array(&$this, 'register_stylesheets')); // make unnecessary: shouldn't require styling in the options page
if ($this->options['show_in_registration'])
add_action('login_head', array(&$this, 'registration_style')); // make unnecessary: instead use jQuery and add to the footer?
// options
register_activation_hook(WPPluginPicatcha::path_to_plugin_directory() . '/wp-picatcha.php', array(&$this, 'register_default_options')); // this way it only happens once, when the plugin is activated
add_action('admin_init', array(&$this, 'register_settings_group'));
// only register the hooks if the user wants picatcha on the registration page
if ($this->options['show_in_registration']) {
// picatcha form display
if ($this->is_multi_blog())
add_action('signup_extra_fields', array(&$this, 'show_picatcha_in_registration'));
else
add_action('register_form', array(&$this, 'show_picatcha_in_registration'));
}
if ($this->options['show_in_lost_password']){
add_action('lostpassword_form', array(&$this, 'show_picatcha_in_registration'));
}
if($this->options['show_in_login']){ //$this->options['show_in_login']
add_action('login_form', array(&$this, 'show_picatcha_in_registration'));
}
// only register the hooks if the user wants picatcha on the comments page
if ($this->options['show_in_comments']) {
add_action('comment_form', array(&$this, 'show_picatcha_in_comments'));
add_action('wp_footer', array(&$this, 'save_comment_script')); // preserve the comment that was entered
// picatcha comment processing (look into doing all of this with AJAX, optionally)
add_action('wp_head', array(&$this, 'saved_comment'), 0);
add_action('preprocess_comment', array(&$this, 'check_comment'), 0);
add_action('comment_post_redirect', array(&$this, 'relative_redirect'), 0, 2);
}
// administration (menus, pages, notifications, etc.)
add_filter("plugin_action_links", array(&$this, 'show_settings_link'), 10, 2);
add_action('admin_menu', array(&$this, 'add_settings_page'));
// admin notices
add_action('admin_notices', array(&$this, 'missing_keys_notice'));
}
function register_filters() {
// only register the hooks if the user wants picatcha on the registration page
if ($this->options['show_in_registration']) {
// picatcha validation
if ($this->is_multi_blog())
add_filter('wpmu_validate_user_signup', array(&$this, 'validate_picatcha_response_wpmu'));
else
add_filter('registration_errors', array(&$this, 'validate_picatcha_response'));
// register for buddy press
add_action('bp_signup_validate', array(&$this,'picatcha_check_bp'));
add_action('bp_before_registration_submit_buttons', array(&$this,'bph_picatcha'));
}
if($this->options['show_in_login']){
if($this->is_multi_blog()){
add_filter('login_redirect', array(&$this, 'validate_picatcha_response_redirect'),10,3);
add_filter('login_errors', array(&$this, 'validate_picatcha_response_wpmu'));
}
else{
add_filter('login_redirect', array(&$this, 'validate_picatcha_response_redirect'),10,3);
add_filter('login_errors', array(&$this, 'validate_picatcha_response'));
}
}
if($this->options['show_in_lost_password']){
// picatcha validation
if ($this->is_multi_blog())
add_filter('lostpassword_post', array(&$this, 'validate_picatcha_response'));
else
add_filter('lostpassword_post', array(&$this, 'check_picatcha_lost_password')); //validate_picatcha_response
}
}
function load_textdomain() {
load_plugin_textdomain('picatcha', false, 'languages');
}
// set the default options
function register_default_options() {
if ($this->options)
return;
$option_defaults = array();
$old_options = WPPluginPicatcha::retrieve_options("picatcha");
if ($old_options) {
$option_defaults['public_key'] = $old_options['pubkey']; // the public key for Picatcha
$option_defaults['private_key'] = $old_options['privkey']; // the private key for Picatcha
// placement
$option_defaults['show_in_comments'] = $old_options['re_comments']; // whether or not to show Picatcha on the comment post
$option_defaults['show_in_registration'] = $old_options['re_registration']; // whether or not to show Picatcha on the registration page
$option_defaults['show_in_lost_password'] = $old_options['show_in_lost_password']; // whether or not to show when the user loses his/her password
$option_defaults['show_in_login'] = $old_options['show_in_login']; // whether or not to require when the user is logging in
$option_defaults['timedelta'] = $old_options['timedelta']; //The minimum amount of time a visitor must be on the page in order to post a legit comment
$option_defaults['timedelta_activation'] = $old_options['timedelta_activation']; // Activation of timedelta
$option_defaults['picatcha_timedelta_dropdown'] = $old_options['picatcha_timedelta_dropdown']; // Where comments go to when caught by TimeDelta
$option_defaults['send_repudata'] = $old_option['send_repudata']; // Repudata on
$option_defaults['reputation_user_levels'] = $old_option['reputation_user_levels']; // Repudata choice
// bypass levels
$option_defaults['bypass_for_registered_users'] = ($old_options['re_bypass'] == "on") ? 1 : 0; // whether to skip Picatcha for registered users
$option_defaults['minimum_bypass_level'] = $old_options['re_bypasslevel']; // who doesn't have to do the Picatcha (should be a valid WordPress capability slug)
if ($option_defaults['minimum_bypass_level'] == "level_10") {
$option_defaults['minimum_bypass_level'] = "activate_plugins";
}
// styling
$option_defaults['comments_theme'] = $old_options['re_theme']; // the default theme for Picatcha on the comment post
$option_defaults['registration_theme'] = $old_options['re_theme_reg']; // the default theme for Picatcha on the registration form
$option_defaults['picatcha_language'] = $old_options['re_lang']; // the default language for Picatcha
$option_defaults['xhtml_compliance'] = $old_options['re_xhtml']; // whether or not to be XHTML 1.0 Strict compliant
$option_defaults['comments_tab_index'] = $old_options['re_tabindex']; // the default tabindex for Picatcha
$option_defaults['registration_tab_index'] = 30; // the default tabindex for Picatcha
$option_defaults['display_powered_by'] = $old_options['display_powered_by']; //if it shows 'powered by picatcha'
$option_defaults['picatcha_captcha_format'] = $old_options['picatcha_captcha_format'];
$option_defaults['picatcha_image_size'] = $old_options['picatcha_image_size'];
$option_defaults['picatcha_image_noise_level'] = $old_options['picatcha_image_noise_level'];
$option_defaults['picatcha_image_noise_type'] = $old_options['picatcha_image_noise_type'];
$option_defaults['picatcha_style_color'] = $old_options['picatcha_style_color'];
$option_defaults['picatcha_use_ssl'] = $old_options['picatcha_use_ssl'];
// error handling
$option_defaults['no_response_error'] = $old_options['error_blank']; // message for no CAPTCHA response
$option_defaults['incorrect_response_error'] = $old_options['error_incorrect']; // message for incorrect CAPTCHA response
}
else {
// keys
$option_defaults['public_key'] = ''; // the public key for Picatcha
$option_defaults['private_key'] = ''; // the private key for Picatcha
// placement
$option_defaults['show_in_comments'] = 1; // whether or not to show Picatcha on the comment post
$option_defaults['show_in_registration'] = 1; // whether or not to show Picatcha on the registration page
$option_defaults['display_powered_by'] = 1; //whether or not to show "Powered by Picatcha"
$option_defaults['show_in_lost_password'] = 0; //defaults to not show in password recovery page
$option_defaults['show_in_login'] = 0; //defaults to not show when the user logs in
$option_defaults['timedelta_activation'] = 0;
$option_defaults['timedelta'] = 60; //Require a minimum of 60 seconds on the page in order to have a legit comment
$option_defaults['picatcha_timedelta_dropdown'] = 'spam'; // By default, cause comments caught by TimeDelta to go to spam
$option_defaults['send_repudata'] = 0;
$option_defaults['reputation_user_levels'] = 0;
// bypass levels
$option_defaults['bypass_for_registered_users'] = 1; // whether to skip Picatcha for registered users
$option_defaults['minimum_bypass_level'] = 'read'; // who doesn't have to do the Picatcha (should be a valid WordPress capability slug)
// styling
$option_defaults['comments_theme'] = 'red'; // the default theme for Picatcha on the comment post
$option_defaults['registration_theme'] = 'red'; // the default theme for Picatcha on the registration form
$option_defaults['picatcha_language'] = 'en'; // the default language for Picatcha
$option_defaults['language_override'] = 0; //allow users to override the language on the Picatcha form
$option_defaults['xhtml_compliance'] = 0; // whether or not to be XHTML 1.0 Strict compliant
$option_defaults['comments_tab_index'] = 5; // the default tabindex for Picatcha
$option_defaults['registration_tab_index'] = 30; // the default tabindex for Picatcha
$option_defaults['picatcha_captcha_format'] = '2';
$option_defaults['picatcha_image_size'] = 75;
$option_defaults['picatcha_image_noise_level'] = 0;
$option_defaults['picatcha_image_noise_type'] = 0;
$option_defaults['picatcha_style_color'] = '2a1f19';
$option_defaults['picatcha_use_ssl'] = 0;
// error handling
$option_defaults['no_response_error'] = 'ERROR: Please select relevant images from the grid'; // message for no CAPTCHA response
$option_defaults['incorrect_response_error'] = 'ERROR: The images selected were incorrect. Please try again.'; // message for incorrect CAPTCHA response
}
// add the option based on what environment we're in
WPPluginPicatcha::add_options($this->options_name, $option_defaults);
}
// require the Picatcha library
function require_library() {
require_once($this->path_to_plugin_directory() . '/picatcha/picatchalib.php');
}
// register the settings
function register_settings_group() {
register_setting("picatcha_options_group", 'picatcha_options', array(&$this, 'validate_options'));
}
// todo: make unnecessary
function register_stylesheets() {
$path = WPPluginPicatcha::url_to_plugin_directory() . '/picatchaWP.css';
echo '';
}
// stylesheet information
// todo: this 'hack' isn't nice, try to figure out a workaround
function registration_style() {
$path = WPPluginPicatcha::url_to_plugin_directory() . '/picatchaWP.css';
echo '';
$width = 0; // the width of the picatcha form
// every theme is 358 pixels wide except for the clean theme, so we have to programmatically handle that
if ($this->options['registration_theme'] == 'clean')
$width = 485;
else
$width = 360;
echo <<
window.onload = function() {
document.getElementById('login').style.width = '{$width}px';
if(document.getElementById('reg_passmail')){document.getElementById('reg_passmail').style.marginTop = '10px';}
if(document.getElementById('picatcha_widget_div')){document.getElementById('picatcha_widget_div').style.marginBottom = '10px';}
};
REGISTRATION;
}
function picatcha_enabled() {
return ($this->options['show_in_comments'] || $this->options['show_in_registration']);
}
function keys_missing() {
return (empty($this->options['public_key']) || empty($this->options['private_key']));
}
function create_error_notice($message, $anchor = '') {
$options_url = admin_url('options-general.php?page=picatcha/picatcha.php') . $anchor;
$error_message = sprintf(__($message . ' Fix this', 'picatcha'), $options_url);
echo '
' . $error_message . '
';
}
function missing_keys_notice() {
if ($this->picatcha_enabled() && $this->keys_missing()) {
$this->create_error_notice('You enabled Picatcha, but some of the Picatcha API Keys seem to be missing.');
}
}
function validate_dropdown($array, $key, $value) {
// make sure that the capability that was supplied is a valid capability from the drop-down list
if (in_array($value, $array))
return $value;
else // if not, load the old value
return $this->options[$key];
}
function validate_options($input) {
// todo: make sure that 'incorrect_response_error' is not empty, prevent from being empty in the validation phase
// trim the spaces out of the key, as they are usually present when copied and pasted
// todo: keys seem to usually be 40 characters in length, verify and if confirmed, add to validation process
$validated['public_key'] = trim($input['public_key']);
$validated['private_key'] = trim($input['private_key']);
$validated['show_in_comments'] = ($input['show_in_comments'] == 1 ? 1 : 0);
$validated['bypass_for_registered_users'] = ($input['bypass_for_registered_users'] == 1 ? 1: 0);
$validated['show_in_lost_password'] = ($input['show_in_lost_password'] == 1 ? 1: 0);
$validated['show_in_login'] = ($input['show_in_login'] == 1 ? 1: 0);
$validated['timedelta_activation'] = $input['timedelta_activation'] == 1 ? 1: 0;
$validated['timedelta'] = $input['timedelta'] ? intval($input["timedelta"]) : 15; // use the intval filter
$timedelta_options = array('spam','0');
$validated['picatcha_timedelta_dropdown'] = $this->validate_dropdown($timedelta_options,'picatcha_timedelta_dropdown', $input['picatcha_timedelta_dropdown']);
$validated['send_repudata'] = ($input['send_repudata'] == 1 ? 1: 0);
$reputation_user_levels_options = array(0,1);
$validated['reputation_user_levels'] = $this->validate_dropdown($reputation_user_levels_options,'reputation_user_levels',$input['reputation_user_levels']);
//$validated['reputation_user_levels'] = ($input['reputation_user_levels'] == 1 ? 1: 0);
$capabilities = array ('read', 'edit_posts', 'publish_posts', 'moderate_comments', 'activate_plugins');
$themes = array ('red', 'white', 'blackglass', 'clean');
$picatcha_languages = array ('en', 'nl', 'fr', 'de', 'pt', 'ru', 'es', 'tr', 'hi', 'hu', 'is', 'zh', 'ar', 'tl', 'it', 'vi');
$validated['minimum_bypass_level'] = $this->validate_dropdown($capabilities, 'minimum_bypass_level', $input['minimum_bypass_level']);
$validated['comments_theme'] = $this->validate_dropdown($themes, 'comments_theme', $input['comments_theme']);
$validated['comments_tab_index'] = $input['comments_tab_index'] ? $input["comments_tab_index"] : 5; // use the intval filter
$validated['show_in_registration'] = ($input['show_in_registration'] == 1 ? 1 : 0); // activation of timedelta
$validated['registration_theme'] = $this->validate_dropdown($themes, 'registration_theme', $input['registration_theme']);
$validated['registration_tab_index'] = $input['registration_tab_index'] ? $input["registration_tab_index"] : 30; // use the intval filter
$validated['picatcha_language'] = $this->validate_dropdown($picatcha_languages, 'picatcha_language', $input['picatcha_language']);
$validated['language_override'] = ($input['language_override'] == 1 ? 1 : 0); //allow users to override the language on the Picatcha form
$validated['xhtml_compliance'] = ($input['xhtml_compliance'] == 1 ? 1 : 0);
$validated['display_powered_by'] = 1;
$picatcha_ssl_options = array(0,1,2); // options for SSL
$validated['picatcha_use_ssl'] = $this->validate_dropdown($picatcha_ssl_options, 'picatcha_use_ssl', $input['picatcha_use_ssl']);
$picatcha_captcha_format_options = array('1','2');
$picatcha_image_size_options = array(50,60,75);
$picatcha_image_noise_level_options = array(0,1,2,3,4,5,6,7,8,9,10);
$picatcha_image_noise_type_options = array(0,1,2);
$validated['picatcha_captcha_format'] = $this->validate_dropdown($picatcha_captcha_format_options, 'picatcha_captcha_format', $input['picatcha_captcha_format']);
$validated['picatcha_image_size'] = $this->validate_dropdown($picatcha_image_size_options, 'picatcha_image_size', $input['picatcha_image_size']);
$validated['picatcha_image_noise_level'] = $this->validate_dropdown($picatcha_image_noise_level_options, 'picatcha_image_noise_level', $input['picatcha_image_noise_level']);
$validated['picatcha_image_noise_type'] = $this->validate_dropdown($picatcha_image_noise_type_options, 'picatcha_image_noise_type', $input['picatcha_image_noise_type']);
$validated['picatcha_style_color'] = trim($input['picatcha_style_color']);
$validated['no_response_error'] = $input['no_response_error'];
$validated['incorrect_response_error'] = $input['incorrect_response_error'];
return $validated;
}
function dumpUserGlobals(){
//if a user opts into providing us with user data for better spam protection.
global $user_login;
global $user_email;
global $user_level;
global $current_user;
$data = $current_user->data;
//echo "current user";
//var_dump($current_user);
#$userData = "Username: ". $user_login. ", Email: ". $user_email.", User Level: ". $user_level . ", Registered since: ". $current_user->data->user_registered. ", fn: ".$current_user->data->first_name . ", ln: ". $current_user->data->last_name;
//$userData = " {\"un\": \"". $user_login. "\", \"em\": \"". $user_email."\", \"ul\": \"". $user_level . "\", \"rs\": \"". $data->user_registered. "\", \"fn\": \"".$data->first_name . "\", \"ln\": \"". $data->last_name ."\", \"ts\":\"".$hour.":".$minute.":".$second."\"}";
$userData = array(
"rd" => array(
"un" => $user_login,
"em" => $user_email,
"ul" => $user_level,
"rs" => $data->user_registered,
"fn" => $data->first_name,
"ln" => $data->last_name,
"ts" => getdate(),
"cd" => array() //form data data
)
);
//echo "";
return $userData;
}
// display picatcha
function show_picatcha_in_registration($errors) {
$format = <<
var PicatchaOptions = { theme : '{$this->options['registration_theme']}', lang : '{$this->options['picatcha_language']}', langOverride : '{$this->options['language_override']}', tabindex : {$this->options['registration_tab_index']} };
FORMAT;
/*if($this->options['display_powered_by']==0){
echo "";
$comment_string = <<
document.getElementById('picatcha').style.direction = 'ltr';
COMMENT_FORM;
// todo: is this check necessary? look at the latest picatchalib.php
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on")
$use_ssl = true;
else
$use_ssl = false;
//check if rerror is set
if(isset($_GET['rerror'])){
$rerror = $_GET['rerror'];
}else{
$rerror = "";
}
//displays an error message in the captcha for the login screen
if(isset($_REQUEST['picatcha'])){
switch ($_REQUEST['picatcha']){
case 'empty':
echo "".$this->options['no_response_error']."";
break;
case 'incorrect':
echo "".$this->options['incorrect_response_error']."";
break;
}
}
// if it's for wordpress mu, show the errors
if ($this->is_multi_blog()) {
#$error = $errors->get_error_message('picatcha');
#echo '';
#echo ($error ? '
'.$error.'
' : '');
echo $format . $this->get_picatcha_html($rerror, $use_ssl);
}
// for regular wordpress
else {
echo $format . $this->get_picatcha_html($rerror, $use_ssl);
}
}
function validate_picatcha_response($errors) {
// if the user does not fill out picatcha, throw the empty response error
if (empty($_POST['picatcha']['r']) || $_POST['picatcha']['r'] == '') {
if(is_wp_error($errors)){
$errors->add('blank_captcha', $this->options['no_response_error']);
}else{
$errors .= $this->options['no_response_error'];
}
return $errors;
}
$response = picatcha_check_answer($this->options['private_key'], $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT'], $_POST['picatcha']['token'], $_POST['picatcha']['r']);
// response is bad, add incorrect response error
if (!$response->is_valid)
if ($response->error == 'incorrect-answer'){
if (is_wp_error($errors))
$errors->add('captcha_wrong', $this->options['incorrect_response_error']);
if (!is_wp_error($errors)){
// works for form errors
$errors .= $this->options['incorrect_response_error'];
}
}
return $errors;
}
function check_picatcha_lost_password(){
if (empty($_POST['picatcha']['r']) || $_POST['picatcha']['r'] == ''){
wp_die(__($this->options['no_response_error']. " Please click your browser's back button to try again."));
}
//validate the captcha
$response = picatcha_check_answer($this->options['private_key'], $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT'], $_POST['picatcha']['token'], $_POST['picatcha']['r']);
if (!$response->is_valid){
if ($response->error == 'incorrect-answer')
wp_die(__($this->options['incorrect_response_error']. " Please click your browser's back button to try again."));
}
}
function validate_picatcha_response_redirect($url) {
//function to validate picatcha when the user is redirecting to a page.. ie if the username and password is correct
//check to see if the picatcha is filled out to begin with...
if (empty($_POST['picatcha']['r']) || $_POST['picatcha']['r'] == '') {
$_SESSION['rerror']= __('picatcha', 'The Picatcha is Empty!', 'picatcha');
// Log the user out immediately
wp_logout();
//return them to the login screen, passing along something to tell them the picatcha was wrong...
return $_SERVER['REQUEST_URI']."?picatcha=empty";
}
$response = picatcha_check_answer($this->options['private_key'], $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT'], $_POST['picatcha']['token'], $_POST['picatcha']['r']);
//if it is filled out, then validate it...
if (!$response->is_valid){
//echo "response is not valid";
if ($response->error == 'incorrect-answer')
if (!is_wp_error($errors)){
// works for form errors
//$url = '?picatcha='.$this->options['no_response_error'];
//return $url;
$PicatchaError = new WP_Error('Picatcha',__('Picatcha is wrong'));
return $_SERVER['REQUEST_URI']."?picatcha=incorrect";
}
//if the user did not pass picatcha, logs them out immediately
wp_logout();
}else{
//user completed the captcha correctly
//return them to the main site
return site_url()."/wp-admin/"; //maybe add in an option to the preferences to redirect it to another page? say the root page or something else?
}
}
function bph_picatcha(){
// generates the captcha on the buddypress signup page
// possibly a more elegant way to do this...
$picatchaOptions =<<
var PicatchaOptions = { theme : '{$this->options['registration_theme']}', lang : '{$this->options['picatcha_language']}', langOverride : '{$this->options['language_override']}', tabindex : {$this->options['registration_tab_index']} };