* Googleカレンダー連携 [#zaec426f] #setlinebreak(on); OAuth認証後に Google Calendar APIを使用してカレンダー情報を取得する簡単なWebアプリケーションを作成する。 ここではPHPで実装するが、他の言語でも基本的な流れは同じ。 エンドポイントに対して直接リクエストを発行する場合のインターフェースは以下のURLを参照。 https://developers.google.com/google-apps/calendar/v3/reference/ &br; #contents // サービスアカウント版は別途記載予定。 ** 概要 [#a76b6172] #html(<div style="padding-left:10px">) 以下の通り、カレンダーAPIからのデータ取得を行う簡単なアプリケーションを作成する。 #TODO(フォルダ構成、サンプル概要) |URI|クライアントAPIの言語|h |http://localhost/test/google-calendar/|PHP| ※Google認証後のリダイレクトページも同じURIを使用する #html(</div>) ** プロジェクトの作成と認証情報の設定/取得 [#h9ad68bf] #html(<div style="padding-left:10px">) *** プロジェクトの登録 [#sfdc52cf] #html(<div style="padding-left:10px">) Google API Console から プロジェクトを作成する //https://console.developers.google.com/?hl=ja // https://console.developers.google.com/flows/enableapi?apiid=calendar&hl=ja https://console.developers.google.com/project #html(</div>) *** APIの有効化 [#u005b2fe] #html(<div style="padding-left:10px">) (1) 対象のプロジェクトを選択して「APIとサービスの有効化」をクリック。 (2) 検索ボックスに ”Calendar” を入力。 (3) "Google Calendar API"を選択し、「有効にする」をクリック。 #html(</div>) *** 認証情報の作成 [#kd46634e] #html(<div style="padding-left:10px">) (1)「認証情報の作成」から「OAuthクライアントID」をクリックして以下の通り入力して「作成」。 アプリケーションの種類: ウェブ アプリケーション 名前: カレンダー連携テストクライアント 承認済みのリダイレクト URI: http://localhost/test/google-calendar/ (2) ”認証情報” の OAuth同意画面を以下の通り入力し、「保存」 ユーザーに表示するサービス名: カレンダー連携テストサービス ※利用規約やプライバシーポリシーの URLを入力する場合はここで入力。 (3) "認証情報" から作成したOAuthクライアントを選択し、「JSONをダウンロード」 ※client_secret.json にリネーム。 #html(</div>) #html(</div>) ** Google クライアントライブラリをインストールする [#tf7d968f] #html(<div style="padding-left:10px">) ここでは composer でインストールする #code(myterm2 nolinenums){{ cd /path/to/www/ mkdir google-calendar && cd google-calendar composer require google/apiclient:^2.0 }} ※GitHubからダウンロードする場合はダウンロード後、上記ディレクトリ(/path/to/www/google-calendar配下)に解凍 https://github.com/google/google-api-php-client/releases *** 参考 [#l6288920] #html(<div style="padding-left:10px">) https://developers.google.com/api-client-library/php https://developers.google.com/google-apps/calendar/quickstart/php?hl=ja https://developers.google.com/api-client-library/php/start/installation?hl=ja https://github.com/google/google-api-php-client#download-the-release #html(</div>) #html(</div>) ** 連携処理の作成 [#r31062d3] #html(<div style="padding-left:10px">) #TODO(登録/更新/削除) /path/to/www/test/google-calendar/index.php #code(mycode2){{ <?php date_default_timezone_set('Asia/Tokyo'); //error_reporting(E_ALL); require_once 'vendor/autoload.php'; // OAuthクライアント認証用のJSONファイル $oauth_credentials = "/path/to/client_secret.json"; // 上記でダウンロードしたJSONファイルのPATH // Google認証後のリダイレクト先(「http://localhost/test/google-calendar/?code=アクセストークン」 という形でリダイレクトされる) $redirect_uri = "http://localhost/test/google-calendar/"; // トークンの退避用 TODO: テスト用(本番時はDB等に退避する) $token_file = "/tmp/google-calendar-api-token-cache.txt"; session_start(); // セッションを破棄する(テスト用) // ※退避しておいたトークンを使用して認証が通る事の確認用 if (isset($_REQUEST['logout'])) { session_destroy(); session_start(); header("Location: " . $redirect_uri); exit; } // トークンを破棄する(テスト用) // ※アカウントの再選択を行いたい場合 if (isset($_REQUEST['remove-token'])) { session_destroy(); session_start(); file_put_contents($token_file, ""); header("Location: " . $redirect_uri); exit; } $msg = ""; // 取得済みのトークンがある場合はセッションにセット TODO: 本番時はDBから取得 if (empty($_SESSION['google-calendar-api-token'])) { if (file_exists($token_file)) { $tokenText = file_get_contents($token_file); if (trim($tokenText) !== "") { $msg = "トークンをファイルから取得しました。"; $_SESSION['google-calendar-api-token'] = unserialize(file_get_contents($token_file)); } } } // Google API Client $client = new Google_Client(); $client->setAuthConfig($oauth_credentials); $client->setRedirectUri($redirect_uri); $client->addScope(Google_Service_Calendar::CALENDAR); $client->setAccessType("offline"); // トークンの自動リフレッシュ $client->setApprovalPrompt("force"); // これがないと初回以外はリフレッシュトークンが得られない $authUrl = $client->createAuthUrl(); // 認証後のリダイレクトの場合 if (isset($_GET['code'])) { $token = $client->fetchAccessTokenWithAuthCode($_GET['code']); $_SESSION['google-calendar-api-token'] = $token; header("Location: ${redirect_uri}"); exit; } // トークンの有効期限が切れている場合はリフレッシュトークンを使用して最新のトークンを得る if (!empty($_SESSION['google-calendar-api-token'])) { $client->setAccessToken($_SESSION['google-calendar-api-token']); If ($client->isAccessTokenExpired()) { if (isset($_SESSION['google-calendar-api-token']["refresh_token"])) { $client->refreshToken($_SESSION['google-calendar-api-token']["refresh_token"]); } else { unset($_SESSION['google-calendar-api-token']); } } } // トークンが無効な場合は認証ページにリダイレクト $accessToken = $client->getAccessToken(); if (!$accessToken) { header("Location: ${authUrl}"); exit; } $_SESSION['google-calendar-api-token'] = $accessToken; // トークンを退避 TODO: 本番時はDBなどに退避する $tokenText = serialize($_SESSION['google-calendar-api-token']); file_put_contents($token_file, $tokenText); // カレンダーAPI用のインスタンス生成 $cal_service = new Google_Service_Calendar($client); // カレンダ一覧を取得 $calendar_list = array(); $calendarList = $cal_service->calendarList->listCalendarList(); while(true) { foreach ($calendarList->getItems() as $i => $calendarListEntry) { $rec = $calendarListEntry; $calendar_list[] = $rec; } $pageToken = $calendarList->getNextPageToken(); if ($pageToken) { $optParams = array('pageToken' => $pageToken); $calendarList = $service->calendarList->listCalendarList($optParams); } else { break; } } // イベント情報 $calender_idx = 0; $calender_id = "primary"; if (isset($_REQUEST["calender_idx"])){ if (isset($calendar_list[$_REQUEST["calender_idx"]])){ $calender_idx = $_REQUEST["calender_idx"]; $calender_id = $calendar_list[$calender_idx]["id"]; } } $event_list = array(); $optParams = array(); /* 日付指定する場合 */ $optParams["timeMin"] = "2017-01-01T00:00:00+0900"; // "2017-01-01T00:00:00Z"; $optParams["timeMax"] = "2017-12-31T23:59:59+0900"; $optParams["timeZone"] = "Asia/Tokyo"; $optParams["singleEvents"] = true; $optParams["orderBy"] = "startTime"; // orderBy指定する場合は singleEvents=true でないと怒られる $events = $cal_service->events->listEvents($calender_id, $optParams); while(true) { foreach ($events->getItems() as $event) { $rec = array(); $rec["id"] = $event->getId(); $rec["start"] = $event->getStart()->date ? $event->getStart()->date : $event->getStart()->dateTime; $rec["end"] = $event->getEnd()->date ? $event->getEnd()->date : $event->getEnd()->dateTime; $rec["summary"] = $event->getSummary(); $event_list[] = $rec; } $pageToken = $events->getNextPageToken(); if ($pageToken) { $optParams['pageToken'] = $pageToken; $events = $cal_service->events->listEvents($calender_id, $optParams); } else { break; } } // 描画 require_once("view/calendar.php"); ?> }} #html(</div>) ** Viewの作成 [#r31062d3] #html(<div style="padding-left:10px">) /path/to/www/test/google-calendar/view/calendar.php #code(myhtml2){{ <!doctype html> <html lang="ja"> <head> <meta charset="utf-8" /> <style> * { font-size: 16px; } h1,h2,h3,h4,h5 { margin: 0; } .tbl { border-collapse: collapse; border-spacing: 0; } .tbl th { background: #ccc; } .tbl th, .tbl td{ padding: 1px 10px; border: 1px solid #333; } html,body { height: 100%; } #loading_layer { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); } #loading_inner { position: fixed; top: 50%; left: 50%; width: 200px; height: 50px; margin-left: -100px; margin-top: -25px; background: #fff; border-radius: 4px; overflow-y: hidden; } #loading_msg { padding-top: 10px; padding-bottom: 10px; text-align: center; vertical-align: middle; } </style> </head> <body> <div><?= $msg ?></div> <div style="position:relative;"> <div style="position:absolute;top:0;right:10px;"> <a href="./?logout=true">セッション破棄</a> <a href="./?remove-token=true" style="margin-left:10px;">トークン破棄</a> </div> <div style="display:inline-block"> カレンダー : <select id="calendar_id"> <?php foreach ($calendar_list as $idx => $rec) {?> <option value="<?= $idx ?>" <?= $calender_idx == $idx ? "selected" : "" ?>><?= $rec["summary"] ?></option> <?php } ?> </select> </div> </div> <table class="tbl" style="margin-top:4px"> <thead> <tr> <th>Id</th> <th>Start</th> <th>End</th> <th>Summary</th> </tr> </thead> <tbody> <?php foreach ($event_list as $rec) { ?> <tr> <td><?= $rec["id"] ?></td> <td><?= $rec["start"] ?></td> <td><?= $rec["end"] ?></td> <td><?= $rec["summary"] ?></td> <tr> <?php } ?> </tbody> </table> <div id="loading_layer" style="display:none"> <div id="loading_inner"><div id="loading_msg">Now Loading...</div></div> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script> <script> jQuery(function($){ $("#calendar_id").on("change", function(){ $("#loading_layer").show(); location.href = "./?calender_idx=" + $(this).val(); }); }); </script> </body> </html> }} #html(</div>) *** APIリファレンス [#me3105f5] #html(<div style="padding-left:10px;">) 以下はAPIのエンドポイントを直叩きする場合用の記載になっているが、クライアントAPIを利用する場合でもオプション引数などはそのまま使える。 https://developers.google.com/google-apps/calendar/v3/reference/ #html(</div>) *** サンプル [#t8c0d4d0] *** サンプルソース [#t8c0d4d0] #html(<div style="padding-left:10px;">) https://github.com/google/google-api-php-client https://github.com/google/google-api-php-client/tree/master/examples #html(</div>)