Having issues submitting form via AJAX on WordPress with reCAPTCHA - ajax

I've spent a good few hours trying to Google my way out of this, and I keep coming up with one issue or another. I'm getting myself wrapped up in knots and I am hoping that someone can help me by giving me a metaphorical slap around the face and showing me how the hell to do this properly.
Ok ... so I have a page on my front end that is inserted by a shortcode. This form is essentially a sort of AMA (Ask Me Anything) form, which I am using to create posts in my Admin area. Visitors enter their question, name and email, and submit the form to me via AJAX. The submissions are saved into the WordPress database under an ama custom post type, and an email is sent to me via wp_mail because I have an SMTP plugin installed that routes all requests for wp_mail through there. I have CMB2 installed on my website, and have keys for reCAPTCHA saved there.
So far, so good.
I had a working form with CMB2 fields, but CMB2 doesn't seem to support reCAPTCHA (otherwise it worked fine). So I decided to start from scratch and write my own form since it was just three fields that I wanted to submit. What could possibly go wrong, right?
Here's my Franken-code.
<?php
function ccdClient_shortcode_amaForm( $atts ) {
// Parse attributes
$atts = ( shortcode_atts( array(
'id' => uniqid('ccdClient_amaForm_'),
), $atts, 'ama_form' ) );
$rcKey = cmb2_get_option( 'ccdtheme_settings_apikeys', '_ccdclient_themesettings_apikeys_captcha_sitekey' );
$form = '
<form id="' . $atts['id'] . '" name="' . $atts['id'] . '" method="post" action="">
<div class="amaError"></div>
<div class="amaForm-field-question amaForm-field">
<p><label for="question">Your Question</label></p>
<p><textarea id="question" name="question" tabindex="1"></textarea></p>
</div>
<div class="amaForm-fieldGroup amaForm-groupTwo">
<div class="amaForm-field-name amaForm-field">
<p><label for="name">Your Name</label></p>
<p><input type="text" id="name" name="name" tabindex="2" /></p>
</div>
<div class="amaForm-field-email amaForm-field">
<p><label for="email">Your Email</label></p>
<p><input type="email" id="email" name="email" tabindex="3" /></p>
</div>
</div>
<div class="amaForm-fieldGroup amaForm-groupTwo">
<div class="amaForm-field-recaptcha amaForm-field amaForm-recaptcha">
<div class="g-recaptcha" data-sitekey="' . $rcKey . '"></div>
</div>
<div class="amaForm-field-submit amaForm-field amaForm-submit">
<input type="submit" value="Publish" id="submit" name="submit" />
</div>
</div>
</form>
<script>
$(document).ready(function() {
// Get form
var amaForm = $("#' . $atts['id'] . '");
// Get messages div
var amaError = $("#' . $atts['id'] . ' .amaError");
// Set data
var amaData = { "action" : "ama_form_process"}
var options = {
url: "'. admin_url( 'admin-ajax.php' ) .'",
type: "post",
dataType: "json",
data: amaData,
success : function(responseText, statusText, xhr, $form) {
$(amaError).html("Your form has been submitted successfully");
},
};
//Set submit function
amaForm.on("submit", function(e) {
//Prevent default form behavior
e.preventDefault();
// Serialise form data
//Post via AJAX
$.ajax(options)
.done(function(response) {
// Make sure that the amaError div has the "success" class.
$(amaError).removeClass("error");
$(amaError).addClass("success");
// Set the message text.
$(amaError).text(response);
})
.fail(function(data) {
// Make sure that the amaError div has the "error" class.
$(amaError).removeClass("success");
$(amaError).addClass("error");
// Set the message text.
if (data.responseText !== "") {
$(amaError).text(data.responseText);
} else {
$(amaError).text("Oops! An error occured, and your message could not be sent.");
}
});
});
});
</script>';
return $form;
}
add_shortcode('ama_form', 'ccdClient_shortcode_amaForm');
function ama_form_process() {
// If the form was submitted
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// Include WordPress files
include_once '../../../../../wp-includes/wp-load.php';
// Set reCaptcha private key
$recaptchaKey = cmb2_get_option( 'ccdtheme_settings_apikeys', '_ccdclient_themesettings_apikeys_captcha_secretkey' );
// If the Google Recaptcha box was clicked
if(isset($_POST['g-recaptcha-response']) && !empty($_POST['g-recaptcha-response'])){
$captcha = $_POST['g-recaptcha-response'];
$response = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=" . $recaptchaKey . "&response=" . $captcha);
$obj = json_decode($response);
// If the Google Recaptcha check was successful
if($obj->success == true) {
$question = strip_tags( trim( $_POST['question'] ) );
$name = strip_tags( trim( $_POST["name"] ) );
$name = str_replace( array("\r","\n"), array(" "," "), $name);
$email = filter_var( trim( $_POST["email"] ), FILTER_SANITIZE_EMAIL );
if ( !$name || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "Oops! There was a problem with your submission. Please complete the form and try again.";
exit;
}
// Add the content of the form to $post as an array
$post = array(
'post_title' => $question,
'post_status' => 'pending', // Could be: publish
'post_type' => 'ama', // Could be: `page` or your CPT
'meta_input' => array(
'_ccdclient_ama_name' => $name,
'_ccdclient_ama_email' => $email,
),
);
wp_insert_post($post);
echo 'Saved your post successfully! :)';
$recipient = get_option('admin_email');
$subject = "New question from $name";
$email_content = "Name: $name\n";
$email_content .= "Email: $email\n\n";
$email_content .= "Message:\n$question\n";
$email_headers = "From: $name <$email>";
wp_mail( $recipient, $subject, $email_content, $email_headers );
}
// If the Google Recaptcha check was not successful
else {
echo "Robot verification failed. Please try again. Success:" . $response;
}
}
// If the Google Recaptcha box was not clicked
else {
echo "Please click the reCAPTCHA box.";
}
}
// If the form was not submitted
// Not a POST request, set a 403 (forbidden) response code.
else {
echo "There was a problem with your submission, please try again.";
}
}
add_action("wp_ajax_ama_form_process", "ama_form_process");
//use this version for if you want the callback to work for users who are not logged in
add_action("wp_ajax_nopriv_ama_form_process", "ama_form_process");
As you can guess, I have looked at about a dozen or so pages and combined the efforts of each of them, thus overwriting parts that would perhaps have worked and confused myself no end, and so am resorting to here to preserve what is left of my sanity.
EDIT: Apologies. Whilst I copied the code, I wasn't specific on what issues I was facing. Goes to show the extent I was frustrated.
I have had a number of issues with this code. Currently, when I submit the form, it keeps the same URL but returns a 404 error page. However, it has previously told me that it cannot recognise functions - and therefore couldn't run - such as cmb2_get_option (which is a modification of get_option that specifically works with CMB2 options pages) and wp_insert_post. The latter error (ie: wp_insert_post) came up when the secret key was hard-coded into the script and not called from the get_option function.

Related

controller method is not working and page is refreshing instead of redirecting to given path in laravel

I am not getting any error, it's just refreshing on that page. The controller method is not called where I have given the path for redirection.
My code is:
blade-file
<form action="{{route('validate-promocode')}}" method="post">
#csrf
<input class="promo-input" id="code" type="text" onkeyup="this.value = this.value.toUpperCase();" name="code" placeholder="EG. APPLY HERE"><br>
<input type="hidden" name="_token" value="{!!csrf_token()!!}">
<button id="save" type="submit" class="apply-promo">Apply Promo Code</button>
</form>
Route file
Route::post('/validade-code', 'PromoController#validateCode')->name('validate-promocode');
Controller file
public function validateCode(Request $request){
if (Gate::allows('isUser')) {
$ip = $request->input('code');
dd($promo);
$promo = Promo::where('code','=', trim($ip))->first();
if(!empty($promo)){
$dataamountMonthly = 40000 - (($promo->discount_percentage * 40000) / 100);
$dataamountYearly = 300000 - (($promo->discount_percentage * 300000) / 100);
if($dataamountMonthly == 0 || $dataamountYearly == 0){
return view('paymet.subscription-success');
}
$data = array(
'dataamountMonthly' => $dataamountMonthly ,
'dataamountYearly' => $dataamountYearly,
'code' => $ip
);
return view('payment.startPromoCode',$data);
}else{
$data = array(
'error' => 'Something went wrong, please try again'
);
return redirect()->back()->with($data);
}
}
}
I don't know what I am missing , any help will be appriciated.
Solved
Sometimes you may wish to redirect the user to their previous location, such as when a submitted form is invalid. You may do so by using the global back helper function. Since this feature utilizes the session, make sure the route calling the back function is using the web middleware group or has all of the session middleware applied
I think according to your code
$promo = Promo::where('code','=', trim($ip))->first();
the value of $promo can be null/ empty
and instead of executing your expected code it is redirecting back the page, and it seems the refresh for you. rest your code is correct.

Laravel - How to send data from a checkbox button to a controller using Ajax

I am trying to have a toggle button (checkbox) changing some data in my MySQL database. I essentially want to to have a nice admin site in which I can unlock users to certain features on the site with a nice on/off switch.
Everything seems to be set up correctly, but my controller doesn't react or get triggered at all.
Right now, I have a working toggle button in my view triggering a javascript function. In that javascript function, I have already set up data that I want to transmit over to my controller.
I have also set up Ajax in my Javascript. Apparently Ajax has this part in its code (see my code if you need to know what I mean) where it says "success" or "error". Before entering the {{csrf_token()}} token, I always ended up in the error section. However, now I always enter the "success" section when toggling my button.
Now to my problem:
Although Ajax confirms a success, literally nothing happens within my controller. I have no idea how to change this as there is no error code whatsoever, either.
This is my button
<input id = "company_{{$company->id}}" data-id="{{$company->id}}" onchange=myFunction({{$company->id}}) value = {{$company->permissionlevel}} class="toggle-class" type="checkbox" data-onstyle="success" data-offstyle="danger" data-toggle="toggle" data-on="Active" data-off="InActive" {{ $company->permissionlevel ? 'checked' : '' }}>
This is my javascript (the relevant part)
var test = 12; // A random variable for this example
$.ajax({
method: 'POST', // Type of response and matches what we said in the route
url: 'home/update', // This is the url we gave in the route
data: {'test' : test, _token: '{{csrf_token()}}'}, // a JSON object to send back
success: function(response){ // What to do if we succeed
console.log(response);
//alert("Ajax success");
},
error: function(jqXHR, textStatus, errorThrown) { // What to do if we fail
console.log(JSON.stringify(jqXHR));
console.log("AJAX error: " + textStatus + ' : ' + errorThrown);
//alert("Ajax error");
}
});
This is my controller function (just to see what happens)
public function update (Request $request)
{
//echo "This is a test";
$data = $request->all(); // This will get all the request data.
dd($data); // This will dump and die
return $data;
}
Route
Route::post('/home/update', 'HomeController#update');
Expected result:
Controller throws something at me. Currently all I want is to see if my controller reacts to the button press or if I can request the data Ajax is supposed to send over.
Actual result:
Nothing happens on my controller's side
I have found a solution by myself. I essentially added this code to my controller:
public function update (Request $request, $id, User $user)
{
$newPermLevel = $request->input('value');
$override = (int) $newPermLevel;
\DB::table('users')->where('id',$id)->update(array(
'permissionlevel'=>$override,
));
}
However, I need to add these small lines at the beginning of my controller right behind the namespace:
use Illuminate\Http\Request;
use App\User;
Just for completion, I will leave the other necessary code here as well for anybody in the need to update his database with a toggle / switch button
Here's my route
Route::post('/home/update/{id}', 'HomeController#update');
Here's my HTML
based on the current state of a button, I make an if statement (using blade engine) either creating a turned off or turned on switch. The button has a value, which essentially is the current database entry (in my case it is either 1 or 0 that I want to jump up and forth with)
#if ($company->permissionlevel == 0)
<div class="col-sm-5">
<label class="switch">
<input id = "company_{{$company->id}}" data-id="{{$company->id}}" onchange=myFunction({{$company->id}}) value = {{$company->permissionlevel}} class="toggle-class" type="checkbox" data-onstyle="success" data-offstyle="danger" data-toggle="toggle" data-on="Active" data-off="InActive" {{ $company->permissionlevel ? 'checked' : '' }}>
<span class="slider round"></span>
</label>
</div>
#endif #if ($company->permissionlevel == 1)
<div class="col-sm-5">
<label class="switch">
<input id = "company_{{$company->id}}" data-id="{{$company->id}}" onchange=myFunction({{$company->id}}) value = {{$company->permissionlevel}} class="toggle-class" type="checkbox" data-onstyle="success" data-offstyle="danger" data-toggle="toggle" data-on="Active" data-off="InActive" {{ $company->permissionlevel ? 'checked' : '' }}>
<span class="slider round"></span>
</label>
</div>
#endif
At last, my Javascript making use of Ajax. The code essentially check if the value of out switch is currently on 0 (then switch it to 1) or on 1 (then switch it to 0). Additionally, I define a variable to grab the current user's (company's ID), so that the controller later on knows to which user I refer.
<script>
function myFunction(id) {
var value = document.getElementById("company_" + id).value;
if (value == 1){
document.getElementById("company_" + id).value = 0;
//alert(document.getElementById("company_" + id).value);
}
if (value == 0){
document.getElementById("company_" + id).value = 1;
//alert(document.getElementById("company_" + id).value);
}
var company_id = id;
var newPermLevel = document.getElementById("company_" + id).value;
$.ajax({
method: 'POST', // Type of response and matches what we said in the route
url: 'home/update/'+company_id, // This is the url we gave in the route
data: {
'company_id' : company_id,
'value' : newPermLevel,
_token: '{{csrf_token()}}',
},
// a JSON object to send back
success: function(response){ // What to do if we succeed
console.log(response);
//alert("Ajax success");
},
error: function(jqXHR, textStatus, errorThrown) { // What to do if we fail
console.log(JSON.stringify(jqXHR));
console.log("AJAX error: " + textStatus + ' : ' + errorThrown);
//alert("Ajax error");
}
});
}
</script>
Consider this question closed, but thanks for reading along. I hope anybody googling desperately for a similar solution can make use of my code. Not all heroes wear capes :)

Inline javascript is getting stripped off from the AJAX response by Prototype JS Ajax.Request method

I am working on a Magento store trying to code a widget's javascript layer with the help of Prototype js framework.
In my grid.js file AJAX call is setup like that:
loadTabContent: function(tab, tabType){
if(tab === undefined || tabType === undefined){
return this;
}
entityId = tab.id.split('-')[3];
request = new Ajax.Request(
this.tabContentLoadUrl,
{
method:'get',
onSuccess: this.onTabContentLoad.bind(tab),
onFailure: this.ajaxFailure.bind(this),
evalJS: true,
parameters: {
id: entityId,
type: tabType
}
}
);
}
Below is the success handler:
onTabContentLoad: function(transport){
if(transport && typeof transport.responseText !== undefined){
try{
response = transport.responseText;
}catch (e) {
console.log('PARSE ERROR', e);
response = {};
}
entityId = this.id.split('-')[3];
tabType = this.id.split('-')[1];
if(response && $('tab-' + tabType + '-' + entityId + '-contents')){
$('tab-' + tabType + '-' + entityId + '-contents').update(response);
}
}
},
The content for the div is getting updated correctly by the AJAX call but there is some inline JS in response which is not working.
I can't even see that javascript snippet in Elements tab(chrome developer tool)
Below is the code that handles the AJAX request on server side:
public function renderTabContentAction()
{
$entityId = Mage::app()->getRequest()->getParam('id');
if( ! $entityId){
$this->getResponse()->setHeader('HTTP/1.0', '400', true);
$this->getResponse()->setBody('Invalid parameters provided.');
}
$tabType = Mage::app()->getRequest()->getParam('type');
if( ! $tabType){
$this->getResponse()->setHeader('HTTP/1.0', '400', true);
$this->getResponse()->setBody('Invalid parameters provided.');
}
Mage::register('current_entity_id', $entityId);
Mage::register('current_tab_type', $tabType);
$tabHtml = $this->_getTabsHtml($entityId, $tabType);
$this->getResponse()->setHeader('HTTP/1.0', '200', true);
$this->getResponse()->setBody($tabHtml);
}
Below is the response that gets passed to onTabContentLoad AJAX handler:
<div class="vertical-tabs">
<div class="tabs">
<div class="tab" id="tab-vertical-137-2441">
<input type="radio" id="label-vertical-product-tab-137-2441" name="product-tab-group-137">
<label class="tabs-label" for="label-vertical-product-tab-137-2441">PG-10ml</label>
<div class="content" id="tab-vertical-137-2441-contents">
</div>
</div>
<div class="tab" id="tab-vertical-137-2442">
<input type="radio" id="label-vertical-product-tab-137-2442" name="product-tab-group-137">
<label class="tabs-label" for="label-vertical-product-tab-137-2442">PG-15ml</label>
<div class="content" id="tab-vertical-137-2442-contents">
</div>
</div>
</div>
</div>
<script type="text/javascript">
bulkOrderGrid.initVerticalTabs();
bulkOrderGrid.activateTab('2441', 'VERTICAL');
</script>
You can see that the SCRIPT tags are there in the response. Its just when the content gets updated using Element.update function it strips off the SCRIPT tags. That's what I can understand so far.
NOTE:
I have also used Ajax.Updater along with evalScripts:true and Ajax.Request along with evalJS:true.
Got stuck here. Any help would be much appreciated.
UPDATES:
Since I am using Element.update function to refresh the section. The source of the problem is this part of the code in prototype.js around line no. 2048. I can see its getting executed in js debugger. It does evaluates the js code but also removes the SCRIPT tags from the source. Commenting out stripScripts works fine.
else {
element.innerHTML = content.stripScripts();
}
content.evalScripts.bind(content).defer();
I think your problem is that
the Ajax response is passed through String#evalScripts() but the context is not the global context so instead of your script block do this
<script type="text/javascript">
window.bulkOrderGrid.initVerticalTabs();
window.bulkOrderGrid.activateTab('2441', 'VERTICAL');
</script>
if that doesnt fix it - than you can directly run transport.responseText.evalScripts() but you still need the window. in your script block to resolve the scope of the variables.
You are correct Element.update() remove script tag. you should use
Element.innerHTML

add submit to delete comment using ajax , php

Hi, I use a php code to view the all comments to any post from my sql and I want to add a submit button to every comment in order to delete it without refreshing the page, I mean using AJAX i don't know how to write that codes and connect it with html codes i want add submit like this :
<form>
<input type="submit" id="deletecomment">
</form>
and connected it with AJAX and delete.php page to delete the comment (ajax,delete.php)???????
this is my codes
$result = mysql_query ("select * from post_comments WHERE link ='$getlink' order by link asc");
while ($row = mysql_fetch_array($result)) {
$id = $row['id'];
$link = $row['link'];
$time = $row['time'];
$content = nl2br($row['content']);
$name = ($row['link'] != '') ? '<h3 style="color:blue">'.$row['name'].'</h3>' : $row['name'];
$imge = $row['imge'];
echo '
<div class="commentuserbackground">
<img src="'.$imge.'" width="30px" height="30px">
<div id="comment-'.$id.'" class="username1">'.$name.'</div>
<div class="username2">'.$content.'</div><div class="commenttime"><h4 style="color:#5a5a5a">'.$time.'</h4>
</div></div>';
}
If you already have the jquery lib included in your html, you could do something like this:
# html page
<button data-id="1" class="delete_comment" /> # no need to wrap in form
# At the bottom of the body tag
$(".delete_comment").click(function(e){
e.preventDefault();
var $button = $(this), $comment = $button.parent(), id = $button.data("id");
$.ajax({
type: 'DELETE',
url: "/path/to/comment/" + id,
success: function(){ $comment.remove(); }
});
});

Ajax request in YouTube API browser-based upload

I'd like to upload a YouTube video using ajax requests. The following script is used to create a form in order to upload a video to YouTube:
function createUploadForm($videoTitle, $videoDescription, $videoCategory, $videoTags, $nextUrl = null)
{
$httpClient = getAuthSubHttpClient();
$youTubeService = new Zend_Gdata_YouTube($httpClient);
$newVideoEntry = new Zend_Gdata_YouTube_VideoEntry();
$newVideoEntry->setVideoTitle($videoTitle);
$newVideoEntry->setVideoDescription($videoDescription);
//make sure first character in category is capitalized
$videoCategory = strtoupper(substr($videoCategory, 0, 1))
. substr($videoCategory, 1);
$newVideoEntry->setVideoCategory($videoCategory);
// convert videoTags from whitespace separated into comma separated
$videoTagsArray = explode(' ', trim($videoTags));
$newVideoEntry->setVideoTags(implode(', ', $videoTagsArray));
$tokenHandlerUrl = 'http://gdata.youtube.com/action/GetUploadToken';
try {
$tokenArray = $youTubeService->getFormUploadToken($newVideoEntry, $tokenHandlerUrl);
if (loggingEnabled()) {
logMessage($httpClient->getLastRequest(), 'request');
logMessage($httpClient->getLastResponse()->getBody(), 'response');
}
} catch (Zend_Gdata_App_HttpException $httpException) {
print 'ERROR ' . $httpException->getMessage()
. ' HTTP details<br /><textarea cols="100" rows="20">'
. $httpException->getRawResponseBody()
. '</textarea><br />'
. '<a href="session_details.php">'
. 'click here to view details of last request</a><br />';
return;
} catch (Zend_Gdata_App_Exception $e) {
print 'ERROR - Could not retrieve token for syndicated upload. '
. $e->getMessage()
. '<br /><a href="session_details.php">'
. 'click here to view details of last request</a><br />';
return;
}
$tokenValue = $tokenArray['token'];
$postUrl = $tokenArray['url'];
// place to redirect user after upload
if (!$nextUrl) {
$nextUrl = $_SESSION['homeUrl'];
}
print <<< END
<br /><form id="ajaxform" action="${postUrl}?nexturl=${nextUrl}"
method="post" enctype="multipart/form-data">
<input name="file" type="file"/>
<input name="token" type="hidden" value="${tokenValue}"/>
<input value="Upload Video File" type="submit" />
</form>
END;
}
At the end, a form is created using action="${postUrl}?nexturl=${nextUrl} which tells to YouTube to redirect the browser after the uploading has been completed. When the user is redirected, to nextUrl, two variables are passed with the URL (the status of the uploaded video and an ID for that video). I don't want a page reload but it seems that nextUrl is mandatory. Furthermore, I want to be able to get that ID for further use.
Is it possible to change the behavior of that automatic redirection and to pass those values in any other way?
Thanks

Resources