Google Calendar API v3でスケジュール同期


----

+ [※ oAuth対応版の最新ライブラリに対応しました。→ 記事ページへ](http://qiita.com/qoAop/items/5dadc1e8ef9bb65e9ed8)

----

#はじめに
別サービスのイベントデータをJavaで作られたツールでGoogle Calendarと同期していたんですが、少し前からGoogle Calendar API v3に対応してない古いツールではアクセス出来なくなってしまいました...

...ということで、Google Calendar API v3に対応した同期ツールをphpで作りました。

**注）phpでプログラムを書くのはほぼ初めてですので記述がおかしいとかあるかもしれません。;)**

私は同期したいデータを落としてきてperlでdata.csvの形に整形し、その後cal.phpを実行するようにcronで設定してます。

#Google Developer Consoleでのアプリケーション設定
まず、[Google Developer Console](https://console.developers.google.com/project)にいってアプリケーションのプロジェクトを作成してください。
細かい操作は他サイトでも紹介されていますので省略しますが、

+ 「APIと認証」のAPIでCalendar APIを利用する設定にしてください。
+ 認証情報で、OAuthの新しいクライアントIDを作成します。
+ クライアントIDのタイプは「サービスアカウント」というのを選んでください。
+ クライアントID、メールアドレス、公開フィンガーキープリントというのが表示されます。
+ 同時に鍵ファイル（~~.p12）がダウンロードされてきます。

これらを保存＆メモしておいてください。

#Google Calendarでの共有設定

次はGoogle Calendarの方にAPIからのアクセスを許可するように設定します。
Google Calendarのマイカレンダーで同期したいカレンダーを作成して、カレンダー設定を開きます。
※普段使ってるのとは別に同期専用カレンダーを作成してください。

+ カレンダー名　をメモしておいてください。

タブを切り替えて、「このカレンダーを共有」を開きます。
特定のユーザーと共有というのがあるので、クライアントIDの時にメモしたメールアドレス（～～～@developer.gserviceaccount.com）を登録してください。権限は「変更及び共有の管理権限」にします。（予定の変更権限でも大丈夫かもしれません。）

これで共有設定は終わりです。


#プログラム構成
プログラム構成はこんな感じです。
googleのサイトに行ってライブラリを見たところperlがない(;_;)ため、phpで作ります。
php版のライブラリは、[google-api-php-client](https://github.com/google/google-api-php-client)からダウンロードします。

hogehoge.p12ファイルはクライアントIDを作成した時にダウンロードしたものです。

```
.
|-- cal.php
|-- data.csv
|-- hogehoge.p12
`-- google-api-php-client-master
    |-- CONTRIBUTING.md
    |-- ～～
    `-- src
         `-- Google

```

data.csvはこんな感じにしてください。utf8でタブ区切りです。
開始時刻、終了時刻、場所、タイトル名称、説明、リンク（なければダミーで）になります。

```data.csv
2014-11-29T11:30:00+09:00	2014-11-29T12:30:00+09:00	場所1	名称1	説明1	http://example.com?no1
2014-11-29T12:30:00+09:00	2014-11-29T13:30:00+09:00	場所2	名称2	説明2	http://example.com?no2
2014-11-29T13:30:00+09:00	2014-11-29T14:30:00+09:00	場所3	名称3	説明3	http://example.com?no3
2014-11-29T14:30:00+09:00	2014-11-29T15:30:00+09:00	場所4	名称4	説明4	http://example.com?no4
```

本プログラムのcal.phpはこんな感じです。

+ data.csvを読み込み
+ Calendar API で同期用のカレンダーのイベントを読み込みます。
+ data.csvに無い情報は削除します。
+ 次に、data.csv新規・変更された情報を挿入します。

個人設定部分は適宜書きなおしてください。

```php:cal.php
#!/usr/bin/php
<?php
require_once './google-api-php-client-master/src/Google/Client.php';

////////////
$clientId = '<OAuth 用のクライアントID>'; 
$authEmail = '<OAuth 用のメールアドレス>';
$p12Key = file_get_contents('./<OAuth 用のp12ファイル.p12>');
$target_calendar = '< Google Calendarのカレンダー名 >';
////////////

//イベント読み込み
$data= array();

setlocale(LC_ALL,'ja_JP.UTF-8');
$fp = fopen("data.csv","r");
while ( $d = fgetcsv($fp,4096,"\t") ) {
	//0:開始時刻
	//1:終了時刻
	//2:場所
	//3:タイトル
	//4:説明文
	//5:URL
	$key = $d[0]."\t".$d[1]."\t".$d[2]."\t".$d[3]."\t".$d[4]."\t".$d[5];
	$data[md5($key)] = $key;
}


//カレンダーAPI認証
$scopes = array('https://www.googleapis.com/auth/calendar');
$credential = new Google_Auth_AssertionCredentials($authEmail,$scopes,$p12Key);

$client = new Google_Client();
$client->setClientId($clientId);
$client->setAssertionCredentials($credential);
if ($client->getAuth()->isAccessTokenExpired()) {
  $client->getAuth()->refreshTokenWithAssertion($credential);
}

$service = new Google_Service_Calendar($client);

//カレンダー一覧
$cal_list = $service->calendarList->listCalendarList();

//登録一覧表示
foreach ($cal_list['items'] as $calendar) {
	
	$calenderId = $calendar->id;
	$calender_name = $calendar->getSummary();
	echo($calender_name."\t".$calenderId."\n");
	if ( $calender_name != $target_calendar ) {
		continue;
	}

	//既存イベント取得	
	$events = $service->events->listEvents($calenderId);
	while(true) {

		foreach ($events->getItems() as $event) {
			$eventId = $event->getId(); 
			
			$source_title = $event->getSource()->getTitle();
			
			//カレンダーから読み込んだキーとデータファイルを比較してデータファイル側になければ削除
			if ( array_key_exists( $source_title , $data) ) {
				//更新リストから削除
				echo("Already exist:".$eventId."\t".$event->getSummary()."\n");
				unset($data[$source_title]);
			} else {
				echo("Delete:".$eventId."\t".$event->getSummary()."\n");
				$service->events->delete( $calenderId , $eventId );
			}

		}

		$pageToken = $events->getNextPageToken();
		
		if ($pageToken) {
			$optParams = array('pageToken' => $pageToken);
			$events = $service->events->listEvents($calenderId, $optParams);
		} else {
			break;
		}
		
	}

	//変更されたイベントの追加
	foreach ($data as $key => $value) {
		
		$d = split("\t", $value);

		//終日イベントの判定
		$start_datetime = split("T",$d[0]);
		$end_datetime   = split("T",$d[1]);
		$all_day = FALSE;

		if ( $start_datetime[0] === $end_datetime[0] && $start_datetime[1] === "00:00:00+09:00" && $end_datetime[1] === "23:59:59+09:00" ) {
			$all_day = TRUE;
		}

		//新規追加
		$event = new Google_Service_Calendar_Event();
		$event->setLocation( $d[2] );	
		$event->setSummary( $d[3] );
		$event->setDescription( $d[4] );

		$start = new Google_Service_Calendar_EventDateTime();
			if ( $all_day ) {
				$start->setDate( $start_datetime[0] );
			} else {
				$start->setDateTime( $d[0] );
			}
			$event->setStart($start);

		$end = new Google_Service_Calendar_EventDateTime();
			if ( $all_day ) {
				$end->setDate( $end_datetime[0] );
			} else {
				$end->setDateTime( $d[1] );
			}
			$event->setEnd($end);
			
		$source = new Google_Service_Calendar_EventSource();
			$source->setTitle( $key );
			$source->setUrl( $d[5] );
			$event->setSource( $source );

		$service->events->insert( $calenderId,$event);

		echo("Append:".$d[0]."\t".$d[3]);echo "\n";

	}	

}
?>

```

- 時刻を同日の00:00:00 ～23:59:59にした場合には終日イベントになるようにしました。2015-09-17追記