Trying to make a plugin that can upload files directly to WP. Despite copying different blocks of code online, it seems determined to not let me upload it.
The way I'm attempting is through a XMLHttpRequest to a php file with wp_handle_upload
. I append the input file element to a formdata
and send it through the request. The data that's sent is correct and in order, as i echoed it multiple times to make sure of it, but when it gets to wp_handle_upload
, it stops working and the network tab says there was a 500 error on the request.
I've tried calling wp_handle_upload
with only the first parameter but it tells me that the form submission is invalid. I've also parsed the file data to a new variable like what's done here but to the same result.
javascript code that handles the input element file:
if ($('#active_p').is(":checked")) personDataActive = 1; //since checked isn't a boolean property, we have to analyze it to get the correct value
else personDataActive = 0;
var personDataUsers = new Array;
var personDataPlaces = new Array;
$("input[name='check_list_user[]']:checked").each(function () {
personDataUsers.push($(this).val());
})
$("input[name='check_list_places[]']:checked").each(function () {
personDataPlaces.push($(this).val());
})
//depending on the value of the id, it'll either update or insert a person
if ($('#id_p').val() > 0) id = $('#id_p').val(); //its a new person
else id = 0; //its an already existing person
let image = $('#profile_picture').prop('files');
const formData = new FormData();
formData.append("profile_picture", image[0]);
formData = new FormData();
formData.append('image', image[0]);
$.ajax({
url: "imageUpload.php",
data: formData,
processData: false,
contentType: false,
type: "POST",
success: function(response){
console.log(response);
},
error: function(response) { console.log("error: " + response)}
});
//pass all of the data as an array to make the ajax data more clean. 0 is id, 1 is name, 2 is active, 3 is birthdate, 4 is internalid, 5 is media url, 6 is users and 7 is places
personData = [id, $('#name_p').val(), personDataActive, $('#birthdate_p').val(), $('#internalid_p').val(), personDataUsers, personDataPlaces];
//ajax call to add new person!!!
$.ajax({
url: 'ajaxToPhpCalls.php', //the file to send the data to. preferably, leave it on the same folder to avoid problems
type: 'POST', //it can always be POST even if the intention is getting values, still, it's better to declare the intended type to not accidentaly overwrite stuff
data: { callFunction : "Insert", personData: personData.join(',')}, //we can't send an array directly, we have to use the .join() function to send it complete
success: function(response) {
//the response can be used for debug reasons, since it will return the same data as in Postman
//alert("Success");
console.log(response);
//hide the modal on success so there's a visual cue for the user. if it fails, they can resend the request
hideCreateModal();
var total = $("#totalNumber").val();
$("#totalNumber").val(total++); //add the new person to the total. val()++ doesn't work outside, may work inside the ()
//console.log($("#totalNumber").val());
$("body").css("cursor", "default");
resetNewPersonFields();
ResetTable();
$("body").css("cursor", "default");
},
error: function() {alert("Error"); $("body").css("cursor", "default");},
});
imageUpload.php
for WP media upload
<?php
//load both to make sure wordpress core functions are loaded
require( dirname(__FILE__) . '/../../../../../wp-config.php' );
require( dirname(__FILE__) . '/../../../../../wp-load.php' );
require_once( ABSPATH . 'wp-admin/includes/file.php' );
//check if request has required variable
if (! empty($_FILES["image"]))
{
$upload_overrides = array( 'test_form' => FALSE );
$movefile = wp_handle_upload( $_FILES['image'], $upload_overrides);
if ( $movefile && !isset( $movefile['error'] ) ) {
$ufiles = get_post_meta( $post_id, 'my_files', true );
if( empty( $ufiles ) ) $ufiles = array();
$ufiles[] = $movefile;
update_post_meta( $post_id, 'my_files', $ufiles );
}
exit;
}
else return "No files detected";
file input html
<div id="Imagens" class="tabcontent" style="display: none;">
<div class="form-group">
<label>Imagem</label><br>
<input type="file" id="profile_picture" name="profile_picture"><br>
<label>ou</label><br>
<a href="#" id="insertImageAnchor" class="button">Adicionar Imagem</a><br>
<img id="preview" src=".png" width=350px>
</form>
</div>
</div>
Trying to make a plugin that can upload files directly to WP. Despite copying different blocks of code online, it seems determined to not let me upload it.
The way I'm attempting is through a XMLHttpRequest to a php file with wp_handle_upload
. I append the input file element to a formdata
and send it through the request. The data that's sent is correct and in order, as i echoed it multiple times to make sure of it, but when it gets to wp_handle_upload
, it stops working and the network tab says there was a 500 error on the request.
I've tried calling wp_handle_upload
with only the first parameter but it tells me that the form submission is invalid. I've also parsed the file data to a new variable like what's done here but to the same result.
javascript code that handles the input element file:
if ($('#active_p').is(":checked")) personDataActive = 1; //since checked isn't a boolean property, we have to analyze it to get the correct value
else personDataActive = 0;
var personDataUsers = new Array;
var personDataPlaces = new Array;
$("input[name='check_list_user[]']:checked").each(function () {
personDataUsers.push($(this).val());
})
$("input[name='check_list_places[]']:checked").each(function () {
personDataPlaces.push($(this).val());
})
//depending on the value of the id, it'll either update or insert a person
if ($('#id_p').val() > 0) id = $('#id_p').val(); //its a new person
else id = 0; //its an already existing person
let image = $('#profile_picture').prop('files');
const formData = new FormData();
formData.append("profile_picture", image[0]);
formData = new FormData();
formData.append('image', image[0]);
$.ajax({
url: "imageUpload.php",
data: formData,
processData: false,
contentType: false,
type: "POST",
success: function(response){
console.log(response);
},
error: function(response) { console.log("error: " + response)}
});
//pass all of the data as an array to make the ajax data more clean. 0 is id, 1 is name, 2 is active, 3 is birthdate, 4 is internalid, 5 is media url, 6 is users and 7 is places
personData = [id, $('#name_p').val(), personDataActive, $('#birthdate_p').val(), $('#internalid_p').val(), personDataUsers, personDataPlaces];
//ajax call to add new person!!!
$.ajax({
url: 'ajaxToPhpCalls.php', //the file to send the data to. preferably, leave it on the same folder to avoid problems
type: 'POST', //it can always be POST even if the intention is getting values, still, it's better to declare the intended type to not accidentaly overwrite stuff
data: { callFunction : "Insert", personData: personData.join(',')}, //we can't send an array directly, we have to use the .join() function to send it complete
success: function(response) {
//the response can be used for debug reasons, since it will return the same data as in Postman
//alert("Success");
console.log(response);
//hide the modal on success so there's a visual cue for the user. if it fails, they can resend the request
hideCreateModal();
var total = $("#totalNumber").val();
$("#totalNumber").val(total++); //add the new person to the total. val()++ doesn't work outside, may work inside the ()
//console.log($("#totalNumber").val());
$("body").css("cursor", "default");
resetNewPersonFields();
ResetTable();
$("body").css("cursor", "default");
},
error: function() {alert("Error"); $("body").css("cursor", "default");},
});
imageUpload.php
for WP media upload
<?php
//load both to make sure wordpress core functions are loaded
require( dirname(__FILE__) . '/../../../../../wp-config.php' );
require( dirname(__FILE__) . '/../../../../../wp-load.php' );
require_once( ABSPATH . 'wp-admin/includes/file.php' );
//check if request has required variable
if (! empty($_FILES["image"]))
{
$upload_overrides = array( 'test_form' => FALSE );
$movefile = wp_handle_upload( $_FILES['image'], $upload_overrides);
if ( $movefile && !isset( $movefile['error'] ) ) {
$ufiles = get_post_meta( $post_id, 'my_files', true );
if( empty( $ufiles ) ) $ufiles = array();
$ufiles[] = $movefile;
update_post_meta( $post_id, 'my_files', $ufiles );
}
exit;
}
else return "No files detected";
file input html
<div id="Imagens" class="tabcontent" style="display: none;">
<div class="form-group">
<label>Imagem</label><br>
<input type="file" id="profile_picture" name="profile_picture"><br>
<label>ou</label><br>
<a href="#" id="insertImageAnchor" class="button">Adicionar Imagem</a><br>
<img id="preview" src="https://cdn-icons-png.flaticon.com/512/983/983213.png" width=350px>
</form>
</div>
</div>
Note: Currently untested, but posting here as I write it
The PHP can go in a plugin or your themes functions.php
and will give you an endpoint at yoursite.com/wp-json/demonipo/v1/profilephoto
that you can make POST
requests to via AJAX. Note it assumes you want only logged in users to use this. If that's not the case, change is_user_logged_in
to __return_true
. Don't forget to resave permalinks before trying to use it:
PHP:
<?php
add_action( 'rest_api_init', 'demonipo_rest_api_init' );
function demonipo_rest_api_init() : void {
register_rest_route( 'demonipo/v1' , '/profilephoto/', [
'methods' => 'POST',
'callback' => 'demonipo_rest_profilephoto', // function to run when this is called
'permission_callback' => 'is_user_logged_in', // only logged in users
] );
}
/**
* Handle the uploading of a profile photo
*
* @param \WP_REST_Request $request the AJAX request with all the fields WP recieved
* @return mixed returns a WP_Error if something went wrong, otherwise it returns an
* array with some info about the uploaded file
*/
function demonipo_rest_profilephoto( $request ) {
// Grab all our parameters, the file and the post to add it to
// Grab Post ID and check it's legit
if ( empty( $request['postid'] ) ) {
return new WP_Error( 'invalid', 'You need to tell us which post this is going on' );
}
$post_id = $request['postid'];
// is it really a post though?
$post = get_post( $post_id );
if ( ! $post instanceof WP_Post ) {
return new WP_Error( 'invalid', 'No post was found using the post ID you gave' );
}
$files = $request->get_file_params();
$headers = $request->get_headers();
if ( empty( $files['profile_picture'] ) ) {
return new WP_Error( 'invalid', 'No profile photo was found!' );
}
$file = $files['profile_picture'];
// Check the upload worked and is valid:
// confirm file uploaded via POST
if ( ! is_uploaded_file( $file['tmp_name'] ) ) {
return new WP_Error( 'error', 'Is uploaded file check failed!' );
}
// confirm no file errors
if (! $file['error'] === UPLOAD_ERR_OK ) {
return new WP_Error( 'error', 'Upload error!' . $file['error'] );
}
$att_id = media_handle_upload( 'profile_picture', $post_id );
// if sideloading failed, return the error so we know what happened:
if ( is_wp_error( $att_id ) ) {
return $att_id;
}
$new_data = [
'file' => basename( wp_get_attachment_image_url( $att_id, 'full' ) ),
'url' => wp_get_attachment_image_url( $att_id, 'full' ),
'type' => 'image/jpeg',
];
// All is good! Update the post meta
$ufiles = get_post_meta( $post_id, 'my_files', true );
if( empty( $ufiles ) ) {
$ufiles = [];
}
$ufiles[] = $new_data;
update_post_meta( $post_id, 'my_files', $ufiles );
// return any necessary data in the response here
return rest_ensure_response( $new_data );
}
Important notes:
$post_id
in the PHP but it was never defined anywhere so it had no way to know which post to update. Make sure to include a post_id
field in your formdata.wp_handle_upload
will move the file into the right place, but it won't create attachments, so I swapped it for media_handle_upload
. This is the missing piece you needed for it to show in the media library. I've also set it to attach it to the post you pass itimage/jpeg
. I also told it to return the fullsize image, but WP will also have created thumbnail sizes etcThat last gist has JS code that's close to what you want:
// Check, if a file is selected.
if ( 'undefined' === typeof( jQuery( '#profile_picture' )[0].files[0] ) ) {
alert( 'Select a file!' );
return;
}
// Grab the file from the input.
var file = jQuery( '#profile_picture' )[0].files[0];
var formData = new FormData();
formData.append( 'file', file );
// TODO: Add Put the post ID in your HTML somewhere
var post_id = jQuery( '#post_id' )
formData.append( 'post_id', post_id );
// Fire the request.
jQuery.ajax( {
url: '/wp-json/demonipo/v1/profilephoto',
method: 'POST',
processData: false,
contentType: false,
data: formData
} ).success( function ( response ) {
console.log( 'success!' );
console.log( response );
} ).error( function( response ) {
console.log( 'error' );
console.log( response );
});
You will need to add a hidden input with the post ID named post_id
with the same ID and name alongside that file input for this to work.
e.g.
<input type="hidden" id="post_id" name="post_id" value="<?php ECHO POST ID HERE ?>" />
<input type="file" id="profile_picture" name="profile_picture"><br>
There is one thing I left out that I don't think is necessary, the REST API nonce. Normally for authenticated requests you have to pass a nonce, which isn't hard but I don't think you need to. If you do, the API will just tell you by saying the request is forbidden, if so let me know. How to do that is covered in some of the links I added so it isn't too much work.
I've also used jQuery
instead of $
as the jQuery that comes with WordPress uses no conflict mode by default, but feel free to change that.
As a general guide, you can take a random PHP file like this:
<?php
//load both to make sure wordpress core functions are loaded
require( dirname(__FILE__) . '/../../../../../wp-config.php' );
require( dirname(__FILE__) . '/../../../../../wp-load.php' );
echo "Hi there " . $_GET['name'];
And convert it into a pretty URL REST API endpoint like this:
<?php
add_action( 'rest_api_init', 'demonipo_rest_api_init' );
function demonipo_rest_api_init() : void {
register_rest_route( 'demonipo/v1' , '/randomphp/', [
'methods' => 'GET',
'callback' => 'demonipo_randomphp', // function to run when this is called
'permission_callback' => '__return_true',
] );
}
function demonipo_randomphp( $request ) {
return 'Hi there' . $request['name'];
}
imageUpload.php
is a major security mistake. Also check your PHP error log – Tom J Nowell ♦ Commented Jul 26, 2022 at 10:49if this then do that
but there are noelse do this
orotherwise do that
to print out messages. So e.g ifif (! empty($_FILES["profile_picture"]))
isfalse
no code is ever run to tell you that, it's just assumed it will work. PS: There are good plugins for handling local uploads of avatars and profile pics, you don't need to reinvent the wheel – Tom J Nowell ♦ Commented Jul 27, 2022 at 12:36