18

I'm trying to use a service account to create entries on a Google calendar. I'm really close on this, but the very last line won't work. I get a 500 Internal Service Error when I let this run. Otherwise, the program runs error free, for whatever that is worth.

The Calendar.php file contents can be found here. The insert() method that I am trying to call begins on line 1455 of that file.

<?php

function calendarize ($title, $desc, $ev_date, $cal_id) {

    session_start();

    /************************************************
    Make an API request authenticated with a service
    account.
    ************************************************/
    set_include_path( '../google-api-php-client/src/');

    require_once 'Google/Client.php';
    require_once 'Google/Service/Calendar.php';

    // (not real keys)
    $client_id = '843319906820-jarm3f5ctbtjj9b7lp5qdcqal54p1he6.apps.googleusercontent.com';
    $service_account_name = '843319906820-jarm3f5ctbtjj7b7lp5qdcqal54p1he6@developer.gserviceaccount.com';
    $key_file_location = '../google-api-php-client/calendar-249226a7a27a.p12';

//    echo pageHeader("Service Account Access");
    if (!strlen($service_account_name) || !strlen($key_file_location))
        echo missingServiceAccountDetailsWarning();

    $client = new Google_Client();
    $client->setApplicationName("xxxx Add Google Calendar Entries");

    if (isset($_SESSION['service_token'])) {
        $client->setAccessToken($_SESSION['service_token']);
    }

    $key = file_get_contents($key_file_location);
    $cred = new Google_Auth_AssertionCredentials(
        $service_account_name, 
        array('https://www.googleapis.com/auth/calendar'), 
        $key
    );
    $client->setAssertionCredentials($cred);
    if($client->getAuth()->isAccessTokenExpired()) {
        $client->getAuth()->refreshTokenWithAssertion($cred);
    }
    $_SESSION['service_token'] = $client->getAccessToken();

    // Prior to this, the code has mostly come from Google's example
    //     google-api-php-client / examples / service-account.php
    // and relates to getting the access tokens. 

    // The rest of this is about setting up the calendar entry.
    //Set the Event data
    $event = new Google_Service_Calendar_Event();
    $event->setSummary($title);
    $event->setDescription($desc);

    $start = new Google_Service_Calendar_EventDateTime();
    $start->setDate($ev_date);
    $event->setStart($start);

    $end = new Google_Service_Calendar_EventDateTime();
    $end->setDate($ev_date);     
    $event->setEnd($end);

    $calendarService = new Google_Service_Calendar($client);
    $calendarList = $calendarService->calendarList;

    $events = $calendarService->events;

    // if I leave this line, my code won't crash (but it won't do anything, either)
    //echo "here"; die(); 

    $events.insert($cal_id, $event, false);
} 

?>
AndroidDev
  • 20,466
  • 42
  • 148
  • 239
  • where are you getting $cal_id? does the service account have access to that calendar? – Linda Lawton - DaImTo Sep 26 '14 at 16:46
  • Yes. That is the calendar id of the specific Google calendar that I am trying to post events to. On that calendar, I have added the $client_id value in 'share with specific people'. – AndroidDev Sep 26 '14 at 16:54

2 Answers2

37

I figured this out. Since I don't see any complete examples of using service accounts with API v3, I'm just going to post my complete solution for reference. There are a few of things that you need to do in addition to implementing the code, however:

1) You need to go to the Google Developer's console and mark your account as a 'service account'. This will differentiate it from a web application. The important difference is that nobody will be prompted to log in to their account before the events are added since the account will belong to your application, not an end user. For more information see this article, starting on page 5.

2) You need to create a public/private key pair. From the developer's console, click on Credentials. Under you service account, click on 'Generate new P12 key'. You'll need to store this somewhere. That file location becomes the $key_file_location variable string in the code below.

3) Also from the developer's console, you need to enable the Calendar API. From your project, on the left margin you'll see APIs. Select that and find the Calendar API. Click it, accept the terms of service, and verify that it is now displayed under Enabled APIs with a status of On

4) In Google Calendar that you want to add events to, under settings, click Calendar Settings then on 'Share this Calendar' at the top. Under 'Share with specific people' in the 'Person' field, paste in the email address from the service account credentials. Change the permission settings to 'Make changes to events'. Don't forget to save the change.

Then, implement this code somewhere.

Comment if something is confusing or omitted. Good luck!

<?php

function calendarize ($title, $desc, $ev_date, $cal_id) {

    session_start();

    /************************************************
    Make an API request authenticated with a service
    account.
    ************************************************/
    set_include_path( '../google-api-php-client/src/');

    require_once 'Google/Client.php';
    require_once 'Google/Service/Calendar.php';

    //obviously, insert your own credentials from the service account in the Google Developer's console
    $client_id = '843319906820-xxxxxxxxxxxxxxxxxxxdcqal54p1he6.apps.googleusercontent.com';
    $service_account_name = '843319906820-xxxxxxxxxxxxxxxxxxxdcqal54p1he6@developer.gserviceaccount.com';
    $key_file_location = '../google-api-php-client/calendar-xxxxxxxxxxxx.p12';

    if (!strlen($service_account_name) || !strlen($key_file_location))
        echo missingServiceAccountDetailsWarning();

    $client = new Google_Client();
    $client->setApplicationName("Whatever the name of your app is");

    if (isset($_SESSION['service_token'])) {
        $client->setAccessToken($_SESSION['service_token']);
    }

    $key = file_get_contents($key_file_location);
    $cred = new Google_Auth_AssertionCredentials(
        $service_account_name, 
        array('https://www.googleapis.com/auth/calendar'), 
        $key
    );
    $client->setAssertionCredentials($cred);
    if($client->getAuth()->isAccessTokenExpired()) {
        $client->getAuth()->refreshTokenWithAssertion($cred);
    }
    $_SESSION['service_token'] = $client->getAccessToken();

    $calendarService = new Google_Service_Calendar($client);
    $calendarList = $calendarService->calendarList;

    //Set the Event data
    $event = new Google_Service_Calendar_Event();
    $event->setSummary($title);
    $event->setDescription($desc);

    $start = new Google_Service_Calendar_EventDateTime();
    $start->setDateTime($ev_date);
    $event->setStart($start);

    $end = new Google_Service_Calendar_EventDateTime();
    $end->setDateTime($ev_date);
    $event->setEnd($end);

    $createdEvent = $calendarService->events->insert($cal_id, $event);

    echo $createdEvent->getId();
} 

?>

Some helpful resources:
Github example for service accounts
Google Developers Console for inserting events in API v3
Using OAuth 2.0 to Access Google APIs

AndroidDev
  • 20,466
  • 42
  • 148
  • 239
  • 2
    It would appear this is for v1 of the google-api-client library. For those reading now, if you're getting a `Fatal error: Class 'Google_Auth_AssertionCredentials' not found` be sure to read https://github.com/google/google-api-php-client/blob/master/UPGRADING.md#google_auth_assertioncredentials-has-been-removed – KFE Dec 14 '16 at 22:41
  • How can i get the calendar ID of service account ? – Hamza Zafeer Oct 06 '21 at 15:04
  • is there any difference if instead of P12 , I go with credentails.json ?? – andilabs Aug 13 '22 at 22:36
  • I followed the steps (with updated code from examples on google.com). And it generates link to the created event for me, but when I follow the link google says this page doesn't exit. Nor I get an event in my calendar. Also if I try to create an event with a conference it returns an error "Invalid conference type value" (all these did work when I used Oauth2 w/o service account) – klm123 Aug 10 '23 at 15:29
8

Here is the Google API Client 2.0 way to do

Follow Alex's Answer Go Google Developer's console to create Service Account, but create credential in JSON

Follow Google API Client Upgrade Guide, install by composer and include in the way of

 require_once 'vendor/autoload.php';

Follow Alex's Answer to go to Google Calendar to share the calendar with Service Account ID

xxxxx@yyyy.iam.gserviceaccount.com 

Take Ref. to below coding and the most important one is the calendar id should be the email of calendar creator, but not primary

<?php


require_once __DIR__.'/vendor/autoload.php';
   session_start();     


$client = new Google_Client();
$application_creds = __DIR__.'/secret.json';  //the Service Account generated cred in JSON
$credentials_file = file_exists($application_creds) ? $application_creds : false;
define("APP_NAME","Google Calendar API PHP");   //whatever
$client->setAuthConfig($credentials_file);
$client->setApplicationName(APP_NAME);
$client->addScope(Google_Service_Calendar::CALENDAR);
$client->addScope(Google_Service_Calendar::CALENDAR_READONLY);

//Setting Complete

//Go Google Calendar to set "Share with ..."  Created in Service Account (xxxxxxx@sustained-vine-198812.iam.gserviceaccount.com)

//Example of Use of API
$service = new Google_Service_Calendar($client);  


$calendarId = 'xxxx@gmail.com';   //NOT primary!! , but the email of calendar creator that you want to view
$optParams = array(
  'maxResults' => 10,
  'orderBy' => 'startTime',
  'singleEvents' => TRUE,
  'timeMin' => date('c'),
);
$results = $service->events->listEvents($calendarId, $optParams);

if (count($results->getItems()) == 0) {
  print "No upcoming events found.\n";
} else {
  echo "Upcoming events:";
  echo "<hr>";
  echo "<table>";
  foreach ($results->getItems() as $event) {
    $start = $event->start->dateTime;
    if (empty($start)) {
      $start = $event->start->date;
    }
    echo "<tr>";
    echo"<td>".$event->getSummary()."</td>";
    echo"<td>".$start."</td>";
    echo "</tr>";
  }
  echo "</table>";
}  
Kalun Lau
  • 81
  • 1
  • 1
  • Thanks, but I'm a little confused. Does this write an entry to a google calendar? I only see it reading events. If it doesn't, I suggest you state this in your answer to clarify for people who read through this with the expectation that it answers the original question. – AndroidDev Apr 24 '21 at 03:57
  • "id should be the email of calendar creator, but not primary" - THIS! it worked with an simple event, but doesn't work when I'm making an event with a conferenceData, result is "Invalid conference type value". – klm123 Aug 10 '23 at 15:32