6.2 Google Calendar API

GoogleCalendarAPI

Google Calendar

1. 介紹

Google日曆(Google Calendar),原始代碼名稱「CL2」,是一款由Google提供的免費連絡人管理、時間管理的網頁應用程式。Google日曆允許使用者使用網路 日曆同步他們的Gmail聯絡人。Google 日曆於2006年4月13日開放使用,於2009年7月結束beta測試。 為了使用此軟體,使用者不需擁有Gmail帳戶,而是需要免費的Google帳戶。 ─ 來自於 Wikipedia

2. 特性

  • AJAX介面
  • 資料線上儲存
  • 使用者可建立多個日曆
  • 多種檢視:日檢視,周檢視,月檢視,任務列表檢視,自訂檢視
  • 共享日曆給他人
  • 郵件/簡訊提醒
  • 可以和微軟的Outlook或Mozilla的Sunbird等第三方日曆軟體進行資料同步.

─ 來自Wikipedia

Google Calendar API

1. 介紹

開發者頁面:http://code.google.com/apis/calendar

「Google 日曆」可讓用戶端應用程式以 Google Data API 資訊提供的形式,檢視並更新日曆活動。用戶端應用程式可以使用 Google Calendar Data API,來建立新活動、編輯或刪除現有活動以及查詢符合特定條件的活動。

可以使用此 API 進行下列操作:建立網路前端,讓使用者在您的網站內檢視自己的「Google 日曆」資訊;透過程式方式將近期活動新增至「Google 日曆」,藉此加以發佈;建置應用程式,使「Google 日曆」與行動裝置同步化。

除此之外,也提供了多種不同語言開存取Google Calendar

  • .NET
  • Java
  • JavaScript
  • PHP
  • Python

或可直接使用任何程式語言來實作Google Cakendar API所提供的Ptotocol的Guide所提供之內容。

2. 實作內容(以PHP為例)

目前Google Calendar須以GData API的基礎,而GData的語法是REST Style實作,而GData也支持其他Google服務的API,(如Analytics、Maps、Helath、YouTube等)。
在PHP語言,目前需引入Zend所開發的Framework來實作Google Calendar API。

由於Google Calendar牽涉到個人隱私的資料,所以在API上也提供兩種不同的認證方式。

  • ClientLogin ─ 將帳號寫死至程式碼中,較為危險的作法。 
$user = 'user@gmail.com';
$pass
= 'myPassword';
$service
= Zend_Gdata_Calendar::AUTH_SERVICE_NAME; // predefined service name for calendar

$client
= Zend_Gdata_ClientLogin::getHttpClient($user,$pass,$service);
  • AuthSub ─ 由Google本身所開發之認證機制,需藉以使用者同意授權第三方(使用API方)存取該使用者的內容資料。  
  $next = getCurrentUrl(); // Redirect site
  $scope
= 'https://www.google.com/calendar/feeds/';
  $secure
= false;
  $session
= true;
 
Zend_Gdata_AuthSub::getAuthSubTokenUri($next, $scope, $secure,
      $session
);  

$authSubUrl
= getAuthSubUrl();

 導回site後,取得回傳的Token或原已認證後將Token儲存的session 

if(! isset($_SESSION['sessionToken']) && isset($_GET['token'])) {
  $_SESSION
['sessionToken'] =
     
Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET['token']);
}

最後在內容頁

$client = Zend_Gdata_AuthSub::getHttpClient($_SESSION['sessionToken']);

所以程式碼一開始中就必需寫以下的內容(引入Zend Framework),方便後面作存取資料的動作。 

require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

上述兩者擇一使用
Zend_Loader::loadClass('Zend_Gdata_Calendar');

...

藉由認證過後取得$Client在後面作存取的動作。

  • 列出日曆清單 
$gdataCal = new Zend_Gdata_Calendar($client);
$calFeed
= $gdataCal->getCalendarListFeed();
echo
'<h1>' . $calFeed->title->text . '</h1>';
echo
'<ul>';
foreach ($calFeed as $calendar) {
  echo
'<li>' . $calendar->title->text . '</li>'; 
}

echo
'</ul>';  
  • 接收事件內容(完整) 
$gdataCal = new Zend_Gdata_Calendar($client);
$eventFeed
= $gdataCal->getCalendarEventFeed();
echo
"<ul>\n";
foreach ($eventFeed as $event) {
  echo
"\t<li>" . $event->title->text .  " (" . $event->id->text . ")\n";
  echo
"\t\t<ul>\n";
 
foreach ($event->when as $when) {
    echo
"\t\t\t<li>Starts: " . $when->startTime . "</li>\n";
 
}
  echo
"\t\t</ul>\n";
  echo
"\t</li>\n";
}
echo
"</ul>\n";
  • 接收事件內容(特定範圍) 
$startDate='2007-05-01';
$endDate='2007-08-01';

$gdataCal
= new Zend_Gdata_Calendar($client);
$query
= $gdataCal->newEventQuery();
$query
->setUser('default');
$query
->setVisibility('private');
$query
->setProjection('full');
$query
->setOrderby('starttime');
$query
->setStartMin($startDate);
$query
->setStartMax($endDate);
$eventFeed
= $gdataCal->getCalendarEventFeed($query);
echo
"<ul>\n";
foreach ($eventFeed as $event) {
  echo
"\t<li>" . $event->title->text .  " (" . $event->id->text . ")\n";
  echo
"\t\t<ul>\n";
 
foreach ($event->when as $when) {
    echo
"\t\t\t<li>Starts: " . $when->startTime . "</li>\n";
 
}
  echo
"\t\t</ul>\n";
  echo
"\t</li>\n";
}
echo
"</ul>\n";
  • 接收事件內容(全文檢索) 
$fullTextQuery='tennis'

$gdataCal
= new Zend_Gdata_Calendar($client);
$query
= $gdataCal->newEventQuery();
$query
->setUser('default');
$query
->setVisibility('private');
$query
->setProjection('full');
$query
->setQuery($fullTextQuery);
$eventFeed
= $gdataCal->getCalendarEventFeed($query);
echo
"<ul>\n";
foreach ($eventFeed as $event) {
  echo
"\t<li>" . $event->title->text .  " (" . $event->id->text . ")\n";
  echo
"\t\t<ul>\n";
 
foreach ($event->when as $when) {
    echo
"\t\t\t<li>Starts: " . $when->startTime . "</li>\n";
    echo
"\t\t</ul>\n";
    echo
"\t</li>\n";
 
}
}
echo
">/ul<\n";
  • 新增事件 
$title = 'Tennis with Beth';
$desc
='Meet for a quick lesson';
$where = 'On the courts' ;
$startDate
= '2008-01-20';
$startTime = '10:00';
$endDate
= '2008-01-20';
$endTime = '11:00';
$tzOffset = '-08';
 
$gdataCal
= new Zend_Gdata_Calendar($client);
$newEvent
= $gdataCal->newEventEntry();
 
$newEvent
->title = $gdataCal->newTitle($title);
$newEvent
->where = array($gdataCal->newWhere($where));
$newEvent
->content = $gdataCal->newContent("$desc");
 
$when
= $gdataCal->newWhen();
$when
->startTime = "{$startDate}T{$startTime}:00.000{$tzOffset}:00";
$when
->endTime = "{$endDate}T{$endTime}:00.000{$tzOffset}:00";
$newEvent
->when = array($when);

// Upload the event to the calendar server
// A copy of the event as it is recorded on the server is returned
$createdEvent
= $gdataCal->insertEvent($newEvent);
  
  • 刪除事件 
$eventEntry = getEvent($client, $eventId, $calendarID);

// $event is an instance of Zend_Gdata_Calendar_EventEntry

$event
->delete();

3. 實作教學

以下這邊先作認證(AuthSub)部份:

  1. $next是寫上自己要導回到自己網站的子頁面
  2. $scope是訪問Google的服務網址,而每個服務的網址則都是不同的
  3. 最後藉由Zend Framework中的Zend_Gdata_AuthSub中的getAuthSubTokenUri的function去取得認證的頁面網址,最後在導向該頁面
  4. 在該頁面進行同意授權給http://yourwebsite/calendar_session.php則會導回該頁面,並且會回傳一個認證後的Token。
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
	  
$next = 'http://yourwebsite/calendar_session.php'; //導回第三方(使用API方)頁面
$scope = 'http://www.google.com/calendar/feeds/'; //訪問API服務的scope	  
$secure = 0;
$session = 1;
//取得Google授權網址
$authSubURL = Zend_Gdata_AuthSub::getAuthSubTokenUri($next, $scope, $secure, $session); 
  
$url = "Location:" . $authSubURL;
header($url); //導向Google授權網址

可是有可能在這個網頁中,必須多次使用到Calendar的服務,當然也不可能一直認證的動作,所以可以在導回的網頁作一些手腳,也就是把Token存起來,不過上面所取得的Token須經過Zend_Gdata_AuthSub中getAuthSubSessionToken的function去轉換可持續性使用的Token,再存至session中,所以以下就是calendar_session.php的內容

<?php
session_start();
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
//轉換可持續性使用的Token
$_SESSION['sessionToken'] = Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET['token']);
header("Location:http://yourwebsite/content.php");
?>
再回到認證部份,這時也要作一個判斷,判斷是否已存有認證過的session,沒有才去執行認證的過程,有的話就直接導向內容存取頁面,就直接進入到Google Calendar API核心部份
//查看是否已有認證的Token
$sessionToken = isset($_SESSION['sessionToken']) ? $_SESSION['sessionToken'] : null;
//若已認證過, 導向內容存取頁面
if($sessionToken!=null){
	header("Location:http://yourwebsite/content.php");
}
else{
	...認證SCOPE...
}

在內容存取頁面,首先先取得個人的Calendar List,藉由Zend_Gdata_AuthSub中的getHttpClient來將Token轉換,getCalendarListFeed()則可以直接取得Calendar List,同時取得Calendar的網址,在下一頁面直接對該Calendar的事件內容存取

require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
Zend_Loader::loadClass('Zend_Gdata_Calendar');

$client = Zend_Gdata_AuthSub::getHttpClient($_SESSION['sessionToken']);

$gdataCal = new Zend_Gdata_Calendar($client);		  
$calFeed = $gdataCal->getCalendarListFeed();
//取得Calendar名稱
echo '<h1>' . $calFeed->title->text . '</h1>';
echo '<ul>';
//取得該Calendar的網址, 傳到calContent.php直接對該Calendar內容存取
foreach ($calFeed as $calendar) 
{			
	echo '<li><a href="calContent.php?calsite=' . $calendar->link[0]->href . '">' . $calendar->title->text . '</li></a>';	
}
echo '</ul>';

最後在calContent.php中取得該Calendar事件的內容,getCalendarEventFeed()則直接取得事件的內容

require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_AuthSub');		
Zend_Loader::loadClass('Zend_Gdata_Calendar');

$client = Zend_Gdata_AuthSub::getHttpClient($_SESSION['sessionToken']);

//設定起迄的範圍
$startDate = '2007-05-01';
$endDate = '2010-12-31';

$gdataCal = new Zend_Gdata_Calendar($client);
//取得該Calendar的內容
$query = $gdataCal->newEventQuery($_GET['calsite']);
//針對取得該Calendar作參數上的設定
$query->setUser(null);
$query->setVisibility(null);
$query->setProjection(null);
$query->setOrderby('starttime');
$query->setOrderby('endtime');
$query->setStartMin($startDate);
$query->setStartMax($endDate);
$eventFeed = $gdataCal->getCalendarEventFeed($query);
echo "<ul>\n";
	foreach ($eventFeed as $event) 
	{
		echo "\t<li><b>" . $event->title->text . "</b>\n";
		echo "\t\t<ul>\n";
			foreach ($event->when as $when) 
			{
				echo "\t\t\t<li>內容: " . $event->content->text . "</li>\n";
				echo "\t\t\t<li>開始時間: " . $when->startTime . "</li>\n";
				echo "\t\t\t<li>結束時間: " . $when->endTime . "</li>\n";
				foreach ($event->where as $where) 
				{
					echo "\t\t\t<li>地點: " . $where->valueString . "</li>\n";
				}
				echo "\t\t\t<li>Event ID: " . basename($event->id->text) . "</li>\n";
				echo "\t\t\t<li>Event URI: <a href=\"" . $event->id->text . "\">". $event->id->text . "</a></li>\n";							
				echo "\t\t</ul>\n";
				echo "\t</li>\n";				
			}
	}
echo "</ul>\n";

最後再重新整理一下...

index.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Google Calendar 範例</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>Google Calendar 範例</h1>

<?php
session_start();

//查看是否已有認證的Token(以session儲存)
$sessionToken = isset($_SESSION['sessionToken']) ? $_SESSION['sessionToken'] : null;
//若已認證過, 導向內容存取頁面
if($sessionToken!=null){
	header("Location:http://yourwebsite/content.php");
}
else{
	require_once 'Zend/Loader.php';
	Zend_Loader::loadClass('Zend_Gdata_AuthSub');
	  
	  $next = 'http://yourwebsite/calendar_session.php';
	  $scope = 'http://www.google.com/calendar/feeds/';	  
	  $secure = 0;
	  $session = 1;
	  $authSubURL = Zend_Gdata_AuthSub::getAuthSubTokenUri($next, $scope, $secure, $session);
	  
	  	  
	  $url = "Location:" . $authSubURL;
	  header($url); 
	
}

?>
</body>
</html>

calendar_session.php

<?php
session_start();
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
//轉換可持續性使用的Token
$_SESSION['sessionToken'] = Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET['token']);
header("Location:http://yourwebsite/content.php");
?>

content.php 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Google Calendar 範例</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>Google Calendar 範例</h1>
<?php
    session_start();
	//查看是否已有認證的Token
	$sessionToken = isset($_SESSION['sessionToken']) ? $_SESSION['sessionToken'] : null;
	//若已認證過, 導向內容存取頁面
	if($sessionToken==null){
		header("Location:Location:http://yourwebsite/calendar/");
	}
	else
	{	
		require_once 'Zend/Loader.php';
		Zend_Loader::loadClass('Zend_Gdata');
		Zend_Loader::loadClass('Zend_Gdata_AuthSub');
		Zend_Loader::loadClass('Zend_Gdata_Calendar');
		
		$client = Zend_Gdata_AuthSub::getHttpClient($_SESSION['sessionToken']);
		
		$gdataCal = new Zend_Gdata_Calendar($client);		  
		$calFeed = $gdataCal->getCalendarListFeed();
		echo '<h1>' . $calFeed->title->text . '</h1>';
		echo '<ul>';
		foreach ($calFeed as $calendar) {			
			echo '<li><a href="calContent.php?calsite=' . $calendar->link[0]->href . '">' . $calendar->title->text . '</li></a>';	
		}
		echo '</ul>';
		
	}
?>
</body>
</html>

calContent.php 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Google Calendar 範例</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>Google Calendar 範例</h1>
<?php
    session_start();
	//查看是否已有認證的Token
	$sessionToken = isset($_SESSION['sessionToken']) ? $_SESSION['sessionToken'] : null;
	//若已認證過, 導向內容存取頁面
	if($sessionToken==null)
	{
		header("Location:http://shs.www1036.idv.tw/contest/logic/h9_index.php");
	}
	else
	{	
		require_once 'Zend/Loader.php';
		Zend_Loader::loadClass('Zend_Gdata');
		Zend_Loader::loadClass('Zend_Gdata_AuthSub');		
		Zend_Loader::loadClass('Zend_Gdata_Calendar');
		
		$client = Zend_Gdata_AuthSub::getHttpClient($_SESSION['sessionToken']);
		
		//設定起迄的範圍
		$startDate = '2007-05-01';
		$endDate = '2010-12-31';		
		
		//取得該Calendar的內容
		$gdataCal = new Zend_Gdata_Calendar($client);
		$query = $gdataCal->newEventQuery($_GET['calsite']);
		//針對取得該Calendar作參數上的設定
		$query->setUser(null);
		$query->setVisibility(null);
		$query->setProjection(null);
		$query->setOrderby('starttime');
		$query->setOrderby('endtime');
		$query->setStartMin($startDate);
		$query->setStartMax($endDate);
		$eventFeed = $gdataCal->getCalendarEventFeed($query);
		echo "<ul>\n";
			foreach ($eventFeed as $event) 
			{
				echo "\t<li><b>" . $event->title->text . "</b>\n";
				echo "\t\t<ul>\n";
					foreach ($event->when as $when) 
					{
						echo "\t\t\t<li>內容: " . $event->content->text . "</li>\n";
						echo "\t\t\t<li>開始時間: " . $when->startTime . "</li>\n";
						echo "\t\t\t<li>結束時間: " . $when->endTime . "</li>\n";
						foreach ($event->where as $where) 
						{
							echo "\t\t\t<li>地點: " . $where->valueString . "</li>\n";
						}
						echo "\t\t\t<li>Event ID: " . basename($event->id->text) . "</li>\n";
						echo "\t\t\t<li>Event URI: <a href=\"" . $event->id->text . "\">". $event->id->text . "</a></li>\n";							
						echo "\t\t</ul>\n";
						echo "\t</li>\n";
						
					}
			}
		echo "</ul>\n";			
	}
?>
</body>
</html>

 

其他幫助

  • 小技巧1

使用Google Calendar API之前 最好瞭解一下event的物件結構才能清楚的使用(Using PHP)

目前是運用Zend公司開發出來的GData函式庫來讀取Google Calendar,先到這裡來下載,並且先在php.ini 啟用 extension=php_openssl.so或是在程式一開始加入ini_set('extension', 'php_openssl.so');  並且複製 PHP資料夾下的 libeay32.dll 與 ssleay32.dll至 Windows\System32 資料夾。

事項:$event->title->text
時間(array):$event->when
起始時間:$event->when->startTime 
結束時間:$event->when->endTime
地點(array):$event->where->valueString
詳情:$event->content->text

  • 小技巧2

裡用開發工具使用google calendar API :eclipse(Using JAVA)

以下連結是安裝介紹

http://code.google.com/intl/zh-TW/apis/gdata/articles/eclipse.html

下載好Eclipse後

需要把下列三個library匯入

mail.jar     Sun's JavaMail API
activation.jar     Sun's JavaBeans Activation Framework
servlet-api.jar     Apache Tomcat

新增Project並匯入所需要的google api
就可以開發
相關範例檔以及資料連結如下

http://code.google.com/p/gdata-java-client-eclipse-plugin/

http://code.google.com/apis/gdata/

http://code.google.com/p/gdata-java-client/

http://code.google.com/apis/gdata/articles/java_client_lib.html

http://code.google.com/apis/gdata/javadoc/

 

主要架構都是先建立一個CalendarFeed
利用CalendarEntry去取得日曆資料
如果要新增或刪除calendar裡面的事件(Event)內容
需要new個calendarEventFeed
再用CalendarEventEntry去做存取的動做
以下列出Google calendar 的JAVA API

http://code.google.com/intl/zh-TW/apis/gdata/javadoc/

  • 小技巧3

如何抓取到Calendar裡面的event?(Using JAVA)
CalendarEventFeed這calss裡面有getId的method
如果程式呈現在持續執行的狀態下
也就是新增刪除查詢修改皆不會關閉程式
此getId會是唯一值 能方便的用ID去判斷event
但是,如果程式只是在需要用的時候才被呼叫
那麼下一次開啟時,同一事件ID會不一樣
我在開發時使用比較蠢的方法 ─ 利用description的比對來找出Event
我承認這不是一個很好的方法

  • 小技巧4

在新增Event時 是使用iCal格式

http://tools.ietf.org/html/rfc2445

可以設定時區、開始、結束的日期與時間、循環規則、標題、內容敘述...等
例如:
DTSTART;VALUE=DATE:20070501  開始日期
DTEND;VALUE=DATE:20070502  結束日期
RRULE:FREQ=WEEKLY;BYDAY=Tu;UNTIL=20070904 以循環當方式每一週的禮拜二顯示
還有其他更細部的設定就要去參考網站了

用php去實作的話可參考以下網站
http://function1122.blogspot.com/2008/07/php-google-calendar-api-part-4.html

 

參考資料:

1. [教學] 在PHP 中使用Google Calendar API , FUNcLogs, http://function1122.blogspot.com/2008/07/php-google-calendar-api-part-4.html
2. Google日曆, 維基百科, http://zh.wikipedia.org/zh-tw/Google_Calendar
3. Google Calendar APIs and Tools, Google Code, http://code.google.com/intl/en/apis/calendar/data/2.0/developers_guide.html