• I am running on a shared server and as a result, do not have root access to the server.
    I am using jsPDF to convert a submitted form from HTML to PDF. This works great and there is no issues when downloading on client-side to their browser.

    I have been trying to use Ajax, however, to save the PDF to a selected folder in my directory but keep getting the error POST https://website.co.uk/wp-admin/admin-ajax.php 400 I have submitted my code on stackoverflow and no one can tell me where I have gone wrong so hopefully I’ll have more joy here 🙂

    Firstly, In functions.php I have added:

    function ASAP_scripts() {
        /** All of my other scripts are also registered here**/
    wp_register_script('js-pod', get_stylesheet_directory_uri() . '/js/POD.js', array('jquery'),'1.1', true);
        wp_enqueue_script('js-pod');
        wp_localize_script( 'js-pod', 'jspod', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
        }
    add_action( 'wp_enqueue_scripts', 'ASAP_scripts' );

    Also in functions.php:

    add_action( 'wp_ajax_so56917978_upload', 'so56917978_upload_callback' );
    add_action( 'wp_ajax_nopriv_so56917978_upload', 'so56917978_upload_callback' );
    function so56917978_upload_callback() {
        if ( ! empty( $_POST['data'] ) ) {
            $data = base64_decode($_POST['data']);
            file_put_contents( get_stylesheet_directory_uri() . '/POD/pod.pdf' , $data );
            echo "success";
        } else {
            echo "No Data Sent";
        }
    
        die();
    }

    The JS used:

    function sendToServer() { 
                html2canvas(document.getElementById("product_sheet"), {
                    onrendered: function(canvas){
                        console.log("#pdfsubmit clicked");
    
                        var img = canvas.toDataURL("image/png");
                        var doc = new jsPDF('p', 'pt', 'a4' );
                        doc.addImage(img, 'JPEG', 20, 20);
    
                        var pdf = doc.output('blob');
                        $.ajax({
                            url: jspod.ajax_url,
                            type: 'post',
                            contentType: false,
                            processData: false,
                            data:{
                                data: pdf,
                                action:'so56917978_upload',
                            },
                            dataType: 'json',
                        });
                    }
                });
            }

    I have tried different things with the above JS such as:

    changing dataType to ‘Text’
    contentType to ‘application/json charset=utf-8’

    As well as countless others. I would really appreciate if someone could advise as to where I have went wrong.

Viewing 11 replies - 1 through 11 (of 11 total)
  • Moderator bcworkz

    (@bcworkz)

    I suggest adding a “success” callback to your .ajax() call so you can see the server output. Won’t solve your issue, but could be useful all the same.

    If this is a back end script, then wp_enqueue_scripts is the wrong hook, you want admin_enqueue_scripts.

    I suspect something in so56917978_upload_callback() is failing, resulting in the 400 error. Everything else looks OK if for front end. Verify by commenting out all within the function but the die() call. If you added a “success” callback, you could simply echo “Got it” to ensure the function was called, or error_log() something if you want to check that way. Start adding code back in until the request fails again. There’s a problem with the last code added back in.

    Thread Starter Supplement Genie

    (@supplementgenie)

    @bcworkz excuse the naivety, I am new to this and unsure how to go about adding a success callback, would you be able to advise of the best way to go about it?

    Also, I commented out everything in the php down to die() but still getting the same 400 error:

    add_action( 'wp_ajax_so56917978_upload', 'so56917978_upload_callback' );
    add_action( 'wp_ajax_nopriv_so56917978_upload', 'so56917978_upload_callback' );
    function so56917978_upload_callback() {
        /* if ( ! empty( $_POST['data'] ) ) {
            $data = base64_decode($_POST['data']);
            file_put_contents( get_stylesheet_directory_uri() . '/POD/pod.pdf' , $data );
            echo "success";
        } else {
            echo "No Data Sent";
        } */
    
        die();
    }

    I also change the enqueue in functions.php to

    function admin_scripts() {
        wp_register_script('js-pod', get_stylesheet_directory_uri() . '/js/POD.js', array('jquery'),'1.1', true);
        wp_enqueue_script('js-pod');
        wp_localize_script( 'js-pod', 'jspod', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
        }
            
        add_action( 'admin_enqueue_scripts', 'admin_scripts' )
    Moderator bcworkz

    (@bcworkz)

    I’ve included an example success callback to the .ajax() code below, which all works(!). I also moved the “action” data value to the URL and that seems to make a difference in my testing of your code. No idea why. The test I did does not involve any other data like a PDF. The content of my PHP callback function is simply:

    echo 'got there';
    die;

    The .ajax() code that works:

    $.ajax({
        url: jspod.ajax_url+'?action=so56917978_upload',
        type: 'POST',
        contentType: false,
        processData: false,
        data:{
            data: 'pdf'
        },
        /*dataType: 'json',*/
        success: function(response, status) {
             alert(response);
        }
    });

    I commented out the JSON dataType due to my simplistic PHP callback which returns plain text, not JSON. There is no 400 error with the dataType argument, but it prevents the success alert from working correctly. If it were all set up to handle JSON it would probably be fine.

    Of course this all doesn’t actually do anything, but at least there is some sort of data making a successful round trip with no errors. I suggest you incrementally add back in actual functionality, testing often. Then any time there is a failure, the source would be easily determined.

    Thread Starter Supplement Genie

    (@supplementgenie)

    @bcworkz
    I have ammended the code as recommended above.

    I am now getting a 200 responce but it is not for admin-ajax.php it shows in network as admin-ajax.php?action=so56917978_upload

    Also, I get no message in console anymore but it flashes up at the top saying No Data Sent Ususally in console It would also tell me that.

    And finally… When I check network it says at the bottom:

    Request Payload
    [object object]

    If you would like a look, the address is: eazyfreight.co.uk/pod

    Thread Starter Supplement Genie

    (@supplementgenie)

    would the likes of:

    var xhr = new XMLHttpRequest();
    xhr.open( ‘post’, ‘ajax-php-link’, true ); 
    xhr.send(data);

    Work better?

    Moderator bcworkz

    (@bcworkz)

    All the reported behavior was expected. My example code requires modification to actually do anything useful. admin-ajax.php?action=so56917978_upload request is necessary for WP to “see” the value it needs to form an action hook. You can change the alert line to console.log(response); to prevent the popup and see the response in the console.

    We’re sending a JS object as the .ajax() data: argument, the payload. It’s why WP could not “see” the action value as part of that object. PHP seems to have trouble with this passed object. I don’t normally use .ajax(), something is odd about how it sends data that I’m not understanding. I normally use .post() where the passed data object is handled just fine:

    	$.post( jspod.ajax_url,{
    		data: 'pdf', 
    		action: 'so56917978_upload'
    	}, function(response, status) {
    		console.log(response);
    	});

    You can use a XMLHttpRequest object if you like. It’s not supposed to make any difference. But .ajax() and .post() aren’t supposed to make any difference either. The jQuery versions are generally easier to work with, but it’s not looking to be true for .ajax(). Use whatever works, especially if it’s something you understand better.

    Thread Starter Supplement Genie

    (@supplementgenie)

    @bcworkz
    To be honest, all this is new to me. I am currently studying software development but have done a lot more on Python than JS.

    The code I have sued has been adapted from what I have found online so when It comes to modifying code of this sort, I struggle.

    As far as I can find, the [object object] is returned because the PHP cannot figure out what I have sent, so it is receiving something, but it cannot decode.

    Thank you for your help getting over the 400 error. I will need to put a lot of research into this to figure out how to fix it

    Moderator bcworkz

    (@bcworkz)

    I’m rather weak with jQuery myself. PHP is pretty much all I do these days. When I’ve used .ajax() in the past to send a FormData object, PHP handled it just fine. There’s something about anonymous objects we’re missing. At least we have something that works.

    I suggest you take my example .post() code and send the jsPDF output in place of my 'pdf'. Reinstate your PHP Ajax handler, maybe without the conditional if ( ! empty()) for the time being. Writing to a theme folder is probably OK, but there is a chance of running into permission issues. I would try writing to /wp-content/uploads/ (WP_CONTENT_DIR.'/uploads/') first just to be safe. You can try the theme folder later if everything else is successful. (I question if a theme folder is a proper destination anyway, but that’s a separate issue.)

    Actually, on second thought, leave the 'pdf' for now and ensure the file writing of plain text is successful. Comment out the base 64 decode line for this initial test. Collect the returned value from file_put_contents(). If it’s false, echo out an appropriate error message for the jQuery success callback to console log the message. Once ‘pdf’ text can be successfully written, then try sending the jsPDF output.

    Thread Starter Supplement Genie

    (@supplementgenie)

    @bcworkz
    I have amended my code slightly (only the JS, PHP is the same as initial question)
    I was wondering if you could see anything that could help me figure this out.

    I have changed the JS to:

    function make_product_sheet() {
    
              console.log("#pdfsubmit clicked");
              var pdf = new jsPDF('p', 'pt', 'a4');
    
              pdf.addHTML(document.getElementById("product_sheet"), function() {
    
                  ps_filename = "generated-product-sheet";
                  var file = btoa(pdf.output());
    
                  var formData = new FormData();
                  formData.append('data', file);
    
                  $.ajax({
                    url: jspod.ajax_url+'?action=so56917978_upload',
                    data: formData,
                    processData: false,
                    contentType: false,
                    type: 'POST',
                    success: function(data){
                        alert(data);
                    }
                });
              });
          }

    I now get the Message Success Got There

    I cannot see the file in the designated folder though.

    when I inspect element and chose the network tab and select admin-ajax.php and scroll to the bottom I see formData – data:… and it is comprised of what looks to be a base64 string of random letters and numbers

    ie.

    formData
    data: JVBERi0xLjMKJbrfrOAKMyAwIG9iago8PC9UeXBlIC9QYWdlCi9QYXJlbnQgMSAwIFIKL1Jlc291cmNlcyAyIDAgUgovTWVkaWFCb3ggWzAgMCA1OTUuMjggODQxLjg5XQovQ29udGVudHMgNCAwIFIKPj4KZW5kb2JqCjQgMCBvYmoKPDwKL0xlbmd0aCA1Mgo Pgp……….

    My understanding is that the PHP above should decode this and place it into the relevant folder?

    Moderator bcworkz

    (@bcworkz)

    There could be a permissions issue writing to the theme folder. Try writing to wp-content.

    If that doesn’t help, you need to do a detained debug of the PHP. Debugging Ajax callbacks through Ajax is difficult. I suggest making a PHP script to test drive the callback directly. Make a custom page template to do this. Then you only need to load the related page to run the code. Have your test code base64 encode some plain text and stuff it into $_POST[‘data’], then call the function directly (or use do_action()).

    You can then output debug data from the Ajax handler and it’ll show up on the page. Ensure the data placed in $_POST gets decoded correctly. Collect the return value from file_put_contents() and dump it out. If the return is false, there’s probably a permission issue. If a number is returned, PHP thinks it wrote to a file somewhere. Be sure the path is to where you think it is.

    Thread Starter Supplement Genie

    (@supplementgenie)

    @bcworkz

    I solved my problem with…

    PHP:

    add_action( 'wp_ajax_so56917978_upload', 'so56917978_upload_callback' );
    add_action( 'wp_ajax_nopriv_so56917978_upload', 'so56917978_upload_callback' );
    function so56917978_upload_callback() {
        $res = [
            'saved' => 0,
        ];
    
        if ( ! empty( $_POST['data'] ) ) {
            $pdf = get_stylesheet_directory() . '/POD/pod.pdf';
            $data = base64_decode($_POST['data']);
            file_put_contents( $pdf, $data );
            $res['pdf'] = $pdf;
            $res['saved'] = 1;
        } else{
            $res['error'] = 'No Data Sent'; 
        }
        wp_send_json( $res );
    }

    jQuery

    jQuery( "#pdfsubmit" ).click(function() {
    	var $form = $("form[name='pdf-download']"),
    		$successMsg = $(".alert");
    	$.validator.addMethod("letters", function(value, element) {
    		return this.optional(element) || value == value.match(/^[a-zA-Z\s]*$/);
        });
    	$form.validate({    
    		rules: {
    			firstname: {
    				required: true,
    				minlength: 3,
    				letters: true
                },
                lastname: {
    				required: true,
    				minlength: 3,
    				letters: true
    			},
    			email_id: {
    				required: true,
    				email: true
    			}
    		},
    		messagess: {
                firstname: "Please specify your first name (only letters and spaces are allowed)",
                lastname: "Please specify your last name (only letters and spaces are allowed)",
    			email_id: "Please specify a valid email address"
    		},
            submitHandler: function sendToServer() {
    
                console.log("#pdfsubmit clicked");
                var pdf = new jsPDF('p', 'pt', 'a4');
    
                pdf.addHTML(document.getElementById("product_sheet"), function() {
    
                    var file = btoa(pdf.output());
    
                    var formData = new FormData();
                    formData.append('data', file);
    
                    $.ajax({
                        url: jspod.ajax_url + '?action=so56917978_upload',
                        data: formData,
                        processData: false,
                        contentType: false,
                        debug:true,
                        type: 'POST',
                        success: function(data) {
                            alert(data);
                        }
                    });
                });
            }
        });
    });

    Thats the complete code with form verification as well.

    thank you for the help you gave 🙂 I got there lol

Viewing 11 replies - 1 through 11 (of 11 total)

The topic ‘Sending to server using Ajax is giving /admin-ajax.php 400 error’ is closed to new replies.