Ninja Forms server-side validation not working - wordpress

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.

Related

WordPress - Verifying nonce with navigator.sendBeacon

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....
}

Wordpress BO post.php - button Publish/Update not working (error : GET 400 bad request, rest_invalid_param, orderby=menu_order)

I'm facing a weird issue on Wordpress…
I have to do some minor content updates on a Wordpress online since 2020, running perfectly.
But for only one of the CPT, when I want to add/modify a post, the publish/update (and save as draft) buttons not working. I can click on them but nothing happens…
In my console, I spot the following errors :
400 Bad Request GET https://mywebsite/wp-json/wp/v2/challenges?per_page=100&exclude=1921&parent_exclude=1921&orderby=menu_order&order=asc&_fields=id%2Ctitle%2Cparent&context=edit&_locale=user
Uncaught (in promise)
Object { code: "rest_invalid_param", message: "Paramètre(s) non valide(s) : orderby", data: {…} } in data.min.js:2:32702
I can update content without any problem with the quick edit…
Has anyone ever encountered this error?
I found a fix here :
Tim Ross webdevelopment
I had to add to functions.php the following code :
// This enables the orderby=menu_order for Posts
add_filter( 'rest_post_collection_params', 'filter_add_rest_orderby_params', 10, 1 );
// And this for a custom post type called 'portfolio'
add_filter( 'rest_challenges_collection_params', 'filter_add_rest_orderby_params', 10, 1 );
//Add menu_order to the list of permitted orderby values function filter_add_rest_orderby_params( $params ) {
$params['orderby']['enum'][] = 'menu_order';
return $params;
}
The filter hook is called rest_{post_type}_collection_params.
In my case, the post_type is "challenges".

Get current user inside register_rest_route method

How to retrive wp_get_current_user() inside a register_rest_route callback (Wordpress site)?
I'm just trying to do a simple hello wp_get_current_user()->user_login on a php test page:
add_action('rest_api_init', 'helloTest');
function helloTest() {
register_rest_route('hello', 'hello/(?P<id>\d+)', array(
'methods' => WP_REST_SERVER::READABLE,
'callback' => 'showHello'
));
}
function showHello($someVariable) {
echo "Hello " . wp_get_current_user()->user_login . $someVariable;
}
But wp_get_current_user() is null and wp_get_current_user->ID is 0;
I dont want to authenticate the user again. I just want to retrieve his username. If he is not logged in, just show empty an empty string.
If I have to authenticate again, how to add a "nonce" to it? On internet I just have examples using javascript, but not directly on PHP methods.
Issues in your code
First off, you should understand properly how to add custom WP REST API endpoints:
An endpoint's namespace (the first parameter passed to register_rest_route()) should be in this format: your-plugin/v<version>. E.g. hello/v1 or hello-world/v1 and not just hello or hello-world.
$someVariable (the first parameter passed to your endpoint callback function) is not just any variable — it's an instance of the WP_REST_Request class — and shouldn't be echo-ed like what you did here:
function showHello($someVariable) {
echo "Hello " . wp_get_current_user()->user_login . $someVariable;
}
And normally, the $someVariable is better be changed to $request (i.e. rename it to "request").
And you should return a valid WP REST API response. For example, to return just the username:
return new WP_REST_Response( wp_get_current_user()->user_login, 200 );
And know your own API endpoint URL..
(based on your original namespace)
/wp-json/hello/hello/1 <- correct
/wp-json/hello/?John <- incorrect
because in your code, the parameter is a number and not string: (?P<id>\d+)
I hope those help you, and once again, do read the handbook for a more detailed guide.
The Corrected Code
add_action( 'rest_api_init', 'helloTest' );
function helloTest() {
register_rest_route( 'hello/v1', 'hello/(?P<id>\d+)', array(
'methods' => WP_REST_SERVER::READABLE,
'callback' => 'showHello'
) );
}
function showHello( $request ) {
return new WP_REST_Response( wp_get_current_user()->user_login, 200 );
}
Now about getting the user (from the API endpoint — showHello())
If I have to authenticate again, how to add a "nonce" to it?
Just because the user is logged-in/authenticated to the (WordPress) site, it doesn't mean the user is automatically logged-in to the WP REST API. So yes, you'd need to either provide a nonce along with your API request, or use one of the authentication plugins mentioned right here.
Now in most cases, GET (i.e. read-only) requests to the API do not need any authentication, but if you'd like to retrieve the data of the currently logged-in user on your site, then one way is via the _wpnonce data parameter (either POST data or in the query for GET requests).
Example for a GET request:
http://example.com/wp-json/wp/v2/posts?_wpnonce=<nonce>
So based on your comment and the corrected code (above):
Theres no "code" that make the request. Its is just an anchor that
calls my route: Hello
You can add the nonce as part of the URL query string like so: (the namespace is hello/v1 and the <id> is 1)
// Make request to /wp-json/hello/v1/hello/<id>
$nonce = wp_create_nonce( 'wp_rest' );
echo 'Hello';
So try that out along with the corrected code and let me know how it goes. :)
And once again, be sure to read the REST API authentication handbook.

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.

WordPress Process $_POST Inside Parse Request

I'm writing an application to receive and process an Instant Payment Notification message in wordpress. It is a POST request made to a "virtual" url with rewrite rules. The problem however is that only GET vars are acessible, I cannot access the post variable:
Both:
print_r($wp->query_vars['ccustfirstname']);
print_r($_POST);
Result in an empty array
I've modified existing code to match my case:
function ao_add_rewrite_rule() {
print_r($_REQUEST);
$ipn_parameters=array("ccustfullname","ccustfirstname", ... );
foreach ($ipn_parameters as $key => $value) {
add_rewrite_tag("%$value%",".");
}
add_rewrite_tag("%arevico_api%",".","");
add_rewrite_rule( 'ipn', 'index.php?&arevico_api=ipn', 'top');
add_rewrite_rule( 'hoplink', 'index.php?&arevico_api=hop', 'top');
add_rewrite_rule( 'pay', 'index.php?&arevico_api=pay', 'top');
flush_rewrite_rules();//TODO: call only when needed
}
add_action( 'parse_request', 'wpse9870_parse_request' );
function wpse9870_parse_request( &$wp )
{
if (!empty($wp->query_vars['arevico_api'])) {
switch ($wp->query_vars['arevico_api']){
case "ipn":
print_r($wp->query_vars['ccustfirstname']);
print_r($_POST);
die();
// require_once(AREVICO_PLG_BP . "ipn.php");
// $ArevicoIPN=new ArevicoIPN();
break ;
default:
break;
}
}
return;
}
Please note that the arevico_api get parameter does get trough, but the POST parameters don't. I'm testing the application by sending a sample post data trough Simple Rest Client for chrome. How do I get to access thhe post parameters

Resources