WordPress - Verifying nonce with navigator.sendBeacon - wordpress

I am working on a very simple tracking plugin. I am using the visibilitychange event to send tracking data to a PHP script included in the plugin using navigator.sendBeacon.
However, unlike a simple WordPress AJAX request, the PHP file doesn't have access to any of the WordPress core files. Therefore the check_ajax_referer function throws an error.
I can't figure out a way of ensuring the PHP file has access to the WordPress core files, or even any of the files I have included in the plugin.
Is it possible to set this up like is normally done with WP and AJAX, or do I need to include all of the WP core files in the PHP script? And, if the second option is the only option, which files do I need to include?
Example code:
Enqueuing the JS script and localizing the PHP script path and nonce:
wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'dist/scripts/main.js', array( 'jquery' ), $this->version, true );
wp_localize_script( $this->plugin_name, 'OBJECT_NAME', array('PHP_SCRIPT_PATH' => PATH, 'ajax_nonce' => wp_create_nonce('TRACKING_NONCE')));
Send the data
document.addEventListener("visibilitychange", function () {
if (document.visibilityState === 'hidden') {
var data = new FormData();
data.append('tracking_data', JSON.stringify(form_data));
navigator.sendBeacon(OBJECT_NAME.PHP_SCRIPT_PATH, data);
}
});
Receive and process the data:
if (!empty($_POST)) {
$security_check = check_ajax_referer( 'TRACKING_NONCE', 'security' ); //Error thrown: Uncaught Error: Call to undefined function check_ajax_referer()
//Process data....
}

Related

Make an HTTP POST request to upload a file in Wordpress - HTTP POST request get converted to GET

I want to have a HTTP POST link in my Wordpress website that lets another server to post an xml file every hour into the Wordpress server and I save it.
I created an index.php file in folders that map with the route I want, let say I need example.com/jobs/uploadFile, so I created a php file inside the folders /jobs/uploadFile of the root Wordpress directory.
<?php
if( $_SERVER['REQUEST_METHOD'] !== 'POST' ) {
header($_SERVER["SERVER_PROTOCOL"]." Method Not Allowed", true, 405);
exit;
}
$postData = trim(file_get_contents('php://input'));
$xml = simplexml_load_string($postData);
if($xml === false) {
header($_SERVER["SERVER_PROTOCOL"]." Bad Request", true, 400);
exit;
}
$xml->asXml('jobs.xml');
http_response_code(200);
1- I send a HTTP POST request via postman, but somehow the server or Wordpress changes it a HTTP GET request, so always the first if condition is executed. I'm using Laravel forge server with Nginx.
2- Appreciate any security advice about this approach, CORS...?
Thanks for your help
Since it may help others, I answer my question. I was doing it the wrong way. The better way to do it is by using actions in a custom Wordpress plugin. Just create a custom plugin and use add_action inside it:
add_action( 'rest_api_init', function() {
register_rest_route(
'myapi/v1', 'myUploadURL',
[
'methods' => 'POST',
'callback' => 'my_upload_function',
'permission_callback' => '__return_true',
]
);
});
And then you can get the $_FILES of the POST request in the my_upload_function() and save it on your server.

Wordpress Cron - Call External API - Save JSON File

i thought i would reach out to get some guidance on a little thing i am working on.
What i would like to do within Wordpress:
Call external API (with token header)
Get the results of the api and save it into a file in wpallimport's upload folder
I would assume i can just make a simple WP plugin and within the 'activate' hook for the plugin:
create a wp-cron (as i would like it to run every day) for the following:
$url = 'the-api-url';
$data = wp_remote_get( $url ,
array('headers' => array( 'Token' => 'tokenkey')
));
$jsonfile = $data['body'];
global $wp_filesystem;
if (empty($wp_filesystem)) {
require_once (ABSPATH . '/wp-admin/includes/file.php');
WP_Filesystem();
}
$file = '/wp-content/uploads/wpallimport/files/JSONFILE.JSON';
$wp_filesystem->put_contents($file, $jsonfile);
However i am not having success with the above (with the correct API url and token etc obviously)
Thanks in advance!

Ninja Forms server-side validation not working

I have spent two days trying to figure this out. I have wordpress page with a four field Ninja Form. One of the fields is an Email. I created an API (in our .NET Core project) that checks and then throws a 400 error if a already with that email address exists in our DB. I am using the Ninja Forms Webhook feature to submit the form to my API. I just want to display my 400 error message to the user.
I am using the documentation below from Ninja Forms but it is not working:
https://developer.ninjaforms.com/codex/custom-server-side-validation/
More specifically, I am getting a 500 with the the following error in the console:
Uncaught Error: Call to undefined function my_nf_validation() in \/home\/site\/wwwroot\/wp-content\/themes\/hello-elementor\/functions.php:245\nStack trace:\n#0 \/home\/site\/wwwroot\/wp-includes\/class-wp-hook.php(287): {closure}(Array)\n#1 \/home\/site\/wwwroot\/wp-includes\/plugin.php(206): WP_Hook->apply_filters(Array, Array)\n#2 \/home\/site\/wwwroot\/wp-content\/plugins\/ninja-forms\/includes\/AJAX\/Controllers\/Submission.php(132): apply_filters('ninja_forms_sub...', Array)\n#3 \/home\/site\/wwwroot\/wp-includes\/class-wp-hook.php(287): NF_AJAX_Controllers_Submission->submit('')\n#4 \/home\/site\/wwwroot\/wp-includes\/class-wp-hook.php(311): WP_Hook->apply_filters('', Array)\n#5 \/home\/site\/wwwroot\/wp-includes\/plugin.php(478): WP_Hook->do_action(Array)\n#6 \/home\/site\/wwwroot\/wp-admin\/admin-ajax.php(175): do_action('wp_ajax_nf_ajax...')\n#7 {main}\n thrown<\/pre>"},"last":{"type":1,"message":"Uncaught Error: Call to undefined function my_nf_validation() in \/home\/site\/wwwroot\/wp-content\/themes\/hello-elementor\/functions.php:245\nStack trace:\n#0 \/home\/site\/wwwroot\/wp-includes\/class-wp-hook.php(287): {closure}(Array)\n#1 \/home\/site\/wwwroot\/wp-includes\/plugin.php(206): WP_Hook->apply_filters(Array, Array)\n#2 \/home\/site\/wwwroot\/wp-content\/plugins\/ninja-forms\/includes\/AJAX\/Controllers\/Submission.php(132): apply_filters('ninja_forms_sub...', Array)\n#3 \/home\/site\/wwwroot\/wp-includes\/class-wp-hook.php(287): NF_AJAX_Controllers_Submission->submit('')\n#4 \/home\/site\/wwwroot\/wp-includes\/class-wp-hook.php(311): WP_Hook->apply_filters('', Array)\n#5 \/home\/site\/wwwroot\/wp-includes\/plugin.php(478): WP_Hook->do_action(Array)\n#6 \/home\/site\/wwwroot\/wp-admin\/admin-ajax.php(175): do_action('wp_ajax_nf_ajax...')\n#7 {main}\n thrown","file":"\/home\/site\/wwwroot\/wp-content\/themes\/hello-elementor\/functions.php","line":245}},"debug":[]}
I placed the code in the function.php file in Themes Editor. This is my first time working with wordpress so I hope I am doing this right. This is my code and is taken directly from the above documentation:
add_filter( 'ninja_forms_submit_data', function( $form_data ){
if( ! my_nf_validation( $form_data ) ) { // Add check here.
$errors = [
__( 'An unexpected error occurred.', 'my-plugin' )
];
$response = [
'errors' => $errors,
];
echo wp_json_encode( $response );
wp_die(); // this is required to terminate immediately and return a proper response
}
// If no errors, be sure to return the $form_data.
return $form_data;
});
I would really appreciate anyone's help with this.
Try the following Code, It will work.
function validateCode($field){
if( checkCondition ){
// let it go
return true;
}
else{
return false;
}
}
add_filter( 'ninja_forms_submit_data', 'my_ninja_forms_submit_data' );
function my_ninja_forms_submit_data( $form_data ) {
$form_settings = $form_data[ 'settings' ]; // Form settings.
if($form_data['id'] == '2'){
foreach( $form_data[ 'fields' ] as $field ) { // Field settigns, including the field key and value.
if( 'unique_code' == $field[ 'key' ] ){
if(!validateCode($field)){
$form_data['errors']['fields'][$field['id']] = "INVALID CODE";
}
}
}
}
return $form_data;
}
The documentation references a custom WordPress filter, as defined by the Ninja Forms plugin. Filters are a feature of WordPress Hooks, which allow a theme or plugin to modify the functionality of another theme or plugin. See https://developer.wordpress.org/plugins/hooks/filters/
To your question, the Webhooks add-on plugin (an official extension of Ninja Forms) is not required to use the filter that you reference from the documentation.
Instead, you'll need to replace "my_nf_validation()" with your own defined function, as the documentation shows an example.
Your function needs to handle the API request and response handling. In the documentation example, the custom function returns a Boolean value.

How to integrate Dropzonejs with wordpress media handler in frontend?

How can I integrate Dropzonejs file uploader library in wordpress front end just like the built in one and have the uploaded one available in my media library?
Dropzonejs is a very extensive javascript library that provides a lot of options to handle media uploading.
To integrate dropzonejs with wordpress the process is pretty straight forward. Assume the following piece of code is where you want to appear your uploader.
<div id="media-uploader" class="dropzone"></div>
<input type="hidden" name="media-ids" value="">
Having a class dropzone will automatically attach the dropzone event with the element. That will stop us from overriding default parameters. So we would like to disable the auto discover feature of the library.
// Disabling autoDiscover, otherwise Dropzone will try to attach twice.
Dropzone.autoDiscover = false;
Now we will use jQuery to bind our configuration with the element.
jQuery("#media-uploader").dropzone({
url: dropParam.upload,
acceptedFiles: 'image/*',
success: function (file, response) {
file.previewElement.classList.add("dz-success");
file['attachment_id'] = response; // push the id for future reference
var ids = jQuery('#media-ids').val() + ',' + response;
jQuery('#media-ids').val(ids);
},
error: function (file, response) {
file.previewElement.classList.add("dz-error");
},
// update the following section is for removing image from library
addRemoveLinks: true,
removedfile: function(file) {
var attachment_id = file.attachment_id;
jQuery.ajax({
type: 'POST',
url: dropParam.delete,
data: {
media_id : attachment_id
}
});
var _ref;
return (_ref = file.previewElement) != null ? _ref.parentNode.removeChild(file.previewElement) : void 0;
}
});
In the code above what we have done is we attached dropzone with our element with some parameters-
url - location where we want to send our files to upload. I'll initialize the variable later.
acceptedFiles - since we are only interested in uploading images, we will limit the files to be attached only to images. You can find about more in the website of this library.
success - a callback that is fired when the file/image is uploaded successfully. It accepts two parameter the reference of the uploaded file itself and the response from the server. This is very important, here we stored the attachment id in our form. You can perform a validation here prior to store the id.
error - if the file failed to upload then you can perform any task here.
addRemoveLinks - add the remove file link below the preview panel, you can style it with your css.
removedfile - handles the operation while you click on the remove file link for an image in the preview panel. In this function we sent an ajax call to our server to remove the image from the library
Of course there are a lot of option available, but I found these are the most basic parameters I required to setup my drag-n-drop media uploader.
Now the most important thing is to decide about the file uploader url. You can have a custom file where you would want to process the operation. But I found another way.
From this question and the answer I found using admin-post.php file is pretty amazing.
Many people complained about this admin-post.php, so think sticking to the wp_ajax.php is the best option.
So I initialized the drophandler variable prior to my dropzone initialization as follows-
wp_enqueue_script('dropzone','path/to/dropzone', array('jquery'));
wp_enqueue_script('my-script','path/to/script',array('jquery','dropzone'));
$drop_param = array(
'upload'=>admin_url( 'admin-ajax.php?action=handle_dropped_media' ),
'delete'=>admin_url( 'admin-ajax.php?action=handle_deleted_media' ),
)
wp_localize_script('my-script','dropParam', $drop_param);
Now we are ready to send our images to the server. Here we will add some php code whether in the theme's function.php file or in our plugin file, but we need to be assured that it is loaded.
The following function will take care of the uploading the image and saving as an attachment in the library.
add_action( 'wp_ajax_handle_dropped_media', 'handle_dropped_media' );
// if you want to allow your visitors of your website to upload files, be cautious.
add_action( 'wp_ajax_nopriv_handle_dropped_media', 'handle_dropped_media' );
function handle_dropped_media() {
status_header(200);
$upload_dir = wp_upload_dir();
$upload_path = $upload_dir['path'] . DIRECTORY_SEPARATOR;
$num_files = count($_FILES['file']['tmp_name']);
$newupload = 0;
if ( !empty($_FILES) ) {
$files = $_FILES;
foreach($files as $file) {
$newfile = array (
'name' => $file['name'],
'type' => $file['type'],
'tmp_name' => $file['tmp_name'],
'error' => $file['error'],
'size' => $file['size']
);
$_FILES = array('upload'=>$newfile);
foreach($_FILES as $file => $array) {
$newupload = media_handle_upload( $file, 0 );
}
}
}
echo $newupload;
die();
}
The following action take care of the deletion of the media element. Second parameter of wp_delete_attachment() function allows us to decide whether we want to trash the image or completely delete it. I wanted to delete it completely so passed true.
add_action( 'wp_ajax_handle_deleted_media', 'handle_deleted_media' );
function handle_deleted_media(){
if( isset($_REQUEST['media_id']) ){
$post_id = absint( $_REQUEST['media_id'] );
$status = wp_delete_attachment($post_id, true);
if( $status )
echo json_encode(array('status' => 'OK'));
else
echo json_encode(array('status' => 'FAILED'));
}
die();
}
This will return the attachment_id in the response and we'll get it in the success function. In the media_handle_upload( $file, 0 ); I passed the reference of the file and a 0 because I didn't wanted to assign the media with any post yet (0 for no post, but if you want to assign then pass the post ID here. More reference in the codex.)
This is all for uploading media in wordpress.
Note: I haven't completed the removing uploaded file part. I'll complete this in a moment.
UPDATE
The post is updated. Now we can remove uploaded media elements from the uploader container. Thanks to this question and the answer I could figure out the actual process.
Those who are having problems getting this to work for non-admin users; please use admin-ajax.php instead of admin-post.php.
I had faced a strange issue that admin-post.php would work for non-admin users on my local server; but my live server refused to let non-admins upload files. php would echo entire page instead of the echoed value.
I replaced admin-post.php with admin-ajax.php and uploads work super cool.
I hope this helps.
The solution added to this post is incorrect unless I've misunderstood the question. Basically the solution won't work for anyone who isn't logged in as an admin. It took me 30 minutes to work it out plus the solution for removing images doesn't delete it from the media library.

Edit include/mail.inc, add my own SMTP setting there

The following function is contained in include/mail.inc of Drupal6, it uses the default SMTP settings buried in a file named "php.ini" to send mail.
function drupal_mail_send($message) {
// Allow for a custom mail backend.
if (variable_get('smtp_library', '') && file_exists(variable_get('smtp_library', ''))) {
include_once './'. variable_get('smtp_library', '');
return drupal_mail_wrapper($message);
}
else {
$mimeheaders = array();
foreach ($message['headers'] as $name => $value) {
$mimeheaders[] = $name .': '. mime_header_encode($value);
}
return mail(
$message['to'],
mime_header_encode($message['subject']),
// Note: e-mail uses CRLF for line-endings, but PHP's API requires LF.
// They will appear correctly in the actual e-mail that is sent.
str_replace("\r", '', $message['body']),
// For headers, PHP's API suggests that we use CRLF normally,
// but some MTAs incorrecly replace LF with CRLF. See #234403.
join("\n", $mimeheaders)
);
}
}
but I use shared host, therefore i can't edit php.ini, i want to edit the above function "drupal_mail_send", add the codes below into that function so that it can bypass the PHP mail() function and send email directly to my favorite SMTP server.
include('Mail.php');
$recipients = array( 'someone#example.com' ); # Can be one or more emails
$headers = array (
'From' => 'someone#example.com',
'To' => join(', ', $recipients),
'Subject' => 'Testing email from project web',
);
$body = "This was sent via php from project web!\n";
$mail_object =& Mail::factory('smtp',
array(
'host' => 'prwebmail',
'auth' => true,
'username' => 'YOUR_PROJECT_NAME',
'password' => 'PASSWORD', # As set on your project's config page
#'debug' => true, # uncomment to enable debugging
));
$mail_object->send($recipients, $headers, $body);
Could you write down the modified code for my reference?
The code in drupal_mail_send is part o the Drupal core functionality and should not be changed directly as your changes may be overwritten when you update Drupal.
Modifications of Drupal core files is often referred to by the Drupal community as "hacking core" and is largely discouraged.
Drupal already has a number of modules available which may help you. See:
http://drupal.org/project/phpmailer module:
Adds SMTP support for sending e-mails using the PHPMailer library.
Comes with detailed configuration instructions for how to use Google
Mail as mail server.
http://drupal.org/project/smtp module:
This module allows Drupal to bypass the PHP mail() function and send
email directly to an SMTP server.

Resources