$_FILES not working with ajax submit in woocommerce checkout - ajax

I am try to upload file on woocommerce checkout page but I am not able to upload file. I have also try to get value using print_r($_FILES) in create_order function in class-wc-checkout.php file but it will return blank array
When I have remove class checkout from checkout form then it will working fine (without ajax submit)
I want to submit form without ajax but I need ajax remain on checkout page because I have add some extra price using ajax (update price without refresh)
My ajax call to add extra fee.
jQuery('#PageCount').change(function () {
var state = jQuery('#PageCount').val();
var current_price = jQuery('#carttot').val();
var data = {
action: 'woocommerce_apply_state',
security: wc_checkout_params.apply_state_nonce,
state: state,
current_price: current_price
};
jQuery.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: data,
success: function (code) {
console.log(code);
if (code === '0') {
jQuery('body').trigger('update_checkout');
}
},
dataType: 'html'
});
return false;
});
and php code or woocommerce hooks to add extra fee
add_action('wp_ajax_woocommerce_apply_state', 'calculate_per_page', 10);
add_action('wp_ajax_nopriv_woocommerce_apply_state', 'calculate_per_page', 10);
function calculate_per_page() {
if (isset($_POST['state'])) {
global $woocommerce;
$weight = WC()->cart->cart_contents_weight;
$state = $_POST['state'];
$current_price = $_POST['current_price'];
$val = $current_price * $state -$current_price;
session_start();
$_SESSION['val'] = $val;
}
}
add_action('woocommerce_cart_calculate_fees', 'woo_add_cart_fee');
function woo_add_cart_fee() {
session_start();
$extracost = $_SESSION['val'];
WC()->cart->add_fee('Per page charges:', $extracost);
}
And my function to upload file..
add_action('woocommerce_checkout_update_order_meta', 'custom_checkout_field_update_order_meta');
function custom_checkout_field_update_order_meta( $order_id ) {
if ($_FILES['avatar']['name']) {
if ( ! function_exists( 'wp_handle_upload' ) ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' );
}
$upload_dir = wp_upload_dir();
$path = $upload_dir['path'];
$fileName = $_FILES["avatar"]["name"];
$fileTmpLoc = $_FILES["avatar"]["tmp_name"];
$pathAndName = $path."/".$fileName;
$moveResult = move_uploaded_file($fileTmpLoc, $pathAndName);
update_post_meta( $order_id, 'uploaded_file_to_translate', $_FILES['avatar']['name']);
if ($_POST['billing_output_to_send'])
update_post_meta( $order_id, 'How do you want your translation sent to you?', esc_attr(htmlspecialchars($_POST['billing_output_to_send'])));
if ($_POST['PageCount'])
update_post_meta( $order_id, 'How many pages are in your documents?', esc_attr(htmlspecialchars($_POST['PageCount'])));
}
}
How it will possible?

$_FILES you can not get it at woocommerce_checkout_update_order_meta hook because of woocommerce serialize the form and submit form throw ajax,
as we know that form serialize can not submit files requests. but I have two solutions for doing this may be it work for you.
You can use a plugin woocommerce upload file in the checkout. you can easily find it on google
But if you want to upload custom files then you can use this
when onchange file fire an ajax and push file array in session when user checkout, get the files from the session and upload to the directory, and then destroy your session.
$('.fileupload').change(function(e){
var action = 'update_session_files';
e.preventDefault();
var fd = new FormData();
fd.append('action', action);
var files = $('input[name="file"]')[0].files[0];
fd.append('billing_artist_headshot', files);
$.ajax({
url: my_ajax_object.ajax_url,
type: 'post',
data: fd,
contentType: false,
processData: false,
success: function(response){
},
});
});
and ajax action
add_action( 'wp_ajax_nopriv_update_session_files', 'update_session_function' );
add_action( 'wp_ajax_update_session_files', 'update_session_function' );
function update_session_function(){
session_start();
$_FILES['billing_artist_headshot'];
// upload your file here with Wordpress and get the id.
}

Related

Laravel Redirect doesn't function similar when called from two different methods

I'm trying to click on a button and load the previous month data onto the screen. Initially it passes the current month and year and the page load works fine. Since the nav button click could be done in any month there it calculates the new month and passes to the same method. But the page load after each button click only returns the response, doesn't display data on screen. Can you please help me to find what causes this issue and sort this out. Here are my code snippets.
jquery
$(".month-nav-left").click(function(e){
e.preventDefault();
// currently hard coded - just to check
var month = 8;
var year = 2024;
var monthDir = "mBack";
$.ajax({
type:'POST',
url:"{{ route('monthBack') }}",
data:{month:month, year:year, monthDir:monthDir},
success:function(data){
// alert(data.success);
// console.log(data);
}
});
routes
use App\Http\Controllers\CalendarSetupController;
Route::get('/', [CalendarSetupController::class, 'index']);
Route::get('/{year}/{month}', [CalendarSetupController::class, 'monthToDisplay'])->name('selectCalendar');
Route::post('/mback', [CalendarSetupController::class, 'selectMonth'])->name('monthBack');
controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;
class CalendarSetupController extends Controller
{
public function index()
{
$month = (int)date('m');
$year = (int)date('Y');
// This displays data correctly on initial load.
return \Redirect::route('selectCalendar', [$year, $month]);
}
public function monthToDisplay()
{
$year = request()->segment(1);
$month = request()->segment(2);
$list=array();
$month=(date("F", mktime(12, 0, 0, $month, 1, $year)));
return View::make('mainPage', ['date' => $list, 'month' => $month, 'year' => $year]);
}
function selectMonth(Request $request)
{
$input = $request->all();
$month = $input["month"];
$year = $input["year"];
switch($input["monthDir"])
{
case "mBack":
$month = (int)strftime('%m', strtotime('+1 month', mktime(0, 0, 0, $month, 1, $year)));
break;
}
// This only have the correct view in the response, but does not load on the screen.
return \Redirect::route('selectCalendar', [$year, $month]);
}
}
Thank you.
monthBack is a web route and all requests that target it will pass throught the VerifyCsrfToken middleware which checks for a valid token.
Since your ajax request includes no valid _token key it will be rejected (unauthorized, You can see the HTTP answer in your live artisan serve log).
If you didn't edit your HTML layout the header will already include a valid token.
<meta name="csrf-token" content="{{ csrf_token() }}" />
Here is how to add that token to your POST request:
$.ajax({
type: "POST",
url: "{{ route('monthBack') }}",
data: {
month:month, year:year, monthDir:monthDir,
_token:$('meta[name="csrf-token"]').attr('content')
},
success: success,
dataType: dataType
});
Or add
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
Note that the token stays valid for a limited period of time, if you do not refresh your page for a while the button will not work.
I added a line to redirect to the desired url in the javascript code. I am not sure if it's a best practice or not, but it did as expected. Thank you for your time.
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$(".month-nav-left").click(function(e){
e.preventDefault();
// currently hard coded - just to check
var month = 8;
var year = 2024;
var monthDir = "mBack";
$.ajax({
type:'POST',
url:"{{ route('monthBack') }}",
data:{month:month, year:year, monthDir:monthDir},
success:function(data){
// alert(data.success);
// console.log(data);
}
});
window.location.replace("http://127.0.0.1:8000/"+year+"/"+month);
});
What happens if you try the helper function redirect()->route('route.name', 'data' => $data); instead of using the \Redirect::route() static method?

Testing an AJAX call in phpunit / symfony, using client or crawler

I want to test a conroller which generates a page with a field that changes dynamically with ajax.
Here is the code of ajax:
<script>
var $groupeCompetence = $('#requete_prestataire_groupeCompetence');
// When sport gets selected ...
$groupeCompetence.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected sport value.
var data = {};
data[$groupeCompetence.attr('name')] = $groupeCompetence.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
// Replace current position field ...
$('#requete_prestataire_competence').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#requete_prestataire_competence')
);
// Position field now displays the appropriate positions.
}
});
});
</script>
How can I call this code from phpunit using client or crawler?
I tried :
$this->client->request(
'POST',
'/',
array('requete_prestataire[groupeCompetence]' =>2),
array(),
array(),
array('HTTP_X-Requested-With' => 'XMLHttpRequest',
));
But it doesn't work.
Thanks a lot !
I found the solution :
you have to disable validation, and send the form as it was generated :
$crawler = $this->client->request('GET', '/');
$form = $crawler->selectButton('requete_prestataire_Rechercher')->form();
$form['requete_prestataire[groupeCompetence]'] = 2;
$form['requete_prestataire[competence]']->disableValidation()->select(50);
$crawler = $this->client->submit($form);
here is the explanation in doc :
doc symfony dom crawler component

Extjs 4 downloading a file through ajax call

The problem is very simple: i have to download a file when i submit a form, it's an ajax call when the form is submitted which lets me build a file with the data taken from the form, server side, and then send it as a link to an alert. The fact is that my boss want the file to be downloaded directly and not through a link in an alert. So i had to make sure that the file is available server side through tornado(web):
self.set_header('Content-Type', 'application/octet-stream')
self.set_header('Content-Disposition', 'attachment; filename=clients_counter.zip')
with open("static/clients_counter.zip", 'r') as f:
while True:
data = f.read()
if not data:
break
self.write(data)
self.finish()
The server side code seems to work fine, but the client side (extjs4.1) is really a nightmare. This is how my ajax call looks like now, and it doesn't work:
Ext.Ajax.request({
method : "GET",
url : 'http://whatever.com/count?client='+client+'&start='+start+'&end='+end,
timeout : 30000,
success :
function (response) {
//Ext.Msg.alert(response.responseText);
desktop.getWindow('count-win').doClose();
return response;
}//handler,
failure :
function(response) {
alert("Wrong request");
}});
After reading on various sources from Ext JS forum and here in stackoverflow, below is the approach I've chosen (using Ext JS version 4.2.1):
downloadFile: function(config){
config = config || {};
var url = config.url,
method = config.method || 'POST',// Either GET or POST. Default is POST.
params = config.params || {};
// Create form panel. It contains a basic form that we need for the file download.
var form = Ext.create('Ext.form.Panel', {
standardSubmit: true,
url: url,
method: method
});
// Call the submit to begin the file download.
form.submit({
target: '_blank', // Avoids leaving the page.
params: params
});
// Clean-up the form after 100 milliseconds.
// Once the submit is called, the browser does not care anymore with the form object.
Ext.defer(function(){
form.close();
}, 100);
}
I had a similar problem trying to download an Excel File in an Ajax call I solved it this way:
Make a standard sumbit instead of Ajax.
var form = Ext.create('Ext.form.Panel', { // this wolud be your form
standardSubmit: true, // this is the important part
url: '../ObtenerArchivoAdjuntoServlet'
});
form.submit({
params: {
nombreArchivo: nombreArchivo
}
});
After this you would be able return the desired file.
After extracting/reading many posts, I managed to get this simple method to work..
Ext.create('Ext.form.Panel', {
renderTo: Ext.getBody(),
standardSubmit: true,
url: 'URL'
}).submit({params: {'PARAM1': param1, 'PARAM2': param2}});
I think you can take a much easier solution. Forget about the ajax, and just get plain old js to open the file for you:
window.open('http://whatever.com/count?client='+client+'&start='+start+'&end='+end)
This will open a new tab and start the download from there.
The following code used to download the file using extjs 5 or 6. Add the following code to method and invoke this for button action. This downloads the file directly insteadof opening in new tab.
use an iframe like this:
/**
* prints the file
*/
printReport: function () {
var url = 'downloadURL';
Ext.Ajax.request({
url: url,
method: 'GET',
autoAbort: false,
success: function(result) {
if(result.status == 204) {
Ext.Msg.alert('Empty Report', 'There is no data');
} else if(result.status == 200) {
Ext.DomHelper.append(Ext.getBody(), {
tag: 'iframe',
frameBorder: 0,
width: 0,
height: 0,
css: 'display:none;visibility:hidden;height:0px;',
src: url
});
}
},
failure: function() {
//failure here will automatically
//log the user out as it should
}
});
}
Copied the answer from extjs forum
Option:2
If you want to open the file in new tab
/**
* open file in tab
*/
openReport: function () {
var url = 'downloadURL';
Ext.Ajax.request({
url: url,
method: 'GET',
autoAbort: false,
success: function(result) {
if(result.status == 204) {
Ext.Msg.alert('Empty Report', 'There is no data');
} else if(result.status == 200) {
var win = window.open('', '_blank');
win.location = url;
win.focus();
}
},
failure: function() {
//failure here will automatically
//log the user out as it should
}
});
}
You cannot use ajax to download file. I've implemented file downloading in extjs which is like ajax. see the blog ajaxlikefiledownload.
FileDownload.downloadFile = function(arguments) {
var url = arguments['url'];
var params = arguments['params'];
var successCallback = arguments['success'];
var failureCallback = arguments['failure'];
var body = Ext.getBody();
var frame = body.createChild({
tag:'iframe',
cls:'x-hidden',
id:'hiddenframe-frame',
name:'iframe'
});
var form = body.createChild({
tag:'form',
cls:'x-hidden',
id:'hiddenform-form',
action: url,
method: 'POST',
target:'iframe'
});
if (params) {
for (var paramName in params) {
form.createChild({
tag:'input',
cls:'x-hidden',
id:'hiddenform-'+paramName,
type: 'text',
text: params[paramName],
target:'iframe',
value: params[paramName],
name: paramName
});
}
}
form.dom.submit();
FileDownload.isFinished(successCallback,failureCallback);
};
FileDownload.isFinished = function(successCallback,failureCallback) {
//Check if file is started downloading
if (Ext.util.Cookies.get('fileDownload') && Ext.util.Cookies.get('fileDownload')=='true' ) {
//Remove cookie call success callback
Ext.util.Cookies.set('fileDownload', null, new Date("January 1, 1970"),application.contextPath+'/');
Ext.util.Cookies.clear('fileDownload',application.contextPath+'/');
successCallback();
return;
}
//Check for error / IF any error happens then frame will load with content
try {
if(Ext.getDom('hiddenframe-frame').contentDocument.body.innerHTML.length>0){
Ext.util.Cookies.set('fileDownload', null, new Date("January 1, 1970"),application.contextPath+'/');
Ext.util.Cookies.clear('fileDownload',application.contextPath+'/');
failureCallback();
//Cleanup
Ext.getDom('hiddenframe-frame').contentDocument.body.innerHTML = "";
return;
}
}
catch (e) {
console.log(e);
}
console.log('polling..');
// If we are here, it is not loaded. Set things up so we check the status again in 100 milliseconds
window.setTimeout('FileDownload.isFinished('+successCallback+','+failureCallback+')', 100);
};
Usage :
FileDownload.downloadFile({
url : url,
params : params,
success : function(){
//Success call back here
},
failure : function(){
//Failure callbak here
}
});
In the http response you need to add a cookie nammed fileDownload = true
I just had to ad to the success function of the ajax request:
window.open('urltothefile.ext')

Wordpress - Admin-AJAX is not saving meta data

Trying to use the built in AJAX functions with the Wordpress Admin. I've been following this tutorial, but when I run the jQuery script, the data isn't being saved to the user meta.
<?php
add_action( 'admin_footer', 'ring_jquery' );
function ring_jquery() {
?>
<script type="text/javascript">
jQuery('.ring-submit').on('click', function(){
var u = jQuery(this).attr('user'),
c = jQuery('.agt_ringc[user="'+u+'"]').val(),
x = jQuery('.agt_ringx[user="'+u+'"]').val(),
formData = 'ringu='+u+'&ringc='+c+'&ringx='+x;
jQuery.ajax({
url: '<?php echo admin_url('admin-ajax.php'); ?>',
type: 'POST',
data: formData,
success: function(e){
jQuery('.success[user="'+u+'"]').fadeIn(400).delay(400).fadeOut(400);
},
error: function(){
jQuery('.fail[user="'+u+'"]').fadeIn(400).delay(400).fadeOut(400);
}
});
});
</script>
<?php
} //End ring_jquery()
add_action('wp_ajax_my_action', 'ring_callback');
function ring_callback() {
global $wpdb; // this is how you get access to the database
$ringu = $_POST['ringu'];
$ringc = $_POST['ringc'];
$ringx = $_POST['ringx'];
update_user_meta($ringu,'agt_ringc',$ringc);
update_user_meta($ringu,'agt_ringx',$ringx);
die(); // this is required to return a proper result
}
?>
You will need to have action : my_action in the data string , or as per codex :
var data = {
action: 'my_action',
whatever: 1234
};
Issue was, as #ObmerkKronen pointed out was that I was missing an action definition in the data string (which tells the AJAX page which function to run).
formData = 'action=ring_callback&ringu='+u+'&ringc='+c+'&ringx='+x;
I also changed the name of the action hook to match the name of the function. I don't know if this was necessary, but some other things I found did it and I wanted to fit in.
add_action('wp_ajax_ring_callback', 'ring_callback');

How do I perform a jQuery ajax request in CakePHP?

I'm trying to use Ajax in CakePHP, and not really getting anywhere!
I have a page with a series of buttons - clicking one of these should show specific content on the current page. It's important that the page doesn't reload, because it'll be displaying a movie, and I don't want the movie to reset.
There are a few different buttons with different content for each; this content is potentially quite large, so I don't want to have to load it in until it's needed.
Normally I would do this via jQuery, but I can't get it to work in CakePHP.
So far I have:
In the view, the button control is like this:
$this->Html->link($this->Html->image('FilmViewer/notes_link.png', array('alt' => __('LinkNotes', true), 'onclick' => 'showNotebook("filmNotebook");')), array(), array('escape' => false));
Below this there is a div called "filmNotebook" which is where I'd like the new content to show.
In my functions.js file (in webroot/scripts) I have this function:
function showNotebook(divId) {
// Find div to load content to
var bookDiv = document.getElementById(divId);
if(!bookDiv) return false;
$.ajax({
url: "ajax/getgrammar",
type: "POST",
success: function(data) {
bookDiv.innerHTML = data;
}
});
return true;
}
In order to generate plain content which would get shown in the div, I set the following in routes.php:
Router::connect('/ajax/getgrammar', array('controller' => 'films', 'action' => 'getgrammar'));
In films_controller.php, the function getgrammar is:
function getgrammar() {
$this->layout = 'ajax';
$this->render('ajax');
}
The layout file just has:
and currently the view ajax.ctp is just:
<div id="grammarBook">
Here's the result
</div>
The problem is that when I click the button, I get the default layout (so it's like a page appears within my page), with the films index page in it. It's as if it's not finding the correct action in films_controller.php
I've done everything suggested in the CakePHP manual (http://book.cakephp.org/view/1594/Using-a-specific-Javascript-engine).
What am I doing wrong? I'm open to suggestions of better ways to do this, but I'd also like to know how the Ajax should work, for future reference.
everything you show seems fine. Double check that the ajax layout is there, because if it's not there, the default layout will be used. Use firebug and log function in cake to check if things go as you plan.
A few more suggestions: why do you need to POST to 'ajax/getgrammar' then redirect it to 'films/getgrammar'? And then render ajax.ctp view? It seems redundant to me. You can make the ajax call to 'films/getgrammar', and you don't need the Router rule. You can change ajax.ctp to getgrammar.ctp, and you won't need $this->render('ajax');
this is ajax call
$(function() {
$( "#element", this ).keyup(function( event ) {
if( $(this).val().length >= 4 ) {
$.ajax({
url: '/clients/index/' + escape( $(this).val() ),
cache: false,
type: 'GET',
dataType: 'HTML',
success: function (clients) {
$('#clients').html(clients);
}
});
}
});
});
This the action called by ajax
public function index($searchterm=NULL) {
if ( $this->RequestHandler->isAjax() ) {
$clients=$this->Client->find('list', array(
'conditions'=>array('LOWER(Client.lname) LIKE \''.$searchterm.'%\''),
'limit'=>500
));
$this->set('clients', $clients);
}
}
This is a function I use to submit forms in cakephp 3.x it uses sweet alerts but that can be changed to a normal alert. It's very variable simply put an action in your controller to catch the form submission. Also the location reload will reload the data to give the user immediate feedback. That can be taken out.
$('#myForm').submit(function(e) {
// Catch form submit
e.preventDefault();
$form = $(this);
// console.log($form);
// Get form data
$form_data = $form.serialize();
$form_action = $form.attr('action') + '.json';
// Do ajax post to cake add function instead
$.ajax({
type : "PUT",
url : $form_action,
data : $form_data,
success: function(data) {
swal({
title: "Updated!",
text: "Your entity was updated successfully",
type: "success"
},
function(){
location.reload(true);
});
}
});
});

Resources