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

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

Related

WooCommerce: Wrong Order Status Updated [duplicate]

When I try to programmatically update an order during a WP_Ajax hook. IE.
$order = wc_create_order($order_data);
$order->add_product( get_product( $membership_product_ids[0] ), 1 );
$order->set_address( $address, 'billing' );
$order->set_address( $address, 'shipping' );
$order_id = $order->id;
$order->update_status('completed');
I get the following errors:
Warning: DOMXPath::query(): Invalid expression in /wp-content/plugins/woocommerce/includes/libraries/class-emogrifier.php on line 299
Warning: DOMXPath::query(): Invalid expression in /wp-content/plugins/woocommerce/includes/libraries/class-emogrifier.php on line 299
Warning: Invalid argument supplied for foreach() in /wp-content/plugins/woocommerce/includes/libraries/class-emogrifier.php on line 302
Warning: DOMXPath::query(): Invalid expression in /wp-content/plugins/woocommerce/includes/libraries/class-emogrifier.php on line 299
Warning: DOMXPath::query(): Invalid expression in /wp-content/plugins/woocommerce/includes/libraries/class-emogrifier.php on line 299
Warning: Invalid argument supplied for foreach() in /wp-content/plugins/woocommerce/includes/libraries/class-emogrifier.php on line 302
If I remove that update_status function it does not errors.
The errors you are seeing are coming from the notification system. Your code appears able to update the status just fine. The Emogrify Class is used to generate inline CSS in an HTML template. You are likely seeing these errors because either A) you have overridden the "Completed order" email template and have created a parsing error or B) there is a WooCommerce add-on that is conflicting in some way with what you are doing.
To correct the issue, the first step will be to verify that it is the notification system by going into the admin under WooCommerce->Settings->Email->Completed order and un-checking the "Enable this email notification" box. Then save that and retest.
If your error goes away, then check your theme directory for an override of that template. It would be in <themename>/woocommerce/emails/customer-completed-order.php. If you have one, them rename it so it will not override anymore and reactivate the "Completed order" notification and see if using the default template corrects the issue.
If the issue persists with the default email notification template, then disable all of the WooCommerce add-ons you might have installed and retest. At this point, if it is working, you can re-enable them one-by-one until the issue returns. Once you have found the offending add-on, you will need to either adjust your code to allow for the add-on to not cause an issue or contact the add-on developer to address you issue.
I am not sure but you could try this trick using new WC_Order( $order_id );, to avoid the error:
// global $woocommerce; // in case of need…
$order = wc_create_order($order_data);
$order_id = $order->id; // moving this here
$_order = new WC_Order( $order_id ); // here
$_order->add_product( get_product( $membership_product_ids[0] ), 1 );
$_order->set_address( $address, 'billing' );
$_order->set_address( $address, 'shipping' );
$_order->update_status('completed');
I agree this is a strange issue.
As it is rare issue, you'd better manually open:
/wp-content/plugins/woocommerce/includes/libraries/class-emogrifier.php
and before line 299, insert these lines, and see what you will get:
var_dump($value);
var_dump($this->translateCssToXpath($value['selector']));
var_dump($xpath->query($this->translateCssToXpath($value['selector'])));
I figured this out by trying a range of different hooks, the issue was that the hook I was using to create the order happened before woocommerce initialized that what caused this issue.

Getting error call_user_func_array() expects parameter 1

Currently i install wordpress in local system with jupitex theme . But after add sales price value i am getting below error in "class-wp-hook.php" file.
"call_user_func_array() expects parameter 1 to be a valid callback, function 'jupiterx_wc_product_page_custom_sale_badge' not found or invalid function name in";
frondend working fine . but in elementor backend i am getting this error.
If anyone have idea then let me know what is exact issue.
I checked the theme code the issue is with the theme. Actually they are not passing any parameters in this filter
add_filter( 'woocommerce_sale_flash', 'jupiterx_wc_product_page_custom_sale_badge' );
But on woocommerce.php template they have added $output as function parameter which doesn't exist
function jupiterx_wc_product_page_custom_sale_badge( $output ) {
$output = str_replace( 'onsale', 'onsale jupiterx-sale-badge', $output );
return $output;
}
You can ask the theme developer to resolve the issue because even if i resolve the issue it will be gone once you update the theme.
Reinstall theme to the latest version & check as well

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.

Safely disable WP REST API

I am considering to improve security of my Wordpress website, and in doing so have come across WP REST API being enabled by default (since WP 4.4 if I'm not mistaken).
What is a safe way to disable it?
By "safe" here I mean that it does not cause unexpected side-effects, e.g. does not break any other WP core functionality.
One possible approach would be to use .htaccess rewrite rules, but surprisingly I haven't found any 'official' instructions on doing so.
Any help or recommendation is greatly appreciated :)
Update:
3rd-party plugins is not the solution I am looking for. Although I'm aware there are plenty of them that solve the task, they include many extra features that slow down the website. I would hope there is a one-line solution to this problem without the overhead of an extra plugin.
Update 2:
Here is the official opinion of Wordpress: https://developer.wordpress.org/rest-api/using-the-rest-api/frequently-asked-questions/#can-i-disable-the-rest-api
According to this, the Wordpress team wants future WP functionality to depend on the new REST API. This means there is no guaranteed safe way to disable the REST API.
Let's just hope there are enough security experts taking care of WP security.
Update 3:
A workaround is presented in WordPress API Handbook - you can Require Authentication for All Reque​sts
This makes sure that anonymous access to your website's REST API is disabled, only authenticated requests will work.
From the author original question I've chosen option 2 that came from wordpress official recommendations(https://developer.wordpress.org/rest-api/using-the-rest-api/frequently-asked-questions/#can-i-disable-the-rest-api). So just put in your functions.php to let only logged in users use the rest api (but just cross check original link in case my code block is outdated ;) ):
UPD(01-10-2021):
add_filter( 'rest_authentication_errors', function( $result ) {
// If a previous authentication check was applied,
// pass that result along without modification.
if ( true === $result || is_wp_error( $result ) ) {
return $result;
}
// No authentication has been performed yet.
// Return an error if user is not logged in.
if ( ! is_user_logged_in() ) {
return new WP_Error(
'rest_not_logged_in',
__( 'You are not currently logged in.' ),
array( 'status' => 401 )
);
}
// Our custom authentication check should have no effect
// on logged-in requests
return $result;
});
You can disable it for requests other than localhost:
function restrict_rest_api_to_localhost() {
$whitelist = [ '127.0.0.1', "::1" ];
if( ! in_array($_SERVER['REMOTE_ADDR'], $whitelist ) ){
die( 'REST API is disabled.' );
}
}
add_action( 'rest_api_init', 'restrict_rest_api_to_localhost', 0 );
The accepted answer disables all API calls from unauthenticated users, but nowadays lot of plugins are dependent on this API's functionality.
Disabling all calls will lead to unexpected site behavior which happened in my case also when I used this code.
For example, ContactForm7 makes use of this API for sending contact info to DB (I think) and for ReCaptcha validation.
I think it would be better to disable some (default) endpoints for unauthenticated users like this:
// Disable some endpoints for unauthenticated users
add_filter( 'rest_endpoints', 'disable_default_endpoints' );
function disable_default_endpoints( $endpoints ) {
$endpoints_to_remove = array(
'/oembed/1.0',
'/wp/v2',
'/wp/v2/media',
'/wp/v2/types',
'/wp/v2/statuses',
'/wp/v2/taxonomies',
'/wp/v2/tags',
'/wp/v2/users',
'/wp/v2/comments',
'/wp/v2/settings',
'/wp/v2/themes',
'/wp/v2/blocks',
'/wp/v2/oembed',
'/wp/v2/posts',
'/wp/v2/pages',
'/wp/v2/block-renderer',
'/wp/v2/search',
'/wp/v2/categories'
);
if ( ! is_user_logged_in() ) {
foreach ( $endpoints_to_remove as $rem_endpoint ) {
// $base_endpoint = "/wp/v2/{$rem_endpoint}";
foreach ( $endpoints as $maybe_endpoint => $object ) {
if ( stripos( $maybe_endpoint, $rem_endpoint ) !== false ) {
unset( $endpoints[ $maybe_endpoint ] );
}
}
}
}
return $endpoints;
}
With this, the only endpoints now open are the ones installed by the plugins.
For complete list of endpoints active on your site, see https://YOURSITE.com/wp-json/
Feel free to edit $endpoints_to_remove array as per your requirement.
If you have custom post type, make sure to add those all to the list too.
In my case, I also changed the default endpoint prefix from wp-json to mybrand-api. This should act a deterrent for bots that were making thousands of brute-force requests.
Here is what I did:
// Custom rest api prefix (Make sure to go to Dashboard > Settings > Permalinks and press Save button to flush/rewrite url cache )
add_filter( 'rest_url_prefix', 'rest_api_url_prefix' );
function rest_api_url_prefix() {
return 'mybrand-api';
}
Disabling REST API was not a bad idea, after all.
It actually opened a huge hole in all websites!
In wordpress 4.4 there was a way
Here, I've found a possible solution with .htaccess but should be carefully tested in combination with whatever else is in your .htaccess file (e.g., pretty-url rules added by wordpress itself):
# WP REST API BLOCK JSON REQUESTS
# Block/Forbid Requests to: /wp-json/wp/
# WP REST API REQUEST METHODS: GET, POST, PUT, PATCH, DELETE
RewriteCond %{REQUEST_METHOD} ^(GET|POST|PUT|PATCH|DELETE) [NC]
RewriteCond %{REQUEST_URI} ^.*wp-json/wp/ [NC]
RewriteRule ^(.*)$ - [F]
A very drastic method, is also to have a 404.html webpage in your root and then add this line:
# WP REST API BLOCK JSON REQUESTS
# Redirect to a 404.html (you may want to add a 404 header!)
RewriteRule ^wp-json.*$ 404.html
Note that, unless you use a static page, i.e., not involved with wordpress functions, if you want to return a 404 error with an appropriate error page, this is a complete separate topic, with a lot of issues when Wordpress is involved
if you want to disable Wordpress REST API completely use this code:
// Disable Wordpress REST API
remove_action( 'init', 'rest_api_init' );
remove_action( 'rest_api_init', 'rest_api_default_filters', 10 );
remove_action( 'rest_api_init', 'register_initial_settings', 10 );
remove_action( 'rest_api_init', 'create_initial_rest_routes', 99 );
remove_action( 'parse_request', 'rest_api_loaded' );
With the plugin "Disable REST API" you can select which APIs you want to enable, e.g. the contact form 7 API. See the plugin's settings (yoursite.com/wp-admin/options-general.php?page=disable_rest_api_settings)
add_filter('rest_enabled', '__return_false');
add_filter('rest_jsonp_enabled', '__return_false');
There are several points you need to "turn off". Also, you might want to place some kind of notice to someone coming to that page...
Here is what I used (and was checked):
function itsme_disable_feed() {
wp_die( __( 'No feed available, please visit the Example!' ) );
}
add_action('do_feed', 'itsme_disable_feed', 1);
add_action('do_feed_rdf', 'itsme_disable_feed', 1);
add_action('do_feed_rss', 'itsme_disable_feed', 1);
add_action('do_feed_rss2', 'itsme_disable_feed', 1);
add_action('do_feed_atom', 'itsme_disable_feed', 1);
add_action('do_feed_rss2_comments', 'itsme_disable_feed', 1);
add_action('do_feed_atom_comments', 'itsme_disable_feed', 1);
As per wp_die() docs:
This function complements the die() PHP function. The difference is
that HTML will be displayed to the user. It is recommended to use this
function only when the execution should not continue any further. It
is not recommended to call this function very often, and try to handle
as many errors as possible silently or more gracefully.
Hope this helps.

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.

Resources