i'm working on a contact form.
i use this for form validation: http://plugins.jquery.com/project/form-validation-and-hints
and i use a captcha that stores the code in $_SESSION['captchacode'] variable.
what i'm trying to accomplish is to simply check using the jquery validator if user entered good captcha code.
that's how check function works:
function isTypeValid( type, value ) {
var cap = <?php echo $SESSION["captchacode"];?>;
if( type == classprefix + 'Text' ) {
return true;
}
if( type == classprefix + 'Captcha' ) {
if(value==cap){
return (true);
}else{
return false;
}
}
}
i searched a few threads here and elsewhere and that's where i got
var cap = <?php echo $SESSION["captchacode"];?>;
from.
i've also tried this:
var cap = "<%= Session['captchacode'] %>";
and it didn't help...
any ideas?
Don't use jquery for captcha validation.
Validate it on the server side.
also, the way you choose (with writing captcha value in the body of the script) is quite funny but it's merely a gift to possible spammer.
You can use remote method of jquery validation plugin to check captcha. You can see example here -
http://jquery.bassistance.de/validate/demo/captcha
Related
I would like to insert a contact form the new version (V3) of Recaptcha.
I have looked for different solutions, but they only show part of the code, they are incomplete or I get an error, and most of the solutions found are very complicated for something so simple and I do not understand the code.
I have searched this and other forums to implement the new version of ReCaptcha (V3) in my forms.
I needed to know how to:
Insert it with JS
How to validate it with PHP
What new fields were needed in my form.
I did not find any simple solution, which would show me all these points, or it was too complicated for somebody who just wanted to insert a contact form on their website.
At the end, taking some code portions of multiple solutions, I use a simple and reusable code, in which you only have to insert the corresponding keys.
Here it is.
The basic JS code
<script src="https://www.google.com/recaptcha/api.js?render=your reCAPTCHA site key here"></script>
<script>
grecaptcha.ready(function() {
// do request for recaptcha token
// response is promise with passed token
grecaptcha.execute('your reCAPTCHA site key here', {action:'validate_captcha'})
.then(function(token) {
// add token value to form
document.getElementById('g-recaptcha-response').value = token;
});
});
</script>
The basic HTML code
<form id="form_id" method="post" action="your_action.php">
<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">
<input type="hidden" name="action" value="validate_captcha">
.... your fields
</form>
The basic PHP code
if(isset($_POST['g-recaptcha-response'])){
$captcha=$_POST['g-recaptcha-response'];
}
else
$captcha = false;
if(!$captcha){
//Do something with error
}
else{
$secret = 'Your secret key here';
$response = json_decode(file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secret."&response=".$captcha."&remoteip=".$_SERVER['REMOTE_ADDR']));
if($response->{'success'}==false)
{
//Do something with error
}
}
//... The Captcha is valid you can continue with the rest of your code
//... Add code to filter access using $response . score
if ($response->{'success'}==true && $response->{'score'} <= 0.5) {
//Do something to denied access
}
You have to filter access using the value of $response->{'score'}. It can takes values from 0.0 to 1.0, where 1.0 means the best user interaction with your site and 0.0 the worst interaction (like a bot). You can see some examples of use in ReCaptcha documentation.
You only have to add your keys, no more changes needed:
src="https://www.google.com/recaptcha/api.js?render=your reCAPTCHA site key here"
grecaptcha.execute('your reCAPTCHA site key here'
and
$secret = 'Your secret key here';
Obviously you also have to change the action of the form, in this example:
action = "your_action.php"
In the answer above, these lines need to be updated in order to be able to read the response values in PHP:
$response = json_decode(file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secret."&response=".$captcha."&remoteip=".$_SERVER['REMOTE_ADDR']));
$response->{'success'}
$response->{'score'}
Looks like Google improved their docs since the first answers. Here is how I do it.
Client side integration in form:
The docs for this are here: https://developers.google.com/recaptcha/docs/v3
According to Google you should include the Recaptcha API on every page so that it can observe the user's behavior. So I added this line to the end of my footer which is included in every page (no parameters needed):
<script src="https://www.google.com/recaptcha/api.js"></script>
On the form you use a submit button like so:
<button class="g-recaptcha" data-sitekey="PASTE-YOUR-RECAPTCHA-SITE-KEY-HERE" data-callback="onSubmit" data-action="submit">Submit Form</button>
And add the following JavaScript function that submits the form:
function onSubmit() {
var form = document.forms[0]; // change this if you have multiple forms
if (/* possible client-side form validation code here */) {
form.submit();
}
}
Server side validating code:
The docs for this are here: https://developers.google.com/recaptcha/docs/verify
For this I created a helper function:
/**
* Checks if the current script has a valid Google Captcha response token.
* #returns True, if the script has a valid repsonse token, otherwise false.
*/
function isCaptchaValid()
{
$captcha = isset($_POST['g-recaptcha-response']) ? $_POST['g-recaptcha-response'] : false;
if (!$captcha) {
return false;
}
$postdata = http_build_query(
array(
"secret" => "PASTE-YOUR-RECAPTCHA-SECRET-KEY-HERE",
"response" => $captcha,
"remoteip" => $_SERVER["REMOTE_ADDR"]
)
);
$opts = array(
'http' =>
array(
"method" => "POST",
"header" => "Content-Type: application/x-www-form-urlencoded",
"content" => $postdata
)
);
$context = stream_context_create($opts);
$googleApiResponse = file_get_contents("https://www.google.com/recaptcha/api/siteverify", false, $context);
if ($googleApiResponse === false) {
return false;
}
$googleApiResponseObject = json_decode($googleApiResponse);
return $googleApiResponseObject->success;
}
No need to check any score value as done in the other answers. According to the docs there isn't even a score property in the response object. I checked it and there is one, but I don't use it.
You should call it at the beginning of the PHP script that handles your form submit like so:
if (!isCaptchaValid()) {
die("STOP! You are a bot."); // or do something else
}
So I'm working on my login page modification again... and I'm running into another issue. Here's the scenario... I've got a plugin that consists of two components: a script that rewrites the username field on the login/registration form, and a function that overrides the validation handler on registration.
Here are the files in question...
validator.php
<?php
// Rewrite registration form
function mpm_registration_form() {
wp_enqueue_script('login_form', plugin_dir_url(__FILE__).'js/usernamerewrite.js', array('jquery'), false, false);
}
add_action('login_head', 'mpm_registration_form');
// Register actions
add_action('register_post', 'mpm_validator_verify_account', 10, 3);
// Check username against minecraft.net database
function mpm_validator_verify_account($login, $email, $errors) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_URL, 'http://www.minecraft.net/haspaid.jsp?user='.rawurlencode($login));
$mcacct = curl_exec($curl);
curl_close($curl);
if($mcacct != 'true') {
if($mcacct == 'false') {
$errors->add('mc_error', __('<strong>Error:</strong> Minecraft account is invalid.'));
return $errors;
} else {
$errors->add('mc_error', __('<strong>Error:</strong> Unable to contact minecraft.net.'));
return $errors;
}
add_filter('registration_errors', 'mpm_validator_verify_account', 10, 3);
}
}
js/usernamerewrite.js
jQuery(document).ready(function ($) {
'use strict';
/*global document: false */
/*global $, jQuery */
var username, reset;
if ($('body').hasClass('login')) {
username = document.createElement("input");
username.type = 'text';
username.name = 'log';
username.id = 'user_login';
username.className = 'input';
username.size = '20';
username.tabIndex = '10';
reset = document.createElement("input");
reset.type = 'text';
reset.name = 'user_login';
reset.id = 'user_login';
reset.className = 'input';
reset.size = '20';
reset.tabIndex = '10';
$('label').each(
function () {
if ($(this).text().trim() === 'Username') {
$(this).html('Minecraft Username<br/>');
$(this).append(username);
} else if ($(this).text().trim() === 'Username or E-mail:') {
$(this).html('Minecraft Username or E-mail:<br/>');
$(this).append(reset);
}
}
);
}
});
The problem is that if I write the wp_enqueue_script line as it currently is (with the script being loaded in the header), the action function is handled properly but the rewrite never happens. Conversely, if I change it such that the script is loaded in the footer, the rewrite occurs, but the action is no longer handled properly (the username field is reset prior to being submitted). I'm at a complete loss.
Second (minor) annoyance: There is always a slight delay between the page loading and the occurrence of the rewrite. Any thoughts on how to make this more transparent would be greatly appreciated.
EDIT: If you're going to vote the question down, the least you could do is indicate why...
Finally figured it out! The way I was doing it was horrible... I really need to brush up on my JS/jQuery. Here's what I've ended up with which works beautifully in all my test cases, and also rewrites not just the field labels, but messages as well...
jQuery(document).ready(function ($) {
'use strict';
/*global document: false */
/*global $, jQuery */
$('label').each(
function () {
$(this).html($(this).html().replace('Username', 'Minecraft Username'));
}
);
$('.message').each(
function () {
$(this).html($(this).html().replace('username', 'Minecraft username'));
}
);
});
Could you please just wrap your function in:
jQuery( document ).ready
(
function( $ )
{
// your js here
}
);
No need to put the (jQuery) behind your function.
If you run DOM modifications before the page has loaded, it can't work. You can now load it in the header and rest assured it will fire.
I don't quite see why the fields would be reset prior to submitting, since you don't bind to any submit event handler... If this still happens, let us know.
Instead of calling if ($('body').hasClass('login')) in your js you probably want to check in php (use the login_head action) if you are on the login page and not send this js to all the pages on your site. But get it working first before optimizing.
If the submission doesn't arrive on the backend, that's probably because your javascript recreates inputs with the same ID's than the wordpress inputs and thus replaces them. As far as I can tell that is not at all what you are after. Just replace the existing labels.
Basically whats happening is I have a php form to send an email. There are several different validation steps all along the way, which all work great. What i'd like to do is have a javascript alert pop up if there is a validation error when the form is submitted. I have this working with the following php:
// Validate email
if(!filter_var($EmailFrom, FILTER_VALIDATE_EMAIL))
{
echo "<script language=javascript>alert('Please Use a Valid Email Address')</script>";
exit;
}
The alert pops up but the page redirects to domain.com/sendemail.php which leaves the user with a blank page. I'd really like to have the alerts pop up without reloading the page. How would I do this?
You can use ajax to accomplish this. But if you don't want to use ajax, instead doing an exit on error, you could redirect back to the form page along with a query string parameter.
header("Location: http://www.example.com/form.php?error=1");
And on your form page you could put the script withing php if. Something like
<?php if(isset($_GET['error']) && $_GET['error']==1): ?>
<script>
....
</script>
<?php endif; ?>
That would achieve what you are looking for. In fact you can perform multiple checks and set error based on your checks. But I would still suggest Ajax will give a better user experience.
Edit: Super easy solution, use jQuery form plugin : http://jquery.malsup.com/form/
I do something similar in some of my web apps, you might find it useful.
I do my validation server side and if I encounter an error I do this :
json_die(array(
'status' => 'error',
'message'=> 'Your error message'
));
and for success :
json_die(array(
'status' => 'success',
'message'=> 'Your success message'
));
The json_die is function is :
function json_die($array) {
header("content-type: application/json");
die(json_encode($array, true));
}
Then on the front end I do something like this:
$.post('/your_url', {
'your': vars
}, function (r) {
if(r.status == 'success') {
alert(r.message);
} else if (r.status == 'error') {
alert(r.message);
//handle error
} else {
alert('server exploded / no connection');
}
},'json');
This is a script I used in some forms I created to validate them fast. It's very simple and effective, I hope it helps you.
<script type="text/javascript">
function validate(){
emailfilter = /^([a-zA-Z0-9_\.\-])+\#(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
condition = 1;
mensaje = "Complete: ";
//Validate 1st input (Check if empty)
if (document.formname.forminput1.value.length==0){
condition = 0;
msg = msg + "-Input 1 is empty "
}
//Validate 1nd input (Check email)
if (!emailfilter.test(document.formname.forminput1.value)) {
condition = 0;
msg = msg + "-Input 1 has a invalid email adresss "
}
if (condition == 0){
alert(msg)
document.formname.forminput1.focus()
return 0;
}
//send
alert("Form sended.");
document.formname.submit();
}
</script>
This is partly a continuation from this thread: jquery - return value from callback function (in post request) into the function its inside of?
because I updated the code, yet trouble persists. I'm validating a simple html form with jquery, and despite all my other if/else statements working, the ajax call never gets made. Here's the javascript code:
var pass_form = $('#pass_form');
pass_form.submit( valid_pass_sett );
function valid_pass_sett() {
//remove old errors - snipped
pass_old = $('input[name=pass_old]').val();
pass_new = $('input[name=pass_new]').val();
pass_confirm_new = $('input[name=pass_confirm_new]').val();
if (pass_old === "") {
//display error on form - snipped
return false;
} else if (pass_new === "") {
//display error on form - snipped
return false;
} else if (pass_new != pass_confirm_new) {
//display error on form - snipped
return false;
} else if (pass_new.length < 8) {
//display error on form - snipped
return false;
} else {
$.post("http://www.example.com/ajax/validate.php",{ // async validation
type: 'valid_old_change_pass',
pass_old: pass_old,
pass_new: pass_new
}, valid_pass_combo_callback);
alert('after the ajax call...');
}
return false; // cancel form submission
}
and here's the relevant part of the validate.php:
$username = $_SESSION['username'];
$pass_old = $_POST['pass_old'];
$pass_new = $_POST['pass_new'];
if (empty($pass_old) || empty($pass_new)) {
echo "invalid";
} else if (!User::valid_user_pass($username, $pass_old)) {
echo "invalid_old";
} else if (!Sanitize::is_legal_password($pass_new)) {
echo "invalid_new";
} else {
echo "valid";
}
When I'm debugging with Firebug, and all other form inputs are correct, the script gets to the ajax call, then submits the form, even though it's supposed to call the callback function. This is the code for the callback function:
function valid_pass_combo_callback( data ) {
if (data == 'valid') {
//only if the form is valid!
pass_form[0].submit();
}
else if (data == "invalid_old") {
//display error on form - snipped
}
else if (data == "invalid_new") {
//display error on form - snipped
}
else {
//it always jumps to here..., even though data *is* the correct value
}
}
EDIT redux: Ok, I fixed the error in my callback function, as seen in the first answer, and now, a bit of a different problem has emerged. I debugged the function valid_pass_combo_callback and it's getting the correct value from validate.php; in this case, invalid_old is the value being returned. When I debug, data is equal to invalid_old. However, the comparison fails... so the code always jumps to the last else statement, no matter what. Nothing happens, because there isn't any behaviour there, so why is the comparison always failing?
EDIT, SOLVED: I decided to forgo binding this function to submit, and instead bound to an onclick event for a button on the form (which I'm using in place of a submit button) and that solved the problem. Validation is called when the button is clicked, and if client-side validation passes, then the form is submitted to the server for validation there.
One problem here is you're invoking your callback, not passing the function itself:
$.post("http://www.example.com/ajax/validate.php",{ // async validation
type: 'valid_old_change_pass',
pass_old: pass_old,
pass_new: pass_new
}, valid_pass_combo_callback); // Remove (data) so the callback
// isn't invoked immediately.
Looking at your code you dont have to submit the form but make an ajax call but you say the form is getting submitted. I think there is some js error in the below peice of code because of which the form is getting submitted.
$.post("http://www.example.com/ajax/validate.php",{ // async validation
type: 'valid_old_change_pass',
pass_old: pass_old,
pass_new: pass_new
}, valid_pass_combo_callback(data));//Here is the issue, it will call the method right away
alert('after the ajax call...');
Why dont you prevent the default behavior of form submit.
function valid_pass_sett(e) {
e.preventDefaul();//This will ensure the form will never be submitted.
And in the callback method make a small change to unbind the submit handler and then submit the form. This is because we already have one submit handler and we dont want to call it next time when we submit in the below method.
function valid_pass_combo_callback( data ) {
if (data === 'valid') {
//only if the form is valid!
pass_form[0].unbind('submit').submit();
}
else if (data === "invalid_old") {
//display error on form - snipped
}
else if (data === "invalid_new") {
//display error on form - snipped
}
}
i'm a little stuck with a jQuery. At the moment my function looks like this.
$(function(){
$(".url2").keyup(validNum).blur(validNum);
function validNum() {
var initVal = $(this).val();
outputVal = initVal.replace(/(https?:\/\/)?(www.)?[0-9A-Za-z-]{2,50}\.[a-zA-Z]{2,4}([\/\w\-\.,#?^=%&:/~\+#]*[\w\-\#?^=%&/~\+#]){0,250}(\s){1}$/
,"http://my.site/ref.php?id=<?php $code = unqiue-ref-id(); echo $unqiue-ref-id;?>");
if (initVal != outputVal) {
$(this).val(outputVal);
return false;
}}
});
So right now it rewrites a user typed url (in a textarea) to a redirection link with my own url (e.g. my.site?ref.php?id=unique12. What I need exactly is a POST Request to a php file (code below) where the valid user-url is given to the php file as a var and then the php file should give back a var with the generated unique unique-ref-id. I do of course know that the code above isn't working like that, it only shows how the final result should look like. The php file wich generates the unique-ref-id looks like this.
function unqiue-ref-id() {
$unqiue-ref-id = "";
$lenght=4;
$string="abcdefghijklmnopqrstuvwxyz123456789";
mt_srand((double)microtime()*1000000);
for ($i=1; $i <= $lenght; $i++) {
$shorturl .= substr($string, mt_rand(0,strlen($string)-1), 1);
}
return $unqiue-ref-id;
}
$user-url = // var send from jquery
do{
$unqiue-ref-id = unqiue-ref-id();
} while(in_array($unqiue-ref-id, $result));
// var send back to jquery function => final results of do function above
// Here i add the $user-url and $unique-ref-id to datebase (Dont need code for that)
?>
Would be so great if someone can help me out with that. Thanks a lot :)
Use the POST jQuery's method. Here's an example
$.post('URL-TO-POST-DATA', {'parameter1': 'value', 'parameter2': 'value'}, function(data_received){
/** Here goes your callback function **/ alert(data_received);
});
More information about POST method
Don't forget one thing. jQuery will not receive nothing if you don't use echo on PHP (instead of return). You MUST use echo in PHP, don't forget it.