How to Interact with Google Calendar?

Solved
emrh Posted messages 439 Status Member -  
emrh Posted messages 439 Status Member -

Hello everyone,

I would like to add the ability to interact with Google Calendar to my web application.
I looked at a couple of tutorials and then the Google documentation. I contacted Ionos, my hosting provider, to tell them that I wanted to access my server via SSH to install Composer, but since I am on shared hosting, this will not be possible. However, they explained to me that I could develop locally and then transfer my files afterwards... which I have started doing!

I followed the instructions given here: https://developers.google.com/calendar/api/quickstart/php

I run php quickstart.php and copy the address obtained in my Chrome browser, and then Google informs me that: "Error 403: access_denied The developer hasn't given you access to this app. It’s currently being tested and it hasn’t been verified by Google".

I am almost sure that the problem comes from the "Authorized redirect URI" entered in the Google Calendar API... But I don’t know what else to put other than the address of my website!
(Problem discussed here as well:
https://stackoverflow.com/questions/34707227/google-client-api-missing-require-parameter-redirect-uri
but I don’t see how to adapt the proposed solution to my case).

Do you have any ideas to help me unlock this situation?

Thank you in advance for your help!

Linux / Chrome 103.0.0.0

10 answers

  1. jordane45 Posted messages 30426 Registration date   Status Moderator Last intervention   4 830
     

    Hello

    When you launched your script for the first time, it must have given you a code to write in the console..

    If you did it locally, you need to delete the token and regenerate it from your website.

    Warning.. using this API from a shared server is not guaranteed... Sometimes, the server's IP is blacklisted by Google... And it's complicated to get it unblocked.


    .
    Sincerely,
    Jordane

    0
  2. emrh Posted messages 439 Status Member 20
     

    Thank you Jordan for this warning... Are you advising me to give up?

    Actually, I would like "simply" for my rental contract registration form to add an entry to Google Calendar with the start and end dates...

    When I ran the command php quickstart.php from my terminal, I got a link that I pasted into Chrome, which then asked me to select a Google account. I chose the one for which I needed access to the calendar.
    And that’s where I got the info: The developer hasn’t given you access to this app. It’s currently being tested and it hasn’t been verified by Google.
    I modified the URI of my API to http://127.0.0.1/monsite/api/quickstart.php, but I have the same error!

    I don't know how to delete the token you mentioned because I don't know where it is!
    I can't generate any command line from my website because I have no SSH access, and if I run the quickstart.php file from my browser I get the error message:
    Fatal error: Uncaught Exception: This application must be run on the command line

    0
  3. emrh Posted messages 439 Status Member 20
     

    After watching 2 or 3 YouTube tutorials (not much found on the subject) in very heavy Indian-accented English, I still managed to make progress on my project to interact with Google Calendar. I created the proper access to my account in my console.cloud.google.com, this time with functional permissions.

    I can now write in my calendar via this test page https://developers.google.com/calendar/api/v3/reference/events/insert?hl=fr&apix=true#try-it...

    The problem is that those tutorials are not clear about the procedure for writing from my web pages in PHP to my calendar...

    I have this piece of code:

    <?php $event = new Google_Service_Calendar_Event(array( 'summary' => 'TEST', 'location' => '800 Howard St., San Francisco, CA 94103', 'description' => 'A chance to hear more about Google\'s developer products.', 'start' => array( 'date' => '2022-01-01', 'timeZone' => 'America/Los_Angeles', ), 'end' => array( 'date' => '2022-01-05', 'timeZone' => 'America/Los_Angeles', ), 'recurrence' => array( 'RRULE:FREQ=DAILY;COUNT=2' ), 'attendees' => array(), 'reminders' => array( 'useDefault' => FALSE, 'overrides' => array( array('method' => 'email', 'minutes' => 24 * 60), array('method' => 'popup', 'minutes' => 10), ), ), )); $calendarId = 'monagenda.rhnorhnerhencpacnn.calendar.google.com'; $event = $service->events->insert($calendarId, $event); printf('Event created: %s\n', $event->htmlLink); ?>

    Which gives me this error:
    Fatal error: Uncaught Error: Class 'Google_Service_Calendar_Event' not found in /var/www/html/gite/gestion/api/event.php

    Could you please advise me?

    0
    1. emrh Posted messages 439 Status Member 20
       

      I also found this piece of code, which somewhat resembles the previous one, but is more complete because it contains access to my credentials.json:

      // Exit if accessed directly defined('ABSPATH') || exit; ini_set('memory_limit', '-1'); ini_set('max_execution_time', '-1'); ini_set('display_errors', 1); /** * update google code */ add_action('wp_footer', 'create_google_calendar_events'); function create_google_calendar_events() { $credentials = __DIR__ . '/credentials.json'; require __DIR__ . '/vendor/autoload.php'; $client = new Google_Client(); $client->setApplicationName('testoo'); $client->getScope(array(Google_Service_Calendar::CALENDAR)); $client->setAuthConfig($credentials); $client->setAccessType('offline'); $client->getAccessToken(); $client->getRefreshToken(); $service = new Google_Service_Calendar($client); $event = new Google_Service_Calendar_event(array( 'summary' => 'testing', 'location' => '800 Howard', 'description' => 'Bla Bla Bla', 'start' => array( 'date' => '2022-01-02', 'timeZone' => 'America/Los_Angeles', ), 'end' => array( 'date' => '2022-01-05', 'timeZone' => 'America/Los_Angeles', ), 'recurrence' => array( 'RRULE:FREQ=DAILY;COUNT=2' ), 'attendees' => array(), 'reminders' => array(), 'useDefault' => FALSE, 'overrides' => array( array('method' => 'email', 'minutes' => 24 * 60), array('method' => 'popup', 'minutes' => 10), ), ), );//)); $calendarId = 'eozirenonrezntornnr@group.calendar.google.com'; $event = $service->events->insert($calendarId, $event); print_r($event->htmlLink); } ?>

      Unfortunately, I can’t retrieve the first 12 lines, so even by neutralizing the function call, the page remains desperately empty as does the calendar!

      0
  4. emrh Posted messages 439 Status Member 20
     

    Finally, the quickstart.php file from Google gives rather good results (no errors in any case), but I still don't see how to write in my calendar:

    <?php require __DIR__ . '/vendor/autoload.php'; /*if (php_sapi_name() != 'cli') { throw new Exception('This application must be run on the command line.'); }*/ use Google\Client; use Google\Service\Calendar; /** * Returns an authorized API client. * @return Client the authorized client object */ function getClient() { $client = new Client(); $client->setApplicationName('Google Calendar API PHP Quickstart'); $client->setScopes(Google_Service_Calendar::CALENDAR); //<= ACCORDING TO DOC developers.google.com $client->setAuthConfig('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 = '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 Calendar($client); // Print the next 10 events on the user's calendar. try{ $calendarId = 'xxxxxxxxxxxxxxxxxxx@group.calendar.google.com'; $optParams = array( 'maxResults' => 10, 'orderBy' => 'startTime', 'singleEvents' => true, 'timeMin' => date('c'), ); $results = $service->events->listEvents($calendarId, $optParams); $events = $results->getItems(); if (empty($events)) { print "No upcoming events found.\n"; } else { print "Upcoming events:\n"; foreach ($events as $event) { $start = $event->start->dateTime; if (empty($start)) { $start = $event->start->date; } printf("%s (%s)\n", $event->getSummary(), $start); } } } catch(Exception $e) { // TODO(developer) - handle error appropriately echo 'Message: ' .$e->getMessage(); } ?>

    RESULT: 

    No upcoming events found.

    0
  5. emrh Posted messages 439 Status Member 20
     

    1. Deleting the initial project
    2. Creating a new project "Agenda Management" on console.cloud.google.com
    3. Activating the Google Calendar API library
    4. Creating an External User Type for OAuth 2.0
    5. Creating a Service Account Identifier
    6. Adding a JSON type key to this service account (saved as credentials.json at the same level as quickstart.php)
    7. Modifying quickstart.php to add an event:

    <?php require __DIR__ . '/vendor/autoload.php'; /* if (php_sapi_name() != 'cli') { throw new Exception('This application must be run on the command line.'); } */ use Google\Client; use Google\Service\Calendar; /** * Returns an authorized API client. * @return Client the authorized client object */ function getClient() { $client = new Client(); $client->setApplicationName('Google Calendar API PHP Quickstart'); $client->setScopes('https://www.googleapis.com/auth/calendar');//.events.readonly'); $client->setAuthConfig('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 = '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 Calendar($client); // Print the next 10 events on the user's calendar. /*try{ $calendarId = 'primary'; $optParams = array( 'maxResults' => 10, 'orderBy' => 'startTime', 'singleEvents' => true, 'timeMin' => date('c'), ); $results = $service->events->listEvents($calendarId, $optParams); $events = $results->getItems(); if (empty($events)) { print "No upcoming events found.\n"; } else { print "Upcoming events:\n"; foreach ($events as $event) { $start = $event->start->dateTime; if (empty($start)) { $start = $event->start->date; } printf("%s (%s)\n", $event->getSummary(), $start); } } } catch(Exception $e) { // TODO(developer) - handle error appropriately echo 'Message: ' .$e->getMessage(); }*/ // ADD TO AGENDA: try{ $event = new Google_Service_Calendar_Event(array( 'summary' => 'TEST ADDITION', 'location' => '800 Howard St., San Francisco, CA 94103', 'description' => 'A chance to hear more about Google\'s developer products.', 'start' => array( 'date' => '2023-01-01', 'timeZone' => 'America/Los_Angeles', ), 'end' => array( 'date' => '2023-01-04', 'timeZone' => 'America/Los_Angeles', ), 'recurrence' => array( 'RRULE:FREQ=DAILY;COUNT=2' ), 'attendees' => array(), 'reminders' => array( 'useDefault' => FALSE, 'overrides' => array( array('method' => 'email', 'minutes' => 24 * 60), array('method' => 'popup', 'minutes' => 10), ), ), )); $calendarId = 'primary'; $event = $service->events->insert($calendarId, $event); printf('Event created: %s\n', $event->htmlLink); } catch(Exception $e) { // TODO(developer) - handle error appropriately echo 'Message: ' .$e->getMessage(); } ?>

    MESSAGE DISPLAYED WHEN EXECUTING QUICKSTART.PHP:
    Event created: https://www.google.com/calendar/event?eid=RRRWNwdm5vYjBpcnRRRRRRcjFqb2dfMjAyMjARRRRRdGlvbi1hZ2VuZGRRRRR2VuZGEtMzU3NDA5LmlhbS5RRRRVydmljZWFjRRRRRbnQuY29t%5Cn

    Better but still nothing in the calendar!

    The same quickstart.php code for reading the calendar, that is uncommenting the part // Print the next 10 events on the user's calendar. gives me:
    Upcoming events: TEST ADDITION (2023-01-01) TEST ADDITION (2023-01-02)

    0
    1. yg_be Posted messages 23437 Registration date   Status Contributor Last intervention   1 588
       

      Hello,

      If I understand correctly, you are writing "still nothing in the agenda," while your code "in agenda reading" proves that the events are in the agenda.

      Did you explain to us what happens when you use the link to the created event?

      Wouldn't it be better not to always use the same title for these events, for example by adding the execution time of the script in the title?

      0
    2. emrh Posted messages 439 Status Member 20 > yg_be Posted messages 23437 Registration date   Status Contributor Last intervention  
       

      Hello yg_be,

      You understood well, "Still nothing in the agenda" means when I use Google Chrome to go to my agenda, if I go to JANUARY 2023, it's all empty!
      However, if I disable the part of the code regarding data insertion to enable the reading part, and I refresh quickstart.php, it properly finds my addition (see Upcoming events...).

      This is what happens when I use the link to the created event:
      My browser opens Google Agenda in July 2022

      Always use the same title for these events?
      I regularly change my titles, but it doesn't change anything

      0
    3. emrh Posted messages 439 Status Member 20 > emrh Posted messages 439 Status Member
       

      Événements à venir : TEST ADDITION (2023-01-01) TEST ADDITION (2023-01-01) ETIENNE (2023-01-01T16:00:00Z) ETIENNE (2023-01-01T16:00:00Z) TEST ADDITION (2023-01-02) TEST ADDITION (2023-01-02) ETIENNE (2023-01-02T16:00:00Z) ETIENNE (2023-01-02T16:00:00Z)

      0
    4. emrh Posted messages 439 Status Member 20 > emrh Posted messages 439 Status Member
       

      If I change the $calendarId = 'primary'; to the ID of my calendar:
      Message: { "error": { "errors": [ { "domain": "global", "reason": "notFound", "message": "Not Found" } ], "code": 404, "message": "Not Found" } }

      (for both insertion and reading)

      0
    5. yg_be Posted messages 23437 Registration date   Status Contributor Last intervention   1 588 > emrh Posted messages 439 Status Member
       

      I was suggesting changing the title to confirm that the reader program correctly finds the latest created events.

      In the browser, do you not see any other calendars for the same account in Google Calendar?

      I would try to create a new calendar (for the same account) in the browser via Google Calendar, and then use its name as $calendarId.

      0
  6. emrh Posted messages 439 Status Member 20
     

    The Google account uses several calendars, each employee has their own (5) + the one I created MANAGEMENT.

    They are all checked as "visible".

    All attempts to replace $calendarId with anything other than 'primary' result in message #10.

    0
  7. emrh Posted messages 439 Status Member 20
     

    Just a heads up, I'm running my tests from my laptop and I think I read that it should be in https...
    So, I transferred my 17,000 files (?!?!?) from Google, Composer, Paragonie, ... to my Ionos server which is in https and now I have this error when inserting into the calendar:

    Message: { "error": { "code": 403, "message": "Request had insufficient authentication scopes.", "errors": [ { "message": "Insufficient Permission", "domain": "global", "reason": "insufficientPermissions" } ], "status": "PERMISSION_DENIED", "details": [ { "@type": "type.googleapis.com/google.rpc.ErrorInfo", "reason": "ACCESS_TOKEN_SCOPE_INSUFFICIENT", "domain": "googleapis.com", "metadata": { "service": "calendar-json.googleapis.com", "method": "calendar.v3.Events.Insert" } } ] } }

    0
  8. emrh Posted messages 439 Status Member 20
     

    I recreated a service account, generated a new key and I no longer have the 403 error!
    I hadn't noticed (as it was very discreet) that the link after insertion displayed:
    Unable to find the requested event

    I also tried insertions without the Timezone America/Los_Angeles and the DateTime,
    I have this in reading:
    Upcoming events: ETIENNE (2023-01-01) ETIENNE (2023-01-01T16:00:00Z) ETIENNE (2023-01-02) ETIENNE (2023-01-02T16:00:00Z)

    0
  9. emrh Posted messages 439 Status Member 20
     

    Big Big breakthrough:

    When I created a service account in console.cloud.google.com, then for this account I created a key, I obtained a JSON file + an email address of the type nomdemonprojet@nomdemonprojet-785123.iam.gserviceaccount.com

    1. I went to my Google Calendar, on the GESTION calendar created to receive events from my PHP code to add this email address in "share with specific people".

    2. I retrieved the ID of this GESTION calendar to assign it to the variable $calendarId.

    I relaunched my quickstart.php page and TAAAADAAAAA:

    <?php require __DIR__ . '/vendor/autoload.php'; /* if (php_sapi_name() != 'cli') { throw new Exception('This application must be run on the command line.'); } */ use Google\Client; use Google\Service\Calendar; /** * Returns an authorized API client. * @return Client the authorized client object */ function getClient() { $client = new Client(); $client->setApplicationName('Google Calendar API PHP Quickstart'); $client->setScopes('https://www.googleapis.com/auth/calendar');//.events.readonly');<= MODIFY HERE $client->setAuthConfig('credentials.json');//<= The authentication file $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 = '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 Calendar($client); // READ FROM CALENDAR /*try{ $calendarId = 'primary'; $optParams = array( 'maxResults' => 10, 'orderBy' => 'startTime', 'singleEvents' => true, 'timeMin' => date('c'), ); $results = $service->events->listEvents($calendarId, $optParams); $events = $results->getItems(); if (empty($events)) { print "No upcoming events found.\n"; } else { print "Upcoming events:\n"; foreach ($events as $event) { $start = $event->start->dateTime; if (empty($start)) { $start = $event->start->date; } printf("%s (%s)\n", $event->getSummary(), $start); } } } catch(Exception $e) { // TODO(developer) - handle error appropriately echo 'Message: ' .$e->getMessage(); }*/ // ADD TO CALENDAR: try{ $event = new Google_Service_Calendar_Event(array( 'summary' => 'VIVA', 'location' => '800 Howard St., San Francisco, CA 94103', 'description' => 'A chance to hear more about Google\'s developer products.', 'start' => array( 'date' => '2023-01-30', ), 'end' => array( 'date' => '2023-02-01', ), 'recurrence' => array(), 'attendees' => array(), 'reminders' => array(), )); $calendarId = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX@group.calendar.google.com'; $event = $service->events->insert($calendarId, $event); printf('Event created: %s\n', $event->htmlLink); } catch(Exception $e) { // TODO(developer) - handle error appropriately echo 'Message: ' .$e->getMessage(); } ?>
    0
    1. emrh Posted messages 439 Status Member 20
       

      I said big progress, but not everything is settled, because if we look at the code we see:

      Start date: 2023-01-30
      End date: 2023-02-01

      And if I look at my calendar, I see that I was shorted by 1 day!

      0