I'm trying to change the color of a Google calendar event with PHP.
The code I'm using is this:
if(version_compare(PHP_VERSION, '7.2.0', '>=')) {
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
}
require_once __DIR__.'/vendor/autoload.php';
$hostname = 'localhost';
$username = 'root';
$password = '';
$database = 'pacientes';
try {
$conex = new PDO("mysql:host=$hostname;dbname=$database;charset=utf8", $username, $password,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
}
catch(PDOException $e){
echo $e->getMessage();
}
session_start();
$client = new Google_Client();
$client->setAuthConfig('client_secrets.json');
$client->addScope(Google_Service_Calendar::CALENDAR_READONLY);
$client->addScope("https://www.googleapis.com/auth/calendar.events");
$client->addScope("https://www.googleapis.com/auth/calendar");
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$data = date("Y-m-d");
$date = date("Y-m-d", strtotime('+7 days', time()));
$status = "3";
$dados = $conex->prepare('SELECT * FROM pacientes WHERE status = ? LIMIT 1');
$dados->bindParam(1, $status);
$dados->execute();
if ($dados->rowCount() > 0) {
foreach($dados->fetchAll(PDO::FETCH_OBJ) as $key => $linha) {
$optParams = "".$linha->ide."";
$service = new Google_Service_Calendar($client);
$event = $service->events->get('primary', $optParams);
$event->setColorID('4');
$updatedEvent = $service->events->update('primary', $optParams, $event);
// Print the updated date.
echo $updatedEvent->getUpdated();
}
}
} else {
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
But when I run the code, I get the error:
Fatal error: Uncaught Google_Service_Exception: { "error": { "code": 403, "message": "Request had insufficient authentication scopes.", "errors": [ { "message": "Insufficient Permission", "domain": "global", "reason": "insufficientPermissions" } ], "status": "PERMISSION_DENIED" } } in C:\xampp\htdocs\google\calendar\vendor\google\apiclient\src\Google\Http\REST.php:118 Stack trace: #0 C:\xampp\htdocs\google\calendar\vendor\google\apiclient\src\Google\Http\REST.php(94): Google_Http_REST::decodeHttpResponse(Object(GuzzleHttp\Psr7\Response), Object(GuzzleHttp\Psr7\Request), 'Google\Service\...') #1 C:\xampp\htdocs\google\calendar\vendor\google\apiclient\src\Google\Task\Runner.php(181): Google_Http_REST::doExecute(Object(GuzzleHttp\Client), Object(GuzzleHttp\Psr7\Request), 'Google\Service\...') #2 C:\xampp\htdocs\google\calendar\vendor\google\apiclient\src\Google\Http\REST.php(58): Google_Task_Runner->run() #3 C:\xampp\htdocs\google\calendar\vendor\google\apiclie in C:\xampp\htdocs\google\calendar\vendor\google\apiclient\src\Google\Http\REST.php on line 118
I recreated authorizations in Google Cloud Platform, made sure to grant authorization to edit events (/auth/calendar.events), but still the error persists.
Anyone have any suggestions?
Thanks
You appear to be trying to use the Events: update this method requires authorization with one of the following scopes
Now if we check your code we can see the following
$client->addScope(Google_Service_Calendar::CALENDAR_READONLY);
$client->addScope("https://www.googleapis.com/auth/calendar.events");
$client->addScope("https://www.googleapis.com/auth/calendar");
the way you have added the last two as just the pure HTTPS scope name rather then the internal scope names supplied by the php client library. Implies to me that you ran your code once with the CALENDAR_READONLY and it didn't work and you checked the documentation and realized that you needed the higher level scope denoted in the documentation. Which is great and perfect. However when you ran your code again you didn't reset the authorization so its still running with the authorization from your first run with CALENDAR_READONLY
So the solution is to force your application to authorize again. The easiest way to do that is to go into your Google account and reset the third party consent and remove it for your app Manage third-party apps & services with access to your account alternately removing's the cookies in your browser might also work. you need to clear $_SESSION['access_token']. Although you are not using offline access so you could probably just wait for the access token to expire in an hour.
What you want is for the consent screen to popup again and request permission to access your account you should see the two new scopes on the screen now.
Related
I've been trying to set up the sample PHP Quickstart.
When I try running it as is I get the following error :
PHP Fatal error: Uncaught InvalidArgumentException: missing the required redirect URI in /opt/lampp/htdocs/rev_wip/vendor/google/auth/src/OAuth2.php:685
Stack trace:
#0 /opt/lampp/htdocs/rev_wip/vendor/google/apiclient/src/Client.php(406): Google\Auth\OAuth2->buildFullAuthorizationUri(Array)
#1 /opt/lampp/htdocs/rev_wip/quickstart.php(40): Google\Client->createAuthUrl()
#2 /opt/lampp/htdocs/rev_wip/quickstart.php(64): getClient()
#3 {main}
thrown in /opt/lampp/htdocs/rev_wip/vendor/google/auth/src/OAuth2.php on line 685
So I add a URI :
// $redirect_uri = 'http://localhost/rev_wip/' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$redirect_uri = 'http://localhost/rev_wip/' . $_SERVER['PHP_SELF'];
$client->setRedirectUri($redirect_uri);
Now I get (when I run it on the browser) :
Authorization Error
Error 400: redirect_uri_mismatch
You can't sign in to this app because it doesn't comply with Google's OAuth 2.0 policy.
If you're the app developer, register the redirect URI in the Google Cloud Console.
Learn more
The content in this section has been provided by the app developer. This content has not been reviewed or verified by Google.
If you’re the app developer, make sure that these request details comply with Google policies.
redirect_uri: http://localhost/rev_wip/quickstart.php
I am thinking that this example is a Desktop APP and so there should be no Redirect URI. I, however get the common redirect URI error when I run it on the CLI.
How should I go about it?
Thank you all in advance.
Here is the code that I am working with:
<?php
require __DIR__ . '/vendor/autoload.php';
if (php_sapi_name() != 'cli') {
throw new Exception('This application must be run on the command line.');
}
/**
* Returns an authorized API client.
* #return Google_Client the authorized client object
*/
function getClient() {
$client = new Google_Client();
$client->setApplicationName('People API PHP Quickstart');
$client->setScopes(Google_Service_PeopleService::CONTACTS_READONLY);
$client->setAuthConfig(__DIR__ . '/credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = __DIR__ . '/token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath) , true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
}
else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath) , 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_PeopleService($client);
// Print the names for up to 10 connections.
$optParams = array(
'pageSize' => 10,
'personFields' => 'names,emailAddresses',
);
$results = $service
->people_connections
->listPeopleConnections('people/me', $optParams);
if (count($results->getConnections()) == 0) {
print "No connections found.\n";
}
else {
print "People:\n";
foreach ($results->getConnections() as $person) {
if (count($person->getNames()) == 0) {
print "No names found for this connection\n";
}
else {
$names = $person->getNames();
$name = $names[0];
printf("%s\n", $name->getDisplayName());
}
}
}
?>
In the tutorial you're linking to there's a section called "Troubleshooting" under which there's a section about the exact error you're facing:
https://developers.google.com/people/quickstart/php#uncaught_invalidargumentexception_missing_the_required_redirect_uri
Uncaught InvalidArgumentException: missing the required redirect URI
This error occurs when the credentials.json file used contains a client ID of the wrong type.
This code requires an OAuth client ID of type Other, which will be
created for you when using the button in Step 1. If creating your own
client ID please ensure you select the correct type.
This suggests you didn't complete step 1 properly or that you have supplied your own client ID of the wrong type.
In a form which takes input and updates the value in a spreadsheet. It was working fine before but suddenly stopped working with this error message:
Fatal error: Uncaught exception 'Google_Service_Exception' with message '{ "error": { "code": 403, "message": "The caller does not have permission", "errors": [ { "message": "The caller does not have permission", "domain": "global", "reason": "forbidden" } ], "status": "PERMISSION_DENIED" } } ' in /google-api-php-client-2.2.2/src/Google/Http/REST.php:118
Stack trace:
#0 /google-api-php-client-2.2.2/src/Google/Http/REST.php(94): Google_Http_REST::decodeHttpResponse(Object(GuzzleHttp\Psr7\Response), Object(GuzzleHttp\Psr7\Request), 'Google_Service_...')
#1 [internal function]: Google_Http_REST::doExecute(Object(GuzzleHttp\Client), Object(GuzzleHttp\Psr7\Request), 'Google_Service_...')
#2 /google-api-php-client-2.2.2/src/Google/Task/Runner.php(176): call_user_func_array(Array, Array)
#3 /google-api-php-client-2.2.2/src/Google/Http/REST.php(58): Google_Task_Runner->run()
#4 /html/form in /google-api-php-client-2.2.2/src/Google/Http/REST.php on line 118
According to other questions and answers it is because of authentication problem, but the form was working for 5 years with the same authentication so it is confusing. Is there any other reason for which the form is not updating?
here is the code included
<?php
ini_set("display_errors", 1);
ini_set("display_startup_errors", 1);
error_reporting(E_ALL);
date_default_timezone_set("US/Central");
// Autoload Composer.
if (file_exists(__DIR__ . "/google-api-php-client-2.2.2/vendor/autoload.php")) {
require_once __DIR__ . "/google-api-php-client-2.2.2/vendor/autoload.php";
$spreadsheetId = "********"; // TODO: Update placeholder value.
// The A1 notation of a range to search for a logical table of data.
// Values will be appended after the last row of the table.
$range = "A2"; // TODO: Update placeholder value.
// TODO: Assign values to desired properties of `requestBody`:
$values = [
[
date("Y-m-d H:i:s"),
$_POST["prop_type"],
$_POST["pstreet"],
$_POST["pcity"],
$_POST["pzip"],
],
];
$service_account_file = "service-account.json";
$client = new Google_Client();
$service = new Google_Service_Sheets($client);
if ($client) {
$client->setApplicationName("Google Sheet Update");
$client->setAuthConfig($service_account_file);
$client->setScopes(Google_Service_Sheets::SPREADSHEETS);
$client->setAccessType("online");
$redirect_uri =
"http://" . $_SERVER["HTTP_HOST"] . $_SERVER["PHP_SELF"];
$client->setRedirectUri($redirect_uri);
$guzzle = new GuzzleHttp\Client([
"verify" => false,
]);
$client->setHttpClient($guzzle);
$requestBody = new Google_Service_Sheets_ValueRange([
"values" => $values,
]);
$params = [
"valueInputOption" => "RAW",
];
$response = $service->spreadsheets_values->append(
$spreadsheetId,
$range,
$requestBody,
$params
);
//echo '<pre>', var_export($response, true), '</pre>', "\n";
} else {
echo "Not Valid Client";
echo "<pre>CLIENT", var_dump($client), "</pre>", "\n";
}
} else {
echo "Client File do not exist";
}
?>
The caller does not have permission
means exactly that. Which ever user you used to authorize this code does not have permission to access that sheet. Authorize your application with a user that has access or grant that user access.
Service accounts need to be preauthorized. The most common way to do that is to take the service account client id and share the file with it though the google drive web application. If someone removed the service accounts access to the file. The service account will no longer have access.
I would double check that it still has access.
I am creating a web site to interact with Google Calendars and watching resources and I want to stop them, but I can't seem to do that, so Google sends the headers "X-Goog-Channel-Id" and "X-Goog-Resource-Id" with the webhook request which from the documentation seems like that's all that's needed to send back to stop them, but I just keep getting a:
Google\Service\Exception: {
"error": {
"errors": [
{
"domain": "global",
"reason": "notFound",
"message": "Channel '0PAA4Z9RXJYMA7YMAV6O' not found for project '309331158475'"
}
],
"code": 404,
"message": "Channel '0PAA4Z9RXJYMA7YMAV6O' not found for project '309331158475'"
}
}
But they should be found as that's what Google has just sent in the header of the webhook. What am I doing wrong?
$headers = getallheaders();
try{
$client = new Google_Client();
$client->setAccessToken(get_google_accesstoken());
$service = new Google_Service_Calendar($client);
$channel = new Google_Service_Calendar_Channel($service);
$channel->setId($headers['X-Goog-Channel-Id']);
$channel->setResourceId($headers['X-Goog-Resource-Id']);
$service->channels->stop($channel);
}catch(Exception $e){
echo $e->getMessage();
}
So the steps I have currently are registering the watch event for the calendar, all good here. Then when the calendar changes Google loads the URL /webhook/google/ on my site and just for concept on that page I have the code above to stop the webhook from happening again, but it shows the error.
I'm generating the watch event with the code below if that helps
$expire = time()+86400;
try {
$client = new Google_Client();
$client->setAccessToken(get_google_accesstoken());
$service = new Google_Service_Calendar($client);
$channel = new Google_Service_Calendar_Channel($client);
$channel->setId(generaterandomstring(20));
$optParams = array('ttl' => $expire);
$channel->setParams($optParams);
$channel->setType('web_hook');
$channel->setAddress($site_url.'/webhook/google/');
$watchEvent = $service->events->watch('email#mysite.com', $channel);
}catch(Exception $e) {
}
I'd guess it's because the channel has already expired.
The $expire = time()+86400 line makes it seem like you're making it expire in 86.4 seconds. Could it be that you're trying to stop the channel watch more than 86 seconds after it was created?
I'm having issues accessing Google Sheets through PHP in WordPress (WP). I've accessed the same sheet before in a local python program and I recall that once the code first ran the google authorization window popped up asking for approval. This WP shortcode isn't getting to that point and instead outputs an error message to the WP page stating a valid API Key is missing. What am I missing here?
Shortcode:
require_once __DIR__ . '/vendor/autoload.php';
function excel_tester_function() {
$client = new Google_Client();
$client->setApplicationName('Google Sheets and PHP');
$client->setAuthConfig(__DIR__ . '/credentials.json');
$client->setScopes(Google_Service_Sheets::SPREADSHEETS);
$client->setAccessType('online');
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$client->setRedirectUri($redirect_uri);
$service = new Google_Service_Sheets($client);
$spreadsheetId = '{SHEET_ID}';
$get_range = '{RANGE}';
$response = $service->spreadsheets_values->get($spreadsheetId, $get_range);
$values = $response->getValues();
foreach ($val as $values) {
print($val);
}
}
add_shortcode('excel-tester', 'excel_tester_function');
Error:
Fatal error: Uncaught Google_Service_Exception: { "error": { "code": 403, "message": "The request is missing a valid API key.", "errors": [ { "message": "The request is missing a valid API key.", "domain": "global", "reason": "forbidden" } ], "status": "PERMISSION_DENIED" } } in...
Please advise. Let me know if there is any info I can provide that could help solve this. Thank you.
I'm newbie on this, so I hope you could help me, I'm checking the Google Drive API docs because I want to create a folder in Google Drive using PHP, I'm using the code example but I notice that I must define this "$driveService" variable first.
I must say I made the previous steps before (Create a project, install via composer, generate my client_secret.json file, etc).
So I tried to use the example writing this code:
<?php
require_once __DIR__.'/vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfigFile('64*****-client_secret.json');
$client->setRedirectUri('https://www.mywebsite.com/drive/oauth2callback.php');
$client->addScope(Google_Service_Drive::DRIVE);
$driveService = new Google_Service_Drive($client);
$fileMetadata = new Google_Service_Drive_DriveFile(array(
'name' => 'TEST',
'mimeType' => 'application/vnd.google-apps.folder'));
$file = $driveService->files->create($fileMetadata, array('fields' => 'id'));
printf("Folder ID: %s\n", $file->id);
?>
But when I execute the page it shows nothing in the browser, it shows this in terminal:
PHP Fatal error: Uncaught exception 'Google_Service_Exception' with
message '{
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Login Required",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Login Required"
}
}
' in /var/www/mywebsite/public_html/drive/vendor/google/apiclient/src/Google/Http/REST.php:118
Stack trace: #0 /var/www/mywebsite/public_html/drive/vendor/google/apiclient/src/Google/Http/REST.php(94): Google_Http_REST::decodeHttpResponse(Object(GuzzleHttp\Psr7\Response), Object(GuzzleHttp\Psr7\Request), 'Google_Service_...') #1 [internal function]: Google_Http_REST::doExecute(Object(GuzzleHttp\Client), Object(GuzzleHttp\Psr7\Request), 'Google_Service_...') #2 /var/www/mywebsite/public_html/drive/vendor/google/apiclient/src/Google/Task/Runner.php(176): call_user_func_array(Array, Array) #3 /var/www/mywebsite/public_html/drive/vendor/google/apiclient/src/Google/Http/REST.php(58): Google_Task_Runner->run() in /var/www/mywebsite/public_html/drive/vendor/google/apiclient/src/Google/Http/REST.php on line 118
I also added these lines:
$client = new Google_Client();
$client->setAuthConfigFile('64*****-client_secret.json');
$client->setRedirectUri('https://www.mywebsite.com/drive/oauth2callback.php');
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->addScope(Google_Service_Drive::DRIVE);
But it's still the same, I can't create the folder and get the folder ID. How can I fix it?
I'll appreciate your answer.
You need to get the access token first; the user needs to grant you access to his or her drive file through the user consent screen.
Yo can try this;
$client = new Google_Client();
$client->setAuthConfigFile('64*****-client_secret.json');
$client->setRedirectUri('https://www.mywebsite.com/drive/oauth2callback.php');
$client->addScope(Google_Service_Drive::DRIVE);
/** checks if the access token has previously been obtained and sets it **/
if (isset($_SESSION['access_token']) && $_SESSION['access_token']){
$client->setAccessToken($_SESSION['access_token']);
//what ever code you want to run
else{
//redirects the user to authenticate
$redirect_uri = 'https://www.mywebsite.com/drive/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
}
The $client->setAuthConfig('credentials.json'); you added is a repetition and not necessary. Also remember to call session start at the beginning of the code like this:session_start();
For detail check out: https://developers.google.com/identity/protocols/OAuth2WebServer