Good day, everyone!
I have a problem with apprequests.
When i use Request Dialog, i have response with request id. Here is the code:
<html xmlns:fb="http://www.facebook.com/2008/fbml">
<body>
<script src="http://connect.facebook.net/en_US/all.js"></script>
<div id="fb-root"></div>
<script>
FB.init({appId:'400884386588720', xfbml:true, cookie:true});
var too = new Array('100003484704320');
function send() {
FB.ui({
method:'apprequests',
message:'http://wasm.ru',
to:too
}, function (response) {
var request = response.request;
var request_id = request + '_' + too[0];
console.log(request_id);
});
}
</script>
</body>
<input type="button" onclick="send(); return true;" value='Request'>
</html>
But user can't see this request! When facebook page refreshing, i can see notification, but after loading it disappears.
When i try use Graph Api, i have an error:
[error] => stdClass Object
(
[message] => (#200) All users in param ids must have accepted TOS
[type] => OAuthException
[code] => 200
)
Here is the code:
$token_url = "https://graph.facebook.com/oauth/access_token?" .
"client_id=" . $this->app_id.
"&client_secret=" . $this->secret .
"&grant_type=client_credentials";
$app_token = $this->request($token_url, 'POST');
$app_token = explode('=', $app_token);
$app_token = $app_token[1];
$message="Message with space and with link - http://wasm.ru";
$message = urlencode($message);
$url = 'https://graph.facebook.com/'.$user.'/apprequests?'.'message='.$message.'&access_token='.$app_token.'&method=post';
$res = $this->request($url, 'POST');
And the request function:
$ch = curl_init();
$options = array();
$options[CURLOPT_URL] = $url;
$options[CURLOPT_SSL_VERIFYPEER] = false;
$options[CURLOPT_RETURNTRANSFER] = true;
if($method == 'get') {
$options[CURLOPT_HTTPGET] = true;
} else {
$options[CURLOPT_CUSTOMREQUEST]= 'POST';
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
if($action == 'access_token'){
return $response;
}
$response = json_decode($response);
return $response;
I can't find error... Help!
Thanks.
The error message you receive ("All users in param ids must have accepted TOS") is because you are trying to send an app generated request to a user who is not connected to your app.
See the developer docs here https://developers.facebook.com/docs/requests/#app_to_user.
Requests sent with the request dialog and app generated requests are different and you can't use app generated requests to invite users to your app.
Related
I am sending request to WordPress REST API endpoint -
https://example.com/wp-json/wp/v2/comments.
But it is not sending JSON as response.
Instead, it is sending HTML having script tag as follows -
<html>
<body>
<script>
document.cookie =
"_test=<cookie value> ; expires=<expiary date>; path=/";
document.location.href =
"https://example.com/wp-json/wp/v2/comments";
</script>
</body>
</html>
Here, 1) a cookie named _test is getting created, 2) After that, it is redirecting to the requested URL with document.location.href.
So, when I am trying to parse the response using JSON.parse method, then it is failing, as it is in HTML format.
But, when I am entering the endpoint URL in browser search bar, then document.location.href method inside the script of the response is helping to redirect to the expected JSON.
My expected response should be like this
[{"id":1,"post":1,"parent":0,"author":0,"author_name":"A WordPress Commenter","author_url":"https:\/\/wordpress.org\/","date":"2022-07-22T16:38:55","date_gmt":"2022-07-22T16:38:55","content":{"rendered":"Comment 1"}}, /*...*/]
Now, how to get response as JSON directly, instead of HTML?
Your question is confusing. In your code you are using JavaScript to redirect the page to the json data, not fetch the information.
Try this and check your console. You should be able to take it from there.
const url = "https://example.com/wp-json/wp/v2/comments";
const comments = getComments( url );
// My function
async function getComments( url ) {
// Try to fetch the url
await fetch( url )
.then( function ( response ) {
console.log( response );
// Get the status
console.log( "Status code: " + response.status );
// The API call was unsuccessful
if ( response.status > 299 ) {
throw new Error( finalError );
// The API call was successful
} else {
return response.json();
}
})
.then( function ( data ) {
// This is the response data as a text string
console.log( data );
// Make sure we have data
if ( data.length == 0 ) {
throw new Error( finalError );
}
// Only continue if not null or empty
if ( data[0] !== null && data[0] !== undefined && data.length > 0 ) {
// Iter through each item
for ( let d = 0; d < data.length; d++ ) {
// Log it
console.log( data[d] );
}
}
})
.catch( function handleError( error ) {
console.log( error );
}
);
}
we are using wordpress JSON API to signon a user and to add / update / remove cart-items. We are doing this with the register_rest_route function.
We use this code to remove a cart item:
function remove_from_cart(WP_REST_Request $req)
{
$resp = null;
$cart_item = $req['cart_item'];
try {
WC()->cart->remove_cart_item($cart_item);
} catch (Exception $e) {
$resp = $e;
}
return rest_ensure_response(new CartResponse());
}
This is working perfectly fine for guests. but as soon as a logged in user tries it, the cart is back to its normal state after a page reload. The response created by new CartResponse() is correctly showing the cart without the removed item. however, after a page reload the item is still there.
As this only happens for logged in users and not for guests I think it is a session issue.
Also, updating the cart with the following method works for logged in users:
function update_cart_item(WP_REST_Request $req)
{
$resp = null;
$cart_item = $req['cart_item'];
try {
if ($cart_item && $cart_item['quantity']) {
WC()->cart->set_quantity($cart_item['key'], $cart_item['quantity']);
}
} catch (Exception $e) {
$resp = $e;
}
return rest_ensure_response(new CartResponse());
}
Unfortunately, setting the quantity to 0 is also not working.
This is how we signon users:
function login_customer(WP_REST_Request $req)
{
$body = $req->get_body();
$input = json_decode($body, TRUE);
$credentials = ['user_login' => $input['email'], 'user_password' => $input['password']];
$user = wp_signon($credentials, false);
if (is_a($user, 'WP_Error') || !$user) {
// if an error occurs, return null
return rest_ensure_response(null);
}
$resp = new CustomerResponse($user->ID);
return rest_ensure_response($resp);
}
And we are not using any caching plugins. What is wrong here?
Here is a list of all session cookies:
EDIT:
I just inspected the cookies while beeing logged in and removing a cart item.
Cart Hash before deleting: bb35785a228a17ceb85f8ed2dc522b16
Cart Hash directly after deleting: d32e22e278d42022e04b6992b7d65816
Cart Hash after page reload: bb35785a228a17ceb85f8ed2dc522b16 again
So it seems like the cart hash is stored somewhere and restored on a reload, but not correctly updated on deleting a cart item
It seems like you need nonces to authenticate DELETE requests.
Now I am adding nonces to each response in a header:
function add_cors_http_header(){
header("X-WP-Nonce: ".wp_create_nonce('wp_rest'));
}
add_action('init','add_cors_http_header');
And in the frontend I set it:
let nonce: string = null;
export const fetchNoAuth = (endpoint: string, method: string = 'GET', data: any = null): Promise<any> => {
let headers: any = {'Content-Type': 'application/json'};
if (nonce) {
headers['X-WP-Nonce'] = nonce;
}
return fetch('http://' + apiUrl + apiPath + endpoint + '?' + debugQuery, {
method,
credentials: 'include',
headers,
body: data ? JSON.stringify(data) : null
})
.then((data) => {
const nonceFromResponse = data.headers.get('X-WP-Nonce');
if (nonceFromResponse) {
nonce = nonceFromResponse;
} else {
nonce = null;
}
return data;
})
};
Make sure that the header in the request is named X-WP-Nonce
I'm using Google Calendar API to display events on fullcalendar (so using a json object in my view). I'm using codeigniter php framework, and I have a few functions in my controller to create a new client then I use that in the oauth2callback() function to exchange my code for an access_token then I start calling the service in gcalendar() and gcalendar_events. I have set the accessType to offline but that doesn't seem to make me access the events offline. It works great except that I'm redirected to log in again every time the session ends. I don't want that, I want them to display all the time after the session ends. I am trying to use a refresh token in case the access_token expires to see if that would fix the problem.
this is the code in my controller
function getClient() {
$client = new Google_Client();
$client->setApplicationName("DL Calendar");
$client->setAuthConfig('application/client_secrets.json');
$client->addScope('profile');
$client->setIncludeGrantedScopes(true);
$client->setAccessType('offline');
return $client;
}
function gcalendar() {
$this->load->add_package_path(APPPATH . 'vendor/autoload');
$client = $this->getClient();
//$client->setRedirectUri(site_url('calendar/index'));
$client->addScope(Google_Service_Calendar::CALENDAR);
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$access_token = $_SESSION['access_token'];
$service = new ]Google_Service_Calendar($client);
$calendar = new Google_Service_Calendar_Calendar();
//$calendarList = $service->calendarList->listCalendarList();
$calendar = $service->calendars->get('primary');
$params = array(
'owner_id' => get_current_user_id(),
'title' => get_current_user(). ' ' .'Google Calendar',
'type' => 'gcal',
'url' => $calendar->id,
);
$calendar_id = $this->Calendar_model->add_calendar($params);
redirect('calendar/index');
} else {
$redirect_uri = site_url('calendar/oauth2callback');
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
$this->session->set_flashdata('success', 'Event Successfully Added');
}
function oauth2callback() {
//Build the client object
$client = $this->getClient();
$client->addScope(Google_Service_Calendar::CALENDAR);
$service = new Google_Service_Calendar($client);
$url = parse_url($_SERVER['REQUEST_URI']); parse_str($url['query'], $params);
$code = $params['code'];
//To exchange an authorization code for an access token, use the authenticate method:
if (! isset($code)) {
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$token = $client->fetchAccessTokenWithAuthCode($code);
$client->setAccessToken($token);
$client->authenticate($code);
$_SESSION['access_token'] = $client->getAccessToken();
$redirect_uri = site_url('calendar/gcalendar');
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
}
function gcalendar_events() {
$client = $this->getClient();
$client->addScope(Google_Service_Calendar::CALENDAR);
// $client->setRedirectUri(site_url('calendar/gcalendar'));
$client->setAccessType('offline'); //need calendar events to appear even if not logged in to google
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$access_token = $_SESSION['access_token'];
$service = new Google_Service_Calendar($client);
$id = 'primary';
$calendar = new Google_Service_Calendar_Calendar();
$calendar = $service->calendars->get('primary');
$event = new Google_Service_Calendar_Event();
$events = $service->events->listEvents($id);
foreach ($events->getItems() as $event) {
$startTime = strtotime($event->getStart()->dateTime) ;
$endTime = strtotime($event->getEnd()->dateTime);
$start = date('Y-m-d H:i:s', $startTime);
$end = date('Y-m-d H:i:s', $endTime);
$eventsArr[] = array(
'title' => $event->getSummary(),
'start'=> $start,
'end' => $end,
);
}
// Return a single `events` with all the `$eventsArr`
echo json_encode($eventsArr);
}
}
Is the problem in my session ending? or does the access token expire and I need a refresh token? where do I set the refresh token cause I tried putting it in more that one place and I get an error message that refresh token has to be set as part off setAccessToken. I put it all over and still got error messages.
Here is the code I used
if ($client->isAccessTokenExpired()) {
$refresh_token = $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$client->setAccessToken($refresh_token);
$_SESSION['access_token'] = $refresh_token;
$this->load->helper('file');
write_file('application/client_secrets.json', json_encode($client->getAccessToken()));
} else {
$access_token = $_SESSION['access_token'];
}
I just noticed that it once used to say 'grant offline access' in my authorization but now it no longer mentions that while google documentation says
"After a user grants offline access to the requested scopes, you can continue to use the API client to access Google APIs on the user's behalf when the user is offline. The client object will refresh the access token as needed."
During your first authorization with Google, you will receive a token that will expire in 3600 seconds or one hour. So you need to use refresh token to get a new working token.
It is something like this SO question.
$token = $client->getAccessToken();
$authObj = json_decode($token);
if(isset($authObj->refresh_token)) {
save_refresh_token($authObj->refresh_token);
}
Make sure you save this refresh_token.
You can update it with:
$client->refreshToken($your_saved_refresh_token);
And then set your new access token to the session:
$_SESSION['access_token'] = $client->getAccessToken();
I also suggest you to visit this quickstart of Google Calendar for PHP.
For more information, check this related SO question.
How to refresh token with Google API client?
i want to make WordPress plugin or if already exists please tell me
i want if user post 15 post on my WordPress site publish post to his Facebook wall with images and level and more than posts with another level
login to WordPress throw Facebook
save the access token to use it to publish offline
on save posts if equal certain number publish the post to user
time line
i do first step and this images the result
but no thing published to timeline and the code is
<?php
session_start();
require_once 'facebook/autoload.php';
$fb = new Facebook\Facebook([
'app_id' => 'xxxxxx',
'app_secret' => 'xxxxxxxxx',
'default_graph_version' => 'v2.5',
]);
$helper = $fb->getCanvasHelper();
$permissions = ['email', 'publish_actions']; // optional
try {
if (isset($_SESSION['facebook_access_token'])) {
$accessToken = $_SESSION['facebook_access_token'];
} else {
$accessToken = $helper->getAccessToken();
}
} catch(Facebook\Exceptions\FacebookResponseException $e) {
// When Graph returns an error
echo 'Graph returned an error: ' . $e->getMessage();
exit;
} catch(Facebook\Exceptions\FacebookSDKException $e) {
// When validation fails or other local issues
echo 'Facebook SDK returned an error: ' . $e->getMessage();
exit;
}
if (isset($accessToken)) {
if (isset($_SESSION['facebook_access_token'])) {
$fb->setDefaultAccessToken($_SESSION['facebook_access_token']);
} else {
$_SESSION['facebook_access_token'] = (string) $accessToken;
// OAuth 2.0 client handler
$oAuth2Client = $fb->getOAuth2Client();
// Exchanges a short-lived access token for a long-lived one
$longLivedAccessToken = $oAuth2Client->getLongLivedAccessToken($_SESSION['facebook_access_token']);
$_SESSION['facebook_access_token'] = (string) $longLivedAccessToken;
$fb->setDefaultAccessToken($_SESSION['facebook_access_token']);
}
// validating the access token
try {
$request = $fb->get('/me');
} catch(Facebook\Exceptions\FacebookResponseException $e) {
// When Graph returns an error
if ($e->getCode() == 190) {
unset($_SESSION['facebook_access_token']);
$helper = $fb->getRedirectLoginHelper();
$loginUrl = $helper->getLoginUrl('https://apps.facebook.com/xxxxxxx/', $permissions);
echo "<script>window.top.location.href='".$loginUrl."'</script>";
exit;
}
} catch(Facebook\Exceptions\FacebookSDKException $e) {
// When validation fails or other local issues
echo 'Facebook SDK returned an error: ' . $e->getMessage();
exit;
}
// posting on user timeline using publish_actins permission
try {
// message must come from the user-end
$data = ['message' => 'testing...'];
$request = $fb->post('/me/feed', $data);
$response = $request->getGraphEdge()->asArray;
} catch(Facebook\Exceptions\FacebookResponseException $e) {
// When Graph returns an error
echo 'Graph returned an error: ' . $e->getMessage();
exit;
} catch(Facebook\Exceptions\FacebookSDKException $e) {
// When validation fails or other local issues
echo 'Facebook SDK returned an error: ' . $e->getMessage();
exit;
}
echo $response['id'];
// Now you can redirect to another page and use the
// access token from $_SESSION['facebook_access_token']
} else {
$helper = $fb->getRedirectLoginHelper();
//$loginUrl = $helper->getLoginUrl('https://apps.facebook.com/xxxxxxxx/', $permissions);
$loginUrl = $helper->getLoginUrl("http://www.xxxxxx.com/facebook/index.php",$permissions);
echo "<script>window.top.location.href='".$loginUrl."'</script>";
}
help :)
i used this tutorial to solve some steps
https://www.youtube.com/watch?v=XYawdJh_mNQ
So I define all parameters on the top of my page:
<?php
session_start();
$client = new Google_Client();
$client->setAuthConfigFile('client_secrets.json');
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$client->getAccessToken();
} else {
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
//get the access token
$myToken = json_decode($client->getAccessToken());
if ($client->getAuth()->isAccessTokenExpired()) {
$token = $myToken->refresh_token;
echo 'token expired';
} else {
$token = $myToken->access_token;
echo 'token not yet expired';
}
?>
Down at the bottom I got java script:
<script>
gapi.analytics.ready(function() {
var CLIENT_ID = 'my-client-id-goes-here';
gapi.analytics.auth.authorize({
'serverAuth': {
'access_token': '<?php echo $token; ?>'
}
});
This works however until access token expires, which is in 60 minutes. After that reports are not showing anymore. What did I do wrong and why it doesn't use a refresh token?
This is actually the expected behavior (unfortunately). The Embed API does not take a refresh token because generally you never want to expose those tokens publicly (which you would be doing if they were in your HTML source).
If you wanted to work around this limitation, you could set a timer on the page that updated the access token every 50 minutes or so (they expire after 60, as you pointed out).
If you had an endpoint on your server that returned a new access token, you could do something like this:
setInterval(function() {
makeRequestToGetNewAccessToken().then(function(access_token) {
gapi.auth.setToken({
access_token: access_token
});
});
}, 1000 * 60 * 50);
Note, the key above is calling setToken with the new access token. That will allow the Embed API to continue to work as normal.