{"id":55000410,"date":"2023-12-01T00:00:00","date_gmt":"2024-01-11T11:34:02","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=410"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Google_Calendar_per_RestAPI_programmieren","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/Google_Calendar_per_RestAPI_programmieren\/","title":{"rendered":"Google Calendar per Rest-API programmieren"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg08.met.vgwort.de\/na\/accb682049e549c3944a34ca7fe7971a\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Google-Kalender sind praktisch: Sie sind von &uuml;berall erreichbar, k&ouml;nnen mit endlos vielen Schnittstellen und Diensten verbunden werden und bieten weitere Vorteile. Noch sch&ouml;ner w&auml;re es nat&uuml;rlich, wenn wir auch per VBA auf diese Kalender zugreifen k&ouml;nnten. Die Vorbereitungen haben wir bereits in zwei weiteren Artikeln erledigt &#8211; damit haben wir eine App bei Google angelegt und eine COM-DLL programmiert, mit der wir ein Token f&uuml;r die Authentifizierung bei Google generieren k&ouml;nnen. Damit folgt nun die K&uuml;r: Wir erzeugen VBA-Prozeduren, um auf die verschiedenen Informationen der Google Calendar API zuzugreifen. Dazu geh&ouml;ren Kalender, Termine und vieles mehr. Welche M&ouml;glichkeiten sich zum Lesen, Schreiben, &Auml;ndern und L&ouml;schen von Terminen bieten, zeigen wir auf den folgenden Seiten.<\/b><\/p>\n<h2>Keine Rest-API ohne JSON<\/h2>\n<p>Die Kommunikation mit der Rest-API des Google Calendars erfolgt wie bei den meisten Rest-APIs &uuml;ber JSON. Wir haben dazu bereits eine Bibliothek vorgestellt, die uns den Zugriff auf die in JSON-Dateien enthaltenen Informationen unter VBA stark vereinfacht. Alles Weitere dazu liest Du im Artikel <b>Mit JSON arbeiten <\/b>(<b>www.vbentwickler.de\/361<\/b>).<\/p>\n<h2>Vorbereitungen<\/h2>\n<p>Wie schon in der Einleitung erw&auml;hnt, sind zwei wichtige Schritte n&ouml;tig, bevor wir mit der eigentlichen Programmierung des Rest-API-Zugriffs beginnen k&ouml;nnen. Jeder wird in einem eigenen Artikel abgehandelt:<\/p>\n<ul>\n<li><b>Google Calendar programmieren: Vorbereitungen <\/b>(<b>www.vbentwickler.de\/408<\/b>): Hier liest Du, wie Du eine App bei Google anlegst und die Client-ID und das Client-Secret ermittelst, das f&uuml;r das Ermitteln eines Tokens f&uuml;r den Zugriff auf die Rest-API erforderlich ist.<\/li>\n<li><b>Google-Token per DLL holen <\/b>(<b>www.vbentwickler.de\/409<\/b>): Hier zeigen wir, wie Du eine COM-DLL programmierst, mit der Du auf Basis der Client-ID und des Client-Secret ein Access-Token f&uuml;r den Zugriff auf den Kalender eines Benutzers sowie ein Refresh-Token f&uuml;r das schnelle Aktualisieren des Access-Tokens generieren kannst.<\/li>\n<\/ul>\n<p>Au&szlig;erdem f&uuml;gen wir der Anwendung, egal, ob es sich um eine Excel-, Outlook-, Access- oder twinBASIC-Anwendung handelt, neben dem Verweis auf die COM-DLL <b>amvGoogleOAuth2 <\/b>noch zwei weitere wichtige Verweise hinzu. Dabei handelt es sich um <b>Microsoft Scripting Runtime <\/b>und <b>Microsoft XML, v6.0<\/b> (siehe Bild 1). Dies ist das Verweise-Fenster des VBA-Editors, unter twinBASIC sieht dies etwas anders aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2023_06\/pic_410_008.png\" alt=\"Verweise f&uuml;r die L&ouml;sung\" width=\"499,6267\" height=\"393,8742\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Verweise f&uuml;r die L&ouml;sung<\/span><\/b><\/p>\n<p>Au&szlig;erdem f&uuml;gen wir dem Projekt die beiden Module <b>mdlJSON <\/b>und <b>mdlJSONDOM <\/b>hinzu. Diese enthalten die Elemente zum Parsen und Lesen der gelieferten JSON-Dokumente.<\/p>\n<p>Im Modul <b>mdlClipboard <\/b>finden wir die Funktion <b>InZwischenablage<\/b>, die immer praktisch ist, um l&auml;ngere JSON-Dokumente in die Zwischenablage zu kopieren, um diese dann beispielsweise formatiert in Visual Studio einzuf&uuml;gen und anzuzeigen.<\/p>\n<p>Das Modul <b>mdlTools <\/b>enth&auml;lt aktuell nur die Funktion <b>SQLDatum<\/b>, mit dem wir Datumsangabe in das SQL-Format &uuml;bertragen k&ouml;nnen.<\/p>\n<p>Das Modul <b>mdlRegistry <\/b>enth&auml;lt einige Funktionen, mit denen wir Daten wie die ClientID, das ClientSecret, das Access-Token und das Refresh-Token in der Registry speichern und wieder auslesen k&ouml;nnen.<\/p>\n<p>Diese beschreiben wir im Detail im Artikel <b>Anwendungsdaten in der Registry <\/b>(<b>www.vbentwickler.de\/411<\/b>).<\/p>\n<p>Das Modul <b>mdlAuthentication <\/b>enth&auml;lt die Prozeduren, mit denen wir das Access-Token f&uuml;r die Authentifizierung unserer Anfragen an die Rest-API von Google generieren.<\/p>\n<p>Das Modul <b>mdlGoogleCalendar <\/b>enth&auml;lt schlie&szlig;lich die eigentlichen Prozeduren, um auf die Daten des Google-Kalenders zuzugreifen.<\/p>\n<h2>Client-ID und Client-Secret holen und speichern<\/h2>\n<p>Im Artikel <b>Google Calendar programmieren: Vorbereitungen <\/b>(<b>www.vbentwickler.de\/408<\/b>) beschreiben wir, wie Du die Client-ID und das Client-Secret &uuml;ber die Webseite von Google holst und dazu auch eine App anlegst. Die beiden Daten sind wichtige Voraussetzungen f&uuml;r das Erhalten eines Access-Tokens und damit letztlich f&uuml;r den Zugriff auf die Rest-API von Google.<\/p>\n<p>Im Modul <b>mdlAuthentication <\/b>findest Du eine Prozedur namens <b>SetClientData<\/b>, mit der Du die gewonnenen Daten f&uuml;r Client-ID und Client-Secret in die Registry schreibst, von wo weitere Prozeduren sich diese Informationen im weiteren Verlauf holen. Wir k&ouml;nnen diese zwar auch in Form von Konstanten direkt im Modul speichern, aber da die weiteren ben&ouml;tigten Informationen wie das Access-Token und das Refresh-Token sich gelegentlich &auml;ndern und wir diese deshalb in der Registry speichern wollen, legen wir dort auch gleich Eintr&auml;ge f&uuml;r Client-ID und Client-Secret an.<\/p>\n<p>Wir tragen die entsprechenden Werte also in den folgenden Zeilen f&uuml;r die Platzhalter <b>xxxxxx <\/b>und <b>yyyyyy <\/b>ein und rufen dann die Prozedur auf:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>SetClientData()\r\n     SaveAppSetting \"ClientID\", \"xxxxxx\"\r\n     SaveAppSetting \"ClientSecret\", \"yyyyyy\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese landen dann wie in Bild 2 in der Registry.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2023_06\/pic_410_009.png\" alt=\"Informationen in der Registry\" width=\"649,627\" height=\"322,045\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Informationen in der Registry<\/span><\/b><\/p>\n<h2>Token f&uuml;r den Zugriff auf die Rest-API holen<\/h2>\n<p>Google liefert zwei Arten von Token:<\/p>\n<ul>\n<li>Access-Token: Das eigentliche Token, welches wir f&uuml;r den Zugriff auf die Rest-API von Google nutzen. Dieses  ist in der Regel nur f&uuml;r 60 Minuten g&uuml;ltig.<\/li>\n<li>Refresh-Token: Langlebiges Token, mit dem wir das Access-Token aktualisieren k&ouml;nnen.<\/li>\n<\/ul>\n<p>Mit der Prozedur <b>GetTokens <\/b>holen wir beide Token und speichern diese in weiteren Eintr&auml;gen in der Registry. Die Prozedur ermittelt als Erstes die Werte der Registry-Eintr&auml;ge <b>ClientID <\/b>und <b>ClientSecret <\/b>und gibt entsprechende Meldungen aus, wenn diese nicht aus der Registry eingelesen werden k&ouml;nnen.<\/p>\n<p>Sind beide Werte vorhanden, ruft die Prozedur eine weitere Prozedur namens <b>GetTokensDLL <\/b>auf. Diese erwartet die Client-ID, das Client-Secret und weitere leere Variablen als Parameter, die mit dem Access-Token, dem Refresh-Token und der Haltbarkeit des Access-Tokens in Sekunden gef&uuml;llt werden sollen. Liefert diese Funktion den Wert <b>True <\/b>zur&uuml;ck, tr&auml;gt die aufrufende Prozedur die Werte der nun gef&uuml;llten Parameter <b>strRefreshToken<\/b>, <b>strAccessToken <\/b>und <b>lngExpiresInSeconds <\/b>unter den Namen <b>RefreshToken<\/b>, <b>AccessToken <\/b>und <b>TokenExpiresAt <\/b>in die Registry ein. Dabei berechnet sie f&uuml;r den Eintrag <b>TokenExpiresAt <\/b>die aktuelle Zeit plus die Anzahl der Sekunden aus <b>lngExpiresInSeconds<\/b> (siehe Listing 1).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>GetTokens()\r\n     <span style=\"color:blue;\">Dim <\/span>strClientID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strClientSecret<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strRefreshToken<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strAccessToken<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngExpiresInSeconds<span style=\"color:blue;\"> As Long<\/span>\r\n     strClientID = GetAppSetting(\"ClientID\")\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strClientID) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Keine ClientID in der Registry gefunden. Verwende die Prozedur SetClientData.\", vbOKOnly + _\r\n             vbExclamation, \"ClientID fehlt\"\r\n             <span style=\"color:blue;\">Exit Sub<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     strClientSecret = GetAppSetting(\"ClientSecret\")\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strClientSecret) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Kein ClientSecret in der Registry gefunden. Verwende die Prozedur SetClientData.\", vbOKOnly + _\r\n             vbExclamation, \"ClientSecret fehlt\"\r\n            <span style=\"color:blue;\">Exit Sub<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span>GetTokensDLL(strClientID, strClientSecret, strRefreshToken, strAccessToken, lngExpiresInSeconds)<span style=\"color:blue;\"> Then<\/span>\r\n         SaveAppSetting \"RefreshToken\", strRefreshToken\r\n         SaveAppSetting \"AccessToken\", strAccessToken\r\n         SaveAppSetting \"TokenExpiresAt\", DateAdd(\"s\", lngExpiresInSeconds, Now)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Die Prozedur GetTokens ruft die Funktion GetTokensDLL auf.<\/span><\/b><\/p>\n<p>Die Funktion <b>GetTokensDLL <\/b>deklariert eine Objektvariable auf Basis der Klasse <b>amvGoogleOAuth2.GoogleOAuth2 <\/b>aus der per Verweis verf&uuml;gbar gemachten DLL <b>amvGoogleOAuth2<\/b> und erstellt eine neue Instanz dieser Klasse (siehe Listing 2).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetTokensDLL(strClientID<span style=\"color:blue;\"> As String<\/span>, _\r\n         strClientSecret<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> strRefreshToken<span style=\"color:blue;\"> As String<\/span>, _\r\n         <span style=\"color:blue;\">Optional<\/span> strAccessToken<span style=\"color:blue;\"> As String<\/span>, _\r\n         <span style=\"color:blue;\">Optional<\/span> lngExpiresInSeconds<span style=\"color:blue;\"> As Long<\/span>) _\r\n    <span style=\"color:blue;\"> As String<\/span>\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">Dim <\/span>objGoogleOAuth2<span style=\"color:blue;\"> As <\/span>amvGoogleOAuth2.GoogleOAuth2\r\n         <span style=\"color:blue;\">Set<\/span> objGoogleOAuth2 = <span style=\"color:blue;\">New<\/span> amvGoogleOAuth2.GoogleOAuth2\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Err.Number = 0<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">MsgBox<\/span> \"Die .NET-DLL amvGoogleOAuth2.dll ist nicht korrekt installiert.\" & <span style=\"color:blue;\">vbCrLf<\/span> & <span style=\"color:blue;\">vbCrLf<\/span> & Err.Number _\r\n                 & \" \" & Err.Description\r\n             <span style=\"color:blue;\">Exit Function<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n         <span style=\"color:blue;\">With<\/span> objGoogleOAuth2\r\n             .ClientID = strClientID\r\n             .ClientSecret = strClientSecret\r\n             <span style=\"color:blue;\">If <\/span>.GetTokens(strRefreshToken, strAccessToken, lngExpiresInSeconds) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n                 GetTokensDLL = <span style=\"color:blue;\">True<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         End <span style=\"color:blue;\">With<\/span>\r\n     End Function<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Die Funktion GetTokensDLL ermittelt per DLL das Access-Token, das Refresh-Token und die G&uuml;ltigkeitsdauer.<\/span><\/b><\/p>\n<p>Sie weist den Eigenschaften <b>ClientId <\/b>und <b>ClientSecret <\/b>die Werte aus <b>strClientID <\/b>und <b>strClientSecret <\/b>zu und ruft dann die Funktion <b>GetTokens <\/b>auf, um das Refresh-Token, das Access-Token und die G&uuml;ltigkeitsdauer des Access-Tokens zu ermitteln.<\/p>\n<p>War dieser Aufruf erfolgreich, liefert die Funktion <b>GetTokensDLL <\/b>den Wert <b>True <\/b>zur&uuml;ck, sodass die aufrufende Prozedur <b>GetTokens <\/b>die Werte der Parameter <b>strRefreshToken<\/b>, <b>strAccessToken <\/b>und <b>lngExpiresInSeconds <\/b>wie oben beschrieben in der Registry speichern kann (siehe Bild 3).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2023_06\/pic_410_010.png\" alt=\"Vervollst&auml;ndigung der Informationen in der Registry\" width=\"649,627\" height=\"290,6711\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Vervollst&auml;ndigung der Informationen in der Registry<\/span><\/b><\/p>\n<h2>Access-Token aktualieren<\/h2>\n<p>Das Access-Token ist genau eine Stunde g&uuml;ltig, und die Stunde ist manchmal schneller herum als man denkt. Deshalb ben&ouml;tigen wir noch eine weitere Funktion, mit der wir das Access-Token aktualisieren k&ouml;nnen. Diese ben&ouml;tigt nicht mehr die COM-DLL, sondern wir kommen hier mit einem direkten Aufruf der Rest-API von Google aus.<\/p>\n<p>Wir verwenden wieder eine Prozedur und eine Funktion, wobei die Funktion den eigentlichen Zugriff auf die Rest-API kapselt. Die erste Prozedur hei&szlig;t <b>RefreshAccessToken <\/b>und k&uuml;mmert sich um das Zusammenstellen der erforderlichen Daten und um die Auswertung des Ergebnisses der Funktion <b>RefreshAccessTokenRestAPI<\/b>. Sie ermittelt als Erstes die Werte f&uuml;r die Client-ID, das Client-Secret und das aktuelle Refresh-Token aus der Registry (siehe Listing 3).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>RefreshAccessToken()\r\n     <span style=\"color:blue;\">Dim <\/span>strClientID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strClientSecret<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngExpiresIn<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strRefreshToken<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strAccessToken<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngStatus<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strErrorTitle<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strErrorDescription<span style=\"color:blue;\"> As String<\/span>\r\n     strClientID = GetAppSetting(\"ClientID\")\r\n     strClientSecret = GetAppSetting(\"ClientSecret\")\r\n     strRefreshToken = GetAppSetting(\"RefreshToken\")\r\n     If RefreshAccessTokenRestAPI(strClientID, strClientSecret, strRefreshToken, strAccessToken, lngExpiresIn, _\r\n             lngStatus, strErrorTitle, strErrorDescription) = <span style=\"color:blue;\">True<\/span> Then\r\n         SaveAppSetting \"AccessToken\", strAccessToken\r\n         SaveAppSetting \"TokenExpiresAt\", DateAdd(\"s\", lngExpiresIn, Now)\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Status: \" & lngStatus & <span style=\"color:blue;\">vbCrLf<\/span> & <span style=\"color:blue;\">vbCrLf<\/span> & \"Fehler: \" & strErrorTitle & <span style=\"color:blue;\">vbCrLf<\/span> & <span style=\"color:blue;\">vbCrLf<\/span> _\r\n             & \"Beschreibung: \" & strErrorDescription, <span style=\"color:blue;\">vbCr<\/span>itical + vbOKOnly, \"Fehler bei Refresh Token\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Die Prozedur RefreshAccessToken startet die Aktualisierung des Access-Tokens.<\/span><\/b><\/p>\n<p>Dann ruft die die Funktion <b>RefreshAccessTokenRestAPI <\/b>auf und &uuml;bergibt einige Parameter, die teilweise erst von dieser Funktion gef&uuml;llt werden. Liefert dieser Aufruf den Wert <b>True <\/b>zur&uuml;ck, werden die neuen Werte f&uuml;r die Registry-Eintr&auml;ge <b>AccessToken <\/b>und <b>TokenExpiresAt <\/b>gespeichert. Falls nicht, wertet die Funktion die zur&uuml;ckgegebenen Informationen aus und liefert eine Meldung wie beispielsweise in Bild 4.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2023_06\/pic_410_011.png\" alt=\"Fehlermeldung\" width=\"424,6267\" height=\"199,3806\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Fehlermeldung<\/span><\/b><\/p>\n<h2>Funktion zum Aktualisieren des Access-Tokens per Rest-API<\/h2>\n<p>Die Funktion <b>RefreshAccessTokenRestAPI <\/b>aus Listing 4 erstellt eine <b>ServerXMLHTTP60<\/b>-Objekt und legt diverse Einstellungen f&uuml;r den Aufruf der Rest-API fest. Dazu geh&ouml;ren zum Beispiel der Content-Type, die Methode (<b>POST<\/b>) und die zu verwendende URL, die wie folgt lautet:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>RefreshAccessTokenRestAPI(strClientID<span style=\"color:blue;\"> As String<\/span>, strClientSecret<span style=\"color:blue;\"> As String<\/span>, _\r\n         strRefreshToken<span style=\"color:blue;\"> As String<\/span>, strAccessToken<span style=\"color:blue;\"> As String<\/span>, lngExpiresIn<span style=\"color:blue;\"> As Long<\/span>, <span style=\"color:blue;\">Optional<\/span> lngStatus<span style=\"color:blue;\"> As Long<\/span>, _\r\n         <span style=\"color:blue;\">Optional<\/span> strErrorTitle<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> strErrorDescription<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>objHTTP<span style=\"color:blue;\"> As <\/span>ServerXMLHTTP60\r\n     <span style=\"color:blue;\">Set<\/span> objHTTP = <span style=\"color:blue;\">New<\/span> MSXML2.ServerXMLHTTP60\r\n     <span style=\"color:blue;\">Dim <\/span>strData<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strURL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strMethod<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strContentType<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strResponse<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>objJSON<span style=\"color:blue;\"> As Object<\/span>\r\n     strContentType = \"application\/x-www-form-urlencoded\"\r\n     strMethod = \"POST\"\r\n     strURL = \"https:\/\/accounts.google.com\/o\/oauth2\/token\"\r\n     strData = \"client_id=\" & strClientID & \"&client_secret=\" & strClientSecret & \"&refresh_token=\" & strRefreshToken _\r\n         & \"&grant_type=refresh_token&scope=https:\/\/www.googleapis.com\/auth\/calendar\"\r\n     <span style=\"color:blue;\">With<\/span> objHTTP\r\n         .Open strMethod, strURL, <span style=\"color:blue;\">False<\/span>\r\n         .setRequestHeader \"Accept\", strContentType\r\n         .setRequestHeader \"Content-Type\", strContentType\r\n         .send strData\r\n         strResponse = .responseText\r\n         <span style=\"color:blue;\">Select Case <\/span>.status\r\n             <span style=\"color:blue;\">Case <\/span>200\r\n                 RefreshAccessTokenRestAPI = <span style=\"color:blue;\">True<\/span>\r\n                 <span style=\"color:blue;\">Set<\/span> objJSON = ParseJson(strResponse)\r\n                 strAccessToken = objJSON.Item(\"access_token\")\r\n                 lngExpiresIn = objJSON.Item(\"expires_in\")\r\n             <span style=\"color:blue;\">Case Else<\/span>\r\n                 RefreshAccessTokenRestAPI = <span style=\"color:blue;\">False<\/span>\r\n                 <span style=\"color:blue;\">Set<\/span> objJSON = ParseJson(strResponse)\r\n                 lngStatus = .status\r\n                 strErrorTitle = objJSON.Item(\"error\")\r\n                 strErrorDescription = objJSON.Item(\"error_description\")\r\n         <span style=\"color:blue;\">End Select<\/span>\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Die Prozedur RefreshAccessTokenRestAPI aktualisiert das Access-Token.<\/span><\/b><\/p>\n<pre>https:\/\/accounts.google.com\/o\/oauth2\/token<\/pre>\n<p>Au&szlig;erdem stellt sie in <b>strData<\/b> die Parameter zusammen, die an die Rest-API &uuml;bergeben werden sollen &#8211; die Client-ID, das Client-Secret und das Refresh-Token sowie den Typ <b>refresh_token<\/b>, der angibt, dass wir das Access-Token aktualisieren wollen.<\/p>\n<p>Und als <b>scope <\/b>beziehungsweise G&uuml;ltigkeitsbereich geben wir <b>https:\/\/www.googleapis.com\/auth\/calendar <\/b>an, was den lesenden und schreibenden Zugriff auf den kompletten Kalender erlaubt. Hier k&ouml;nnen wir auch andere Werte wie hier angegeben eintragen:<\/p>\n<pre>https:\/\/developers.google.com\/calendar\/api\/auth?hl=de<\/pre>\n<p>Danach verwendet die Funktion die <b>Open<\/b>-Methode des <b>ServerXMLHTTP60<\/b>-Objekts und &uuml;bergibt die URL. Dann stellt sie verschiedene <b>Header<\/b>-Eigenschaften ein und ruft schlie&szlig;lich die <b>send<\/b>-Methode auf und &uuml;bergibt den Inhalt von <b>strData<\/b>. Das Ergebnis liefert das <b>ServerXMLHTTP60<\/b>-Objekt mit der Eigenschaft <b>Response<\/b>.<\/p>\n<p>Daneben erhalten wir den Status des Aufrufs. Lautet dieser <b>200<\/b>, hat der Aufruf funktioniert und wir k&ouml;nnen den das Ergebnis aus <b>strResponse <\/b>mit der <b>ParseJSON<\/b>-Funktion auslesen und in das Objekt <b>objJSON <\/b>schreiben. Auf dieses greifen wir dann &uuml;ber die entsprechende Syntax zu &#8211; mehr zum Umfang mit JSON-Dateien unter <b>Mit JSON arbeiten <\/b>(<b>www.vbentwickler.de\/361<\/b>). Dabei f&uuml;llen wir die R&uuml;ckgabeparameter <b>strAccessToken <\/b>und <b>lngExpiresIn<\/b>. Die Funktion liefert in diesem Fall den Wert <b>True <\/b>zur&uuml;ck.<\/p>\n<div class=\"rcp_restricted\"><p><span style=\"color: #ff0000\">M&ouml;chten Sie weiterlesen? Dann l&ouml;sen Sie Ihr Ticket!<\/span><br \/>\n<span style=\"color: #ff0000\">Hier geht es zur Bestellung des Jahresabonnements des Magazins <strong>Visual Basic Entwickler<\/strong>:<\/span><br \/>\n<span style=\"color: #ff0000\"><a style=\"color: #ff0000\" href=\"https:\/\/shop.minhorst.com\/magazine\/363\/visual-basic-entwickler-jahresabonnement?c=77\">Zur Bestellung ...<\/a><\/span><br \/>\n<span style=\"color: #ff0000\">Danach greifen Sie sofort auf <strong>alle rund 200 Artikel<\/strong> unseres Angebots zu - auch auf diesen hier!<\/span><br \/>\n<span style=\"color: #000000\">Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:<\/span><\/p>\n<\/div>\n\n\t\n\t<form id=\"rcp_login_form\"  class=\"rcp_form\" method=\"POST\" action=\"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000410\/\">\n\n\t\t\n\t\t<fieldset class=\"rcp_login_data\">\n\t\t\t<p>\n\t\t\t\t<label for=\"rcp_user_login\">Username or Email<\/label>\n\t\t\t\t<input name=\"rcp_user_login\" id=\"rcp_user_login\" class=\"required\" type=\"text\"\/>\n\t\t\t<\/p>\n\t\t\t<p>\n\t\t\t\t<label for=\"rcp_user_pass\">Password<\/label>\n\t\t\t\t<input name=\"rcp_user_pass\" id=\"rcp_user_pass\" class=\"required\" type=\"password\"\/>\n\t\t\t<\/p>\n\t\t\t\t\t\t<p>\n\t\t\t\t<input type=\"checkbox\" name=\"rcp_user_remember\" id=\"rcp_user_remember\" value=\"1\"\/>\n\t\t\t\t<label for=\"rcp_user_remember\">Remember me<\/label>\n\t\t\t<\/p>\n\t\t\t<p class=\"rcp_lost_password\"><a href=\"\/data\/wp\/v2\/posts\/55000410?rcp_action=lostpassword\"><\/a><\/p>\n\t\t\t<p>\n\t\t\t\t<input type=\"hidden\" name=\"rcp_action\" value=\"login\"\/>\n\t\t\t\t\t\t\t\t\t<input type=\"hidden\" name=\"rcp_redirect\" value=\"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000410\/\"\/>\n\t\t\t\t\t\t\t\t<input type=\"hidden\" name=\"rcp_login_nonce\" value=\"b96152fcc5\"\/>\n\t\t\t\t<input id=\"rcp_login_submit\" class=\"rcp-button\" type=\"submit\" value=\"Login\"\/>\n\t\t\t<\/p>\n\t\t\t\t\t<\/fieldset>\n\n\t\t\n\t<\/form>\n<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Google-Kalender sind praktisch: Sie sind von &uuml;berall erreichbar, k&ouml;nnen mit endlos vielen Schnittstellen und Diensten verbunden werden und bieten weitere Vorteile. Noch sch&ouml;ner w&auml;re es nat&uuml;rlich, wenn wir auch per VBA auf diese Kalender zugreifen k&ouml;nnten. Die Vorbereitungen haben wir bereits in zwei weiteren Artikeln erledigt &#8211; damit haben wir eine App bei Google angelegt und eine COM-DLL programmiert, mit der wir ein Token f&uuml;r die Authentifizierung bei Google generieren k&ouml;nnen. Damit folgt nun die K&uuml;r: Wir erzeugen VBA-Prozeduren, um auf die verschiedenen Informationen der Google Calendar API zuzugreifen. Dazu geh&ouml;ren Kalender, Termine und vieles mehr. Welche M&ouml;glichkeiten sich zum Lesen, Schreiben, &Auml;ndern und L&ouml;schen von Terminen bieten, zeigen wir auf den folgenden Seiten.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662023,66062023,44000004,44000022],"tags":[],"yst_prominent_words":[],"class_list":["post-55000410","post","type-post","status-publish","format-standard","hentry","category-662023","category-66062023","category-Loesungen","category-Von_Access_zu_NET"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000410","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000410"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000410\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000410"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000410"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000410"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000410"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}