├── .gitattributes ├── .gitignore ├── LICENSE.TXT ├── README.md ├── addToCalendar.php ├── common ├── css │ └── styles.css ├── footer.php └── header.php ├── doAdd.php ├── error.php ├── home.php ├── loc └── readme-ja.md ├── logout.php ├── o365 ├── ClientReg.php ├── Office365Service.php └── authorize.php ├── sessionManager.php └── site-event-list.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | # Project-specific 46 | deploy.bat 47 | *.zip 48 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | php-calendar, https://github.com/jasonjoh/php-calendar 2 | 3 | Copyright (c) Microsoft Corporation 4 | All rights reserved. 5 | 6 | MIT License: 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | ""Software""), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Calendar API Sample # 2 | 3 | [日本 (日本語)](https://github.com/jasonjoh/php-calendar/blob/master/loc/readme-ja.md) (Japanese) 4 | 5 | This sample shows how you can use the [Calendar API](https://msdn.microsoft.com/office/office365/APi/calendar-rest-operations) from PHP. The sample app is an "upcoming shows" app for a fictional community theater's Shakespearean festival. Users can connect their Office 365 account and add events to their calendar for the show times they are attending. The user has the option of inviting friends, which will send a meeting request to each invited friend. 6 | 7 | ## API features used ## 8 | 9 | - Creating events on a user's default calendar 10 | - Adding attachments to events 11 | - Adding attendees to events 12 | - Using a [calendar view](https://msdn.microsoft.com/office/office365/APi/calendar-rest-operations#GetCalendarView) to expand recurring events and display all appointments for a single day. 13 | 14 | ## Required software ## 15 | 16 | - [PHP 5.6](http://php.net/downloads.php) 17 | - A web server capable of serving PHP. 18 | 19 | In my testing I used IIS 8 installed on a Windows 8.1 laptop. I installed PHP 5.6.0 using the [Web Platform Installer](http://www.microsoft.com/web/downloads/platform.aspx) (Windows/IIS only). 20 | 21 | ## Running the sample ## 22 | 23 | It's assumed that you have PHP installed before starting, and that your web server is configured to process and server PHP files. 24 | 25 | 1. Download or fork the sample project. 26 | 1. Create a new directory in your web root directory called `php-calendar`. Copy the files from the repository to this directory. 27 | 1. [Register the app in Azure Active Directory](https://github.com/jasonjoh/office365-azure-guides/blob/master/RegisterAnAppInAzure.md). The app should be registered as a web app with a Sign-on URL of `http://localhost/php-calendar`, and should be given the permission to "Have full access to users' calendars", which is available in the "Delegated Permissions" dropdown. 28 | 1. Edit the `.\o365\ClientReg.php` file. 29 | 1. Copy the client ID for your app obtained during app registration and paste it as the value for the `$clientId` variable. 30 | 1. Copy the key you created during app registration and paste it as the value for the `$clientSecret` variable. 31 | 1. Save the file. 32 | 1. If your PHP installation is not configured with updated CA certificates to verify SSL, requests will fail unless you run Fiddler on the server and set the `$enableFiddler` variable to `true` in `Office365Service.php`. Alternatively, you can insert the following line immediately before any call to `curl_exec`. **However,** it should be noted that doing so disables any SSL verification, which should NOT be done in production. 33 | 34 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); 35 | 1. Open a web browser and browse to `http://localhost/php-calendar/home.php`. 36 | 1. You should see a list of upcoming show times for various Shakespearean plays. Click on any of the "Connect my Calendar" buttons to sign in to Office 365. 37 | 1. Once signed in you should be redirected back to the home page, and the buttons should now read "Add to Calendar." Click the button next to a specific show time to add it to your calendar. Events with a "Voucher Required" field of Yes will include the voucher as an attachment on the event. 38 | 39 | ## Copyright ## 40 | 41 | Copyright (c) Microsoft. All rights reserved. 42 | 43 | ---------- 44 | Connect with me on Twitter [@JasonJohMSFT](https://twitter.com/JasonJohMSFT) 45 | 46 | Follow the [Exchange Dev Blog](http://blogs.msdn.com/b/exchangedev/) -------------------------------------------------------------------------------- /addToCalendar.php: -------------------------------------------------------------------------------- 1 | title."' from session."); 23 | 24 | // Get all events on the user's O365 calendar for that day. 25 | $eventsOnThisDay = Office365Service::getEventsForDate($accessToken, $event->startTime); 26 | if (SessionManager::checkResponseAndRefreshToken($eventsOnThisDay)) { 27 | // Pick up new access token 28 | $accessToken = $_SESSION['accessToken']; 29 | 30 | error_log("Retrying get events request"); 31 | $eventsOnThisDay = Office365Service::getEventsForDate($accessToken, $event->startTime); 32 | } 33 | 34 | // Build a link URL to the doAdd.php file, which does the actual work to add 35 | // the event to the O365 calendar. 36 | $buttonUrl = "doAdd.php"; 37 | 38 | $altRow = false; 39 | ?> 40 | 41 |
42 |
43 |

Add Event To Calendar

44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
Showtitle ?>
Locationlocation ?>
DatestartTime, "M j, Y") ?>
TimestartTime, "g:i a")." - ".date_format($event->endTime, "g:i a") ?>
Voucher required?voucherRequired ? "Yes" : "No" ?>
66 |
67 |
68 |
Your calendar for startTime, "m/d/Y") ?>
69 | ERROR: ".$eventsOnThisDay['error']."
"; 72 | } 73 | ?> 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | > 82 | 92 | 101 | 110 | 111 | 112 |
EventStartEnd
83 | 91 | 93 | setTimeZone(new DateTimeZone(date_default_timezone_get())); 98 | echo date_format($startDate, "g:i a"); 99 | ?> 100 | 102 | setTimeZone(new DateTimeZone(date_default_timezone_get())); 107 | echo date_format($endDate, "g:i a"); 108 | ?> 109 |
113 | No events found
"; } ?> 114 | 115 | 116 | 117 |
118 | 119 |
120 |
121 | 122 |
123 | -------------------------------------------------------------------------------- /common/css/styles.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. */ 2 | body { 3 | font-family: Tahoma, Geneva, sans-serif; 4 | } 5 | 6 | #info-bar { 7 | width: 100%; 8 | background-color: #2E8B57; 9 | color: #ffffff; 10 | font-size: 1.5em; 11 | text-align: left; 12 | padding: 5px; 13 | margin-bottom: 10px; 14 | } 15 | 16 | #cal-view-title { 17 | width: 100%; 18 | background-color: #2E8B57; 19 | color: #ffffff; 20 | font-size: 1.5em; 21 | text-align: center; 22 | padding: 5px; 23 | } 24 | 25 | #nav-links { 26 | width: auto; 27 | } 28 | 29 | #app-title { 30 | width: auto; 31 | } 32 | 33 | #logout { 34 | float: right; 35 | width: auto; 36 | } 37 | 38 | #table-title { 39 | font-style: bold; 40 | font-size: 2em; 41 | } 42 | 43 | .show-list { 44 | width: 100%; 45 | border-collapse: collapse; 46 | margin-top: 10px; 47 | margin-left: 5px; 48 | } 49 | 50 | .show-list td, .show-list th { 51 | font-size: 1em; 52 | border: 1px solid #2E8B57; 53 | padding: 3px 7px 2px 7px; 54 | } 55 | 56 | .show-list th { 57 | font-size: 1.1em; 58 | text-align: left; 59 | padding-top: 5px; 60 | padding-bottom: 4px; 61 | background-color: #2E8B57; 62 | color: #ffffff; 63 | } 64 | 65 | .show-list th.button, .show-list td.button { 66 | width: 1px; 67 | } 68 | 69 | .show-list tr.alt td { 70 | color: #000000; 71 | background-color: #C1FFC1; 72 | } 73 | 74 | /* Calendar view */ 75 | .cal-view { 76 | width: 100%; 77 | border-collapse: collapse; 78 | margin-top: 10px; 79 | margin-bottom: 5px; 80 | } 81 | 82 | .cal-view td, .cal-view th { 83 | font-size: 1em; 84 | border: 1px solid #2E8B57; 85 | padding: 3px 7px 2px 7px; 86 | } 87 | 88 | .cal-view th { 89 | font-size: 1.1em; 90 | text-align: left; 91 | padding-top: 5px; 92 | padding-bottom: 4px; 93 | background-color: #2E8B57; 94 | color: #ffffff; 95 | } 96 | 97 | .cal-view tr.alt td { 98 | color: #000000; 99 | background-color: #C1FFC1; 100 | } 101 | 102 | a.user:link { 103 | color: #ffffff; 104 | } 105 | 106 | a.nav:link, a.nav:visited { 107 | color: #ffffff; 108 | text-decoration: none; 109 | padding-left: 10px; 110 | } 111 | 112 | a.create:link, a.create:visited { 113 | display: block; 114 | width: 200px; 115 | font-weight: bold; 116 | color: #FFFFFF; 117 | background-color: #2E8B57; 118 | text-align: center; 119 | padding: 4px; 120 | text-decoration: none; 121 | text-transform: uppercase; 122 | margin-top: 10px; 123 | margin-bottom: 10px; 124 | } 125 | 126 | a.action:link, a.action:visited { 127 | display: block; 128 | width: 60px; 129 | font-size: .7em; 130 | font-weight: bold; 131 | color: #FFFFFF; 132 | background-color: #2E8B57; 133 | text-align: center; 134 | padding: 4px; 135 | text-decoration: none; 136 | text-transform: uppercase; 137 | float: right; 138 | } 139 | 140 | .add-event { 141 | clear: both; 142 | font-weight: bold; 143 | padding-top: 40px; 144 | } 145 | 146 | input[type=text] { 147 | width: 80%; 148 | } 149 | 150 | input[type=submit] { 151 | display: block; 152 | width: auto; 153 | font-size: .7em; 154 | font-weight: bold; 155 | color: #FFFFFF; 156 | background-color: #2E8B57; 157 | text-align: center; 158 | padding: 4px; 159 | margin-top: 5px; 160 | text-transform: uppercase; 161 | float: left; 162 | } 163 | 164 | #content { 165 | float: left; 166 | width: 750px; 167 | margin:0 auto; 168 | } 169 | 170 | #event-details { 171 | float: left; 172 | width: auto; 173 | text-align: left; 174 | } 175 | 176 | #calendar-sidebar { 177 | display: inline; 178 | float: right; 179 | width: auto; 180 | text-align: left; 181 | border: 1px solid; 182 | margin-top: 20px; 183 | margin-right: 5px; 184 | padding-right: 10px; 185 | clear: right; 186 | } 187 | 188 | .debug-dump { 189 | clear: both; 190 | float: left; 191 | width: 100%; 192 | margin-top: 45px; 193 | word-wrap: break-word; 194 | } 195 | 196 | /* 197 | MIT License: 198 | 199 | Permission is hereby granted, free of charge, to any person obtaining 200 | a copy of this software and associated documentation files (the 201 | ""Software""), to deal in the Software without restriction, including 202 | without limitation the rights to use, copy, modify, merge, publish, 203 | distribute, sublicense, and/or sell copies of the Software, and to 204 | permit persons to whom the Software is furnished to do so, subject to 205 | the following conditions: 206 | 207 | The above copyright notice and this permission notice shall be 208 | included in all copies or substantial portions of the Software. 209 | 210 | THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 211 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 212 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 213 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 214 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 215 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 216 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 217 | */ -------------------------------------------------------------------------------- /common/footer.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 |
 7 | SESSION VARIABLES:
 8 |   $value) 
11 |     {
12 |       echo "  ".$key.": ".$value;
13 |       echo "\n";
14 |     }
15 |   ?>
16 | 
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /common/header.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | <?php echo $page['title'];?> 9 | 10 | 11 | 12 | 13 | 14 |
15 | php-calendar 16 | 20 | logout 21 | 24 |
25 | 26 | 27 | 28 | 52 | -------------------------------------------------------------------------------- /doAdd.php: -------------------------------------------------------------------------------- 1 | title."' from session."); 32 | 33 | // Add the event to the Office 365 calendar. 34 | $eventId = Office365Service::addEventToCalendar($accessToken, $event->title, $event->location, 35 | $event->startTime, $event->endTime, $attendeeString); 36 | error_log("Create returned: ".$eventId); 37 | if (SessionManager::checkResponseAndRefreshToken($eventId)) { 38 | // Pick up new access token 39 | $accessToken = $_SESSION['accessToken']; 40 | // Retry request 41 | $eventId = Office365Service::addEventToCalendar($accessToken, $event->title, $event->location, 42 | $event->startTime, $event->endTime, $attendeeString); 43 | } 44 | 45 | if (is_array($eventId) && $eventId['error']) { 46 | $msg = "Error adding event to calendar: ".$eventId['error']; 47 | error_log($msg); 48 | header("Location: ".$errorPage."?errorMsg=".urlencode($msg)); 49 | exit; 50 | } 51 | 52 | // If a voucher is required, add it as an attachment to the event. 53 | if ($event->voucherRequired) { 54 | $attachmentData = "This is your voucher for '".$event->title."' on ".date_format($event->startTime, "M j, Y"); 55 | $result = Office365Service::addAttachmentToEvent($accessToken, $eventId, $attachmentData); 56 | if (SessionManager::checkResponseAndRefreshToken($result)) { 57 | // Pick up new access token 58 | $accessToken = $_SESSION['accessToken']; 59 | // Retry request 60 | $result = Office365Service::addAttachmentToEvent($accessToken, $eventId, $attachmentData); 61 | } 62 | 63 | if ($result['error']) { 64 | $msg = "Error adding attachment to event: ".$result['error']; 65 | error_log($msg); 66 | header("Location: ".$errorPage."?errorMsg=".urlencode($msg)); 67 | exit; 68 | } 69 | } 70 | 71 | // Finally, redirect the user back to the home page. 72 | header("Location: ".$homePage); 73 | 74 | /* 75 | MIT License: 76 | 77 | Permission is hereby granted, free of charge, to any person obtaining 78 | a copy of this software and associated documentation files (the 79 | ""Software""), to deal in the Software without restriction, including 80 | without limitation the rights to use, copy, modify, merge, publish, 81 | distribute, sublicense, and/or sell copies of the Software, and to 82 | permit persons to whom the Software is furnished to do so, subject to 83 | the following conditions: 84 | 85 | The above copyright notice and this permission notice shall be 86 | included in all copies or substantial portions of the Software. 87 | 88 | THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 91 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 92 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 93 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 94 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 95 | */ 96 | ?> -------------------------------------------------------------------------------- /error.php: -------------------------------------------------------------------------------- 1 | 12 | 13 |

Unfortunately, something didn't work right.

14 |

An error occurred:

15 |

Check the PHP error log on the server for all the gory details.

16 | 17 | 43 | -------------------------------------------------------------------------------- /home.php: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 |

Welcome to php-calendar!

23 |
Here are the upcoming shows for our Shakespearean Festival.
24 |
Upcoming Shows
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | getEventList(); 46 | $_SESSION['events'] = $eventList; 47 | } 48 | 49 | $altRow = false; 50 | 51 | foreach($eventList as $index => $event) { 52 | ?> 53 | > 54 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 76 | 77 |
PerformanceLocationVoucher Required?DateStartEnd
title ?>location ?>voucherRequired ? "Yes" : "No") ?>startTime, "M j, Y") ?>startTime, "g:i a") ?>endTime, "g:i a") ?>
78 | 79 | 83 | 84 | -------------------------------------------------------------------------------- /loc/readme-ja.md: -------------------------------------------------------------------------------- 1 | # PHP 予定表 API のサンプル # 2 | 3 | [日本 (日本語)](https://github.com/jasonjoh/php-calendar/blob/master/loc/readme-ja.md) (日本語) 4 | 5 | このサンプルは、PHP の[予定表 API](https://msdn.microsoft.com/office/office365/APi/calendar-rest-operations) を使用する方法を示しています。サンプル アプリは、架空のコミュニティ劇場のシェークスピア祭の「今後の公演」アプリです。ユーザーは、Office 365 アカウントに接続し、参加する公演について予定表にイベントを追加することができます。ユーザーには、友人を招待して、招待した友人それぞれに会議出席依頼を送信するオプションがあります。 6 | 7 | ## 使用する API の機能 ## 8 | 9 | - ユーザーの既定の予定表にイベントを作成する 10 | - イベントに添付ファイルを追加する 11 | - イベントに出席者を追加する 12 | - [予定表ビュー](https://msdn.microsoft.com/office/office365/APi/calendar-rest-operations#GetCalendarView)を使用して、定期的なイベントを展開し、1 日のすべての予定を表示します。 13 | 14 | ## 必要なソフトウェア ## 15 | 16 | - [PHP 5.6](http://php.net/downloads.php) 17 | - PHP に対応可能な Web サーバーです。 18 | 19 | 私のテストでは、Windows 8.1 ノート PC にインストールされている IIS 8 を使用しました。[Web Platform インストーラー](http://www.microsoft.com/web/downloads/platform.aspx) (Windows/IIS のみ) を使用して、PHP 5.6.0 をインストールしました。 20 | 21 | ## サンプルの実行 ## 22 | 23 | 開始する前に PHP がインストールされていること、および Web サーバーが処理用に構成され、サーバーの PHP ファイルが構成されていることが前提となっています。 24 | 25 | 1. サンプル プロジェクトをダウンロードまたは分岐します。 26 | 1. `php-calendar` という Web の root ディレクトリに新しいディレクトリを作成します。このディレクトリに、リポジトリからファイルをコピーします。 27 | 1. [Azure Active Directory にアプリを登録](https://github.com/jasonjoh/office365-azure-guides/blob/master/RegisterAnAppInAzure.md)します。アプリは、サインオン URL が `http://localhost/php-calendar` の Web アプリとして登録されている必要があります。また、[ユーザーの予定表へのフル アクセス] のアクセス許可が付与されている必要があります。このアクセス許可は、[代理アクセス許可] ドロップダウン リストから使用できます。 28 | 1. `.\o365\ClientReg.php` ファイルを編集します。 29 | 1. アプリの登録時に取得したアプリのクライアント ID をコピーし、`$clientId` 変数の値として貼り付けます。 30 | 1. アプリの登録時に作成したキーをコピーし、`$clientSecret` 変数の値として貼り付けます。 31 | 1. ファイルを保存します。 32 | 1. PHP のインストールが SSL を検証するための更新された CA 証明書以外で構成されている場合、サーバーで Fiddler を実行し、かつ `Office365Service.php` で変数 `$enableFiddler` を `true` に設定するというのでない限り、要求は失敗します。代わりに、`curl_exec` への呼び出しの直前に次の行を挿入することができます。**ただし、** そのようにすると、SSL 検証がすべて無効になるため、運用環境では実行しないほうがよいことに注意してください。 33 | 34 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); 35 | 1. Web ブラウザーを開き、`http://localhost/php-calendar/home.php` を参照します。 36 | 1. 今後のさまざまなシェークスピア演劇の上演時間が一覧表示されていることが分かります。いずれかの [予定表に接続] ボタンをクリックすると、Office 365 にサインインします。 37 | 1. サインインすると、ホーム ページにリダイレクトされ、ボタンは [予定表に追加] に変わります。特定の上演時間の横にあるボタンをクリックすると、その時間が予定表に追加されます。[はい] の [伝票が必要] フィールドがあるイベントには、イベントの添付ファイルとして伝票が含まれています。 38 | 39 | ## 著作権 ## 40 | 41 | Copyright (c) Microsoft. All rights reserved. 42 | 43 | ---------- 44 | Twitter ([@JasonJohMSFT](https://twitter.com/JasonJohMSFT)) をぜひフォローしてください。 45 | 46 | [Exchange 開発ブログ](http://blogs.msdn.com/b/exchangedev/)をフォローする 47 | 48 | -------------------------------------------------------------------------------- /logout.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /o365/ClientReg.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /o365/Office365Service.php: -------------------------------------------------------------------------------- 1 | "authorization_code", 40 | "code" => $authCode, 41 | "redirect_uri" => $redirectUri, 42 | "resource" => "https://outlook.office365.com/", 43 | "client_id" => ClientReg::$clientId, 44 | "client_secret" => ClientReg::$clientSecret 45 | ); 46 | 47 | // Calling http_build_query is important to get the data 48 | // formatted as Azure expects. 49 | $token_request_body = http_build_query($token_request_data); 50 | error_log("Request body: ".$token_request_body); 51 | 52 | $curl = curl_init(self::$authority.self::$tokenUrl); 53 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 54 | curl_setopt($curl, CURLOPT_POST, true); 55 | curl_setopt($curl, CURLOPT_POSTFIELDS, $token_request_body); 56 | 57 | if (self::$enableFiddler) { 58 | // ENABLE FIDDLER TRACE 59 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); 60 | // SET PROXY TO FIDDLER PROXY 61 | curl_setopt($curl, CURLOPT_PROXY, "127.0.0.1:8888"); 62 | } 63 | 64 | $response = curl_exec($curl); 65 | error_log("curl_exec done."); 66 | 67 | $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); 68 | error_log("Request returned status ".$httpCode); 69 | if (self::isFailure($httpCode)) { 70 | return array('errorNumber' => $httpCode, 71 | 'error' => 'Token request returned HTTP error '.$httpCode); 72 | } 73 | 74 | // Check error 75 | $curl_errno = curl_errno($curl); 76 | $curl_err = curl_error($curl); 77 | if ($curl_errno) { 78 | $msg = $curl_errno.": ".$curl_err; 79 | error_log("CURL returned an error: ".$msg); 80 | return array('errorNumber' => $curl_errno, 81 | 'error' => $msg); 82 | } 83 | 84 | curl_close($curl); 85 | 86 | // The response is a JSON payload, so decode it into 87 | // an array. 88 | $json_vals = json_decode($response, true); 89 | error_log("TOKEN RESPONSE:"); 90 | foreach ($json_vals as $key=>$value) { 91 | error_log(" ".$key.": ".$value); 92 | } 93 | 94 | return $json_vals; 95 | } 96 | 97 | // Sends a request to the token endpoint to get a new access token 98 | // from a refresh token. 99 | public static function getTokenFromRefreshToken($refreshToken) { 100 | // Build the form data to post to the OAuth2 token endpoint 101 | $token_request_data = array( 102 | "grant_type" => "refresh_token", 103 | "refresh_token" => $refreshToken, 104 | "resource" => "https://outlook.office365.com/", 105 | "client_id" => ClientReg::$clientId, 106 | "client_secret" => ClientReg::$clientSecret 107 | ); 108 | 109 | $token_request_body = http_build_query($token_request_data); 110 | error_log("Request body: ".$token_request_body); 111 | 112 | $curl = curl_init(self::$authority.self::$tokenUrl); 113 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 114 | curl_setopt($curl, CURLOPT_POST, true); 115 | curl_setopt($curl, CURLOPT_POSTFIELDS, $token_request_body); 116 | 117 | if (self::$enableFiddler) { 118 | // ENABLE FIDDLER TRACE 119 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); 120 | // SET PROXY TO FIDDLER PROXY 121 | curl_setopt($curl, CURLOPT_PROXY, "127.0.0.1:8888"); 122 | } 123 | 124 | $response = curl_exec($curl); 125 | error_log("curl_exec done."); 126 | 127 | $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); 128 | error_log("Request returned status ".$httpCode); 129 | if (self::isFailure($httpCode)) { 130 | return array('errorNumber' => $httpCode, 131 | 'error' => 'Token request returned HTTP error '.$httpCode); 132 | } 133 | 134 | // Check error 135 | $curl_errno = curl_errno($curl); 136 | $curl_err = curl_error($curl); 137 | if ($curl_errno) { 138 | $msg = $curl_errno.": ".$curl_err; 139 | error_log("CURL returned an error: ".$msg); 140 | return array('errorNumber' => $curl_errno, 141 | 'error' => $msg); 142 | } 143 | 144 | curl_close($curl); 145 | 146 | // The response is a JSON payload, so decode it into 147 | // an array. 148 | $json_vals = json_decode($response, true); 149 | error_log("TOKEN RESPONSE:"); 150 | foreach ($json_vals as $key=>$value) { 151 | error_log(" ".$key.": ".$value); 152 | } 153 | 154 | return $json_vals; 155 | } 156 | 157 | // Parses an ID token returned from Azure to get the user's 158 | // display name. 159 | public static function getUserName($id_token) { 160 | $token_parts = explode(".", $id_token); 161 | 162 | // First part is header, which we ignore 163 | // Second part is JWT, which we want to parse 164 | error_log("getUserName found id token: ".$token_parts[1]); 165 | 166 | // First, in case it is url-encoded, fix the characters to be 167 | // valid base64 168 | $encoded_token = str_replace('-', '+', $token_parts[1]); 169 | $encoded_token = str_replace('_', '/', $encoded_token); 170 | error_log("After char replace: ".$encoded_token); 171 | 172 | // Next, add padding if it is needed. 173 | switch (strlen($encoded_token) % 4){ 174 | case 0: 175 | // No pad characters needed. 176 | error_log("No padding needed."); 177 | break; 178 | case 2: 179 | $encoded_token = $encoded_token."=="; 180 | error_log("Added 2: ".$encoded_token); 181 | break; 182 | case 3: 183 | $encoded_token = $encoded_token."="; 184 | error_log("Added 1: ".$encoded_token); 185 | break; 186 | default: 187 | // Invalid base64 string! 188 | error_log("Invalid base64 string"); 189 | return null; 190 | } 191 | 192 | $json_string = base64_decode($encoded_token); 193 | error_log("Decoded token: ".$json_string); 194 | $jwt = json_decode($json_string, true); 195 | error_log("Found user name: ".$jwt['name']); 196 | return $jwt['name']; 197 | } 198 | 199 | // Uses the Calendar API's CalendarView to get all events 200 | // on a specific day. CalendarView handles expansion of recurring items. 201 | public static function getEventsForDate($access_token, $date) { 202 | error_log("getEventsForDate called:"); 203 | error_log(" access token: ".$access_token); 204 | error_log(" date: ".date_format($date, "M j, Y g:i a (e)")); 205 | 206 | // Set the start of our view window to midnight of the specified day. 207 | $windowStart = $date->setTime(0,0,0); 208 | $windowStartUrl = self::encodeDateTime($windowStart); 209 | error_log(" Window start (UTC): ".$windowStartUrl); 210 | 211 | // Add one day to the window start time to get the window end. 212 | $windowEnd = $windowStart->add(new DateInterval("P1D")); 213 | $windowEndUrl = self::encodeDateTime($windowEnd); 214 | error_log(" Window end (UTC): ".$windowEndUrl); 215 | 216 | // Build the API request URL 217 | $calendarViewUrl = self::$outlookApiUrl."/Me/CalendarView?" 218 | ."startDateTime=".$windowStartUrl 219 | ."&endDateTime=".$windowEndUrl 220 | ."&\$select=Subject,Start,End" // Use $select to limit the data returned 221 | ."&\$orderby=Start"; // Sort the results by the start time. 222 | 223 | return self::makeApiCall($access_token, "GET", $calendarViewUrl); 224 | } 225 | 226 | // Use the Calendar API to add an event to the default calendar. 227 | public static function addEventToCalendar($access_token, $subject, $location, $startTime, $endTime, $attendeeString) { 228 | // Create a static body. 229 | $htmlBody = "Added by php-calendar app."; 230 | 231 | // Generate the JSON payload 232 | $event = array( 233 | "Subject" => $subject, 234 | "Location" => array("DisplayName" => $location), 235 | "Start" => self::encodeDateTime($startTime), 236 | "End" => self::encodeDateTime($endTime), 237 | "Body" => array("ContentType" => "HTML", "Content" => $htmlBody) 238 | ); 239 | 240 | if (!is_null($attendeeString) && strlen($attendeeString) > 0) { 241 | error_log("Attendees included: ".$attendeeString); 242 | 243 | $attendeeAddresses = array_filter(explode(';', $attendeeString)); 244 | 245 | $attendees = array(); 246 | foreach($attendeeAddresses as $address) { 247 | error_log("Adding ".$address); 248 | 249 | $attendee = array( 250 | "EmailAddress" => array ("Address" => $address), 251 | "Type" => "Required" 252 | ); 253 | 254 | $attendees[] = $attendee; 255 | } 256 | 257 | $event["Attendees"] = $attendees; 258 | } 259 | 260 | $eventPayload = json_encode($event); 261 | error_log("EVENT PAYLOAD: ".$eventPayload); 262 | 263 | $createEventUrl = self::$outlookApiUrl."/Me/Events"; 264 | 265 | $response = self::makeApiCall($access_token, "POST", $createEventUrl, $eventPayload); 266 | 267 | // If the call succeeded, the response should be a JSON representation of the 268 | // new event. Try getting the Id property and return it. 269 | if ($response['Id']) { 270 | return $response['Id']; 271 | } 272 | 273 | else { 274 | error_log("ERROR: ".$response); 275 | return $response; 276 | } 277 | } 278 | 279 | // Use the Calendar API to add an attachment to an event. 280 | public static function addAttachmentToEvent($access_token, $eventId, $attachmentData) { 281 | // Generate the JSON payload 282 | $attachment = array( 283 | "@odata.type" => "#Microsoft.OutlookServices.FileAttachment", 284 | "Name" => "voucher.txt", 285 | "ContentBytes" => base64_encode($attachmentData) 286 | ); 287 | 288 | $attachmentPayload = json_encode($attachment); 289 | error_log("ATTACHMENT PAYLOAD: ".$attachmentPayload); 290 | 291 | $createAttachmentUrl = self::$outlookApiUrl."/Me/Events/".$eventId."/Attachments"; 292 | 293 | return self::makeApiCall($access_token, "POST", $createAttachmentUrl, $attachmentPayload); 294 | } 295 | 296 | // Make an API call. 297 | public static function makeApiCall($access_token, $method, $url, $payload = NULL) { 298 | // Generate the list of headers to always send. 299 | $headers = array( 300 | "User-Agent: php-calendar/1.0", // Sending a User-Agent header is a best practice. 301 | "Authorization: Bearer ".$access_token, // Always need our auth token! 302 | "Accept: application/json", // Always accept JSON response. 303 | "client-request-id: ".self::makeGuid(), // Stamp each new request with a new GUID. 304 | "return-client-request-id: true" // Tell the server to include our request-id GUID in the response. 305 | ); 306 | 307 | $curl = curl_init($url); 308 | 309 | if (self::$enableFiddler) { 310 | // ENABLE FIDDLER TRACE 311 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); 312 | // SET PROXY TO FIDDLER PROXY 313 | curl_setopt($curl, CURLOPT_PROXY, "127.0.0.1:8888"); 314 | } 315 | 316 | switch(strtoupper($method)) { 317 | case "GET": 318 | // Nothing to do, GET is the default and needs no 319 | // extra headers. 320 | error_log("Doing GET"); 321 | break; 322 | case "POST": 323 | error_log("Doing POST"); 324 | // Add a Content-Type header (IMPORTANT!) 325 | $headers[] = "Content-Type: application/json"; 326 | curl_setopt($curl, CURLOPT_POST, true); 327 | curl_setopt($curl, CURLOPT_POSTFIELDS, $payload); 328 | break; 329 | case "PATCH": 330 | error_log("Doing PATCH"); 331 | // Add a Content-Type header (IMPORTANT!) 332 | $headers[] = "Content-Type: application/json"; 333 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PATCH"); 334 | curl_setopt($curl, CURLOPT_POSTFIELDS, $payload); 335 | break; 336 | case "DELETE": 337 | error_log("Doing DELETE"); 338 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); 339 | break; 340 | default: 341 | error_log("INVALID METHOD: ".$method); 342 | exit; 343 | } 344 | 345 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 346 | curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 347 | $response = curl_exec($curl); 348 | error_log("curl_exec done."); 349 | 350 | $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); 351 | error_log("Request returned status ".$httpCode); 352 | 353 | if (self::isFailure($httpCode)) { 354 | return array('errorNumber' => $httpCode, 355 | 'error' => 'Request returned HTTP error '.$httpCode); 356 | } 357 | 358 | $curl_errno = curl_errno($curl); 359 | $curl_err = curl_error($curl); 360 | 361 | if ($curl_errno) { 362 | $msg = $curl_errno.": ".$curl_err; 363 | error_log("CURL returned an error: ".$msg); 364 | curl_close($curl); 365 | return array('errorNumber' => $curl_errno, 366 | 'error' => $msg); 367 | } 368 | else { 369 | error_log("Response: ".$response); 370 | curl_close($curl); 371 | return json_decode($response, true); 372 | } 373 | } 374 | 375 | // This function convert a dateTime from local TZ to UTC, then 376 | // encodes it in the format expected by the Outlook APIs. 377 | public static function encodeDateTime($dateTime) { 378 | $utcDateTime = $dateTime->setTimeZone(new DateTimeZone("UTC")); 379 | 380 | $dateFormat = "Y-m-d\TH:i:s\Z"; 381 | return date_format($utcDateTime, $dateFormat); 382 | } 383 | 384 | // This function generates a random GUID. 385 | public static function makeGuid(){ 386 | if (function_exists('com_create_guid')) { 387 | error_log("Using 'com_create_guid'."); 388 | return strtolower(trim(com_create_guid(), '{}')); 389 | } 390 | else { 391 | error_log("Using custom GUID code."); 392 | $charid = strtolower(md5(uniqid(rand(), true))); 393 | $hyphen = chr(45); 394 | $uuid = substr($charid, 0, 8).$hyphen 395 | .substr($charid, 8, 4).$hyphen 396 | .substr($charid, 12, 4).$hyphen 397 | .substr($charid, 16, 4).$hyphen 398 | .substr($charid, 20, 12); 399 | 400 | return $uuid; 401 | } 402 | } 403 | 404 | public static function isFailure($httpStatus){ 405 | // Simplistic check for failure HTTP status 406 | return ($httpStatus >= 400); 407 | } 408 | } 409 | 410 | /* 411 | MIT License: 412 | 413 | Permission is hereby granted, free of charge, to any person obtaining 414 | a copy of this software and associated documentation files (the 415 | ""Software""), to deal in the Software without restriction, including 416 | without limitation the rights to use, copy, modify, merge, publish, 417 | distribute, sublicense, and/or sell copies of the Software, and to 418 | permit persons to whom the Software is furnished to do so, subject to 419 | the following conditions: 420 | 421 | The above copyright notice and this permission notice shall be 422 | included in all copies or substantial portions of the Software. 423 | 424 | THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 425 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 426 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 427 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 428 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 429 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 430 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 431 | */ 432 | ?> 433 | -------------------------------------------------------------------------------- /o365/authorize.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sessionManager.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /site-event-list.php: -------------------------------------------------------------------------------- 1 | setTime(19, 0, 0); 32 | error_log("Current time adjusted: ".date_format($currentTime, "m/d/Y H:i")); 33 | 34 | // Intervals for date manipulation 35 | $oneDay = new DateInterval("P1D"); 36 | $playDuration = new DateInterval("PT3H"); 37 | 38 | foreach ($this->_eventTitles as $title) { 39 | $event = new Event; 40 | $event->title = $title; 41 | $event->location = $this->_eventLocations[mt_rand(0,sizeof($this->_eventLocations)-1)]; 42 | $event->voucherRequired = mt_rand(0,1) == 1; 43 | 44 | $event->startTime = $currentTime->add($oneDay); 45 | error_log("Start time: ".date_format($event->startTime, "m/d/Y H:i")); 46 | $event->endTime = $event->startTime->add($playDuration); 47 | error_log("End time: ".date_format($event->endTime, "m/d/Y H:i")); 48 | $eventList[] = $event; 49 | 50 | $currentTime = $currentTime->add($oneDay); 51 | } 52 | 53 | return $eventList; 54 | } 55 | } 56 | 57 | class Event { 58 | public $title; 59 | public $location; 60 | public $voucherRequired; 61 | public $startTime; 62 | public $endTime; 63 | } 64 | 65 | /* 66 | MIT License: 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining 69 | a copy of this software and associated documentation files (the 70 | ""Software""), to deal in the Software without restriction, including 71 | without limitation the rights to use, copy, modify, merge, publish, 72 | distribute, sublicense, and/or sell copies of the Software, and to 73 | permit persons to whom the Software is furnished to do so, subject to 74 | the following conditions: 75 | 76 | The above copyright notice and this permission notice shall be 77 | included in all copies or substantial portions of the Software. 78 | 79 | THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 80 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 81 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 82 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 83 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 84 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 85 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 86 | */ 87 | ?> --------------------------------------------------------------------------------