Get current user inside register_rest_route method - wordpress

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.

Related

Altering a dynamic route in Drupal 8 with RouteSubscriber

I have a web-app made in Drupal 8, and I have some routing problems. Users will login using an external service, and after login they are sent to the path /user/{user}, where {user} is their user id. I want to change this behaviour and send them to a page /dashboard. Not having access to what the external service routing is doing, I need to reroute /user/{user} to /dashboard. It seems like the drupal 8 redirect module could solve the problem, but I am not too keen on using an external module just for this simple task.
Because of this, I tried to change the route with drupal's RouteSubscriber and alterRoutes method. So I made my RouteSubscriber class in my module called "module" like this:
class RouteSubscriber extends RouteSubscriberBase {
protected function alterRoutes(RouteCollection $collection) {
if ($route = $collection->get('user.page')) {
$route->setDefaults(array(
'_controller' => '\Drupal\module\Controller\Dashboard::content',
));
}
if ($route = $collection->get('entity.user.canonical')) {
$route->setDefaults(array(
'_controller' => '\Drupal\module\Controller\Dashboard::content',
));
}
}
}
The route user.page has path /user, and the path entity.user.canonical is the route I'm interested in, which has path /user/{user}, where {user} is again a path parameter. When I go to the page /user, the dashboard is displayed as expected, but going to for example /user/123 does not show the dashboard, but seems display what the route originally did. Just to test whether it was impossible to alter this route, I tried to set all routes to have display the dashboard by inserting the following code into RouteSubscriber:
foreach ($collection->all() as $route) {
$route->setDefaults(array(
'_controller' => '\Drupal\module\Controller\Dashboard::content',
'pid' => '',
'uid' => '',
'modifier' => '',
'display' => '',
));
}
The junk pid, uid, modifier and display are just defaults to different routes path parameters so the code will run. This makes the page /user/123 correctly display the dashboard! However, I do get the following error message at the bottom of the screen:
The website encountered an unexpected error. Please try again later.
Symfony\Component\Routing\Exception\MissingMandatoryParametersException: Some mandatory parameters are missing ("filter") to generate a URL for route "devel.configs_list". in Drupal\Core\Routing\UrlGenerator->doGenerate() (line 182 of core/lib/Drupal/Core/Routing/UrlGenerator.php).
So, what am I doing wrong here? Can what I want be done, or should I do something different to achieve what I want? Also, feel free to ask for more code if needed!

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!

How to get current logged in user from WordPress via custom endpoints?

I have an endpoint in my WordPress plugin, and using it. I want to get the user who is currently logged in the WordPress. My endpoint looks like this:
add_action( 'rest_api_init', function () {
register_rest_route( 't2mchat/v2', '/get_curr_user', array(
'methods' => 'GET',
'callback' => 'get_curr_user'
));
});
The callback function:
function get_curr_user(WP_REST_Request $request){
$user = wp_get_current_user();
return $user;
}
This gives me back user ID as 0.
I read the article on WordPress official website about Authentication , and learned that I need to pass nonces, but since I am a new to this, I could not understand everything it says.
Also, I am calling this endpoint in my React app like this:
loadData() {
return fetch(`/wordpress/wp-json/t2mchat/v2/get_curr_user`)
.then(response => response.json())
.then(responseJson => {
this.setState({ curr_user: responseJson });
//console.log(this.state.curr_user, "curr user role");
})
.catch(error => {
console.error(error);
});
}
I am not sure how do I pass nonces in the request, so I can get the currently logged in user.
Can anyone with experience/idea suggest what changes I need to make in my code?
An example would be highly appreciated.
Thank you.
in your PHP file include the wp-load.php
include_once("wp-load.php");
then you can access to all native Wordpress Function just call them.
So you have to be able to retrieve the current logged in user by wp_get_current_user();
I used session storage to store user data when the wordpress initializes and retrieved from my endpoint. It worked for me.

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