Schlagwort-Archive: Computer

OAuth 2.0 – Lord, have mercy!

OAuth 2.0

Es gelingt mir nicht immer, auf dem Laufenden zu bleiben, was technische Entwicklungen im Web angeht. Vorsichtig ausgedrückt. Obwohl ich mich durchaus bemühe. Aber da draußen ist einfach zu viel los. So ging es auch an mir vorbei, dass Google angekündigt hatte, seinen Client-Login im April 2015 abschalten zu wollen. Oder ich habe einfach nicht verstanden, dass mich das betrifft. Hat es mich aber, und das gleich an verschiedenen Stellen.

Zum einen nutze ich an verschiedenen Stellen im Netz die Möglichkeit, Daten aus Google Tabellen auszulesen und diese weiter zu verarbeiten. Dieser Weg hat im Gegensatz zur Nutzung einer Datenbank den Charme, dass auch eine technisch nicht so versierte Gruppe von Usern sehr leicht Daten editieren kann, die dann direkt auf irgendwelchen Webseiten dargestellt oder für andere Dienste verarbeitet werden können.

Zum anderen brauchte ich diese Art der Authentifizierung für das Backup meiner Google Kontakte auf dem Raspberry Pi. Ich hatte darüber mal ausführlich geschrieben.

Die Nutzung des Client-Logins sah (vereinfacht) so aus, dass man sich bei Google-APIs mit seinen Google-Zugangsdaten authentifizieren konnte. Einen Schritt besser war es schon, dazu eine sog. „anwendungsspezifisches Passwort“ zu verwenden. D.h. man generierte sich bei Google ein weiteres Passwort, welches dann nur eine einzige Anwendung verwenden durfte. Vorteil: Durch Löschen des vergebenen Passworts war auch der Anwendung der Zugriff auf die Daten entzogen, außerdem musste man nicht sein richtiges Google-Passwort in irgendwelchen Skripten hinterlegen. Umgesetzt hatte ich das alles mit Hilfe des Zend Frameworks.

Diese Möglichkeit gibt es jetzt nicht mehr. OAUth 2.0 ist angesagt. OAuth ist an sich nichts neues, aber das Ding ist einfach deutlich komplexer als die herkömmliche Authentifizierung. Wie OAuth 2.0 funktioniert genau funktioniert erkläre ich hier nicht weiter, man kann das z.B. hier schön nachlesen. Ein Artikel in 5 Teilen, das sagt schon alles. Die Grundidee ist die, dass eine Anwendung einen z.B. auf eine Google-Seite weiterleitet, man sich dort authentifiziert und somit seiner Anwendung bestimmte Rechte zuweist, ohne dass die Anwendung Details der Zugangsberechtigung erhält. Das typische „Loggen Sie sich einfach mit Facebook ein“-Ding. Der große Vorteil ist hier, dass keine Passwörter weitergegeben werden, sondern lediglich sog. Access-Tokens generiert werden, die in der Regel auch eine Verfallsdatum haben. Alles crispy, alles funky, aber eben auch alles etwas komplizierter.

Wie gesagt war der Anlass, mich wieder mal mit dem Thema zu befassen, die Reaktivierung des Backups meiner Google Kontakte mit dem Raspberry Pi. Deshalb werde ich hier mal kurz umreißen, wie das Prozedere jetzt aussieht.

Der PHP-Code

Mir ist klar, dass sich meine PHP-Künste im Rahmen halten. Der Code hier ist also nur zum Hausgebrauch und als Arbeitsgrundlage für weitere Optimierungen geeignet. Außerdem enthält er eine Reihe an Kommentaren, die eher den Stil eines Selbstgesprächs als einer sinnvollen Code-Kommentierung haben. Egal.


<?

		// Backup der Google Kontakte per Cronjob funktioniert nur noch mit OAuth2

		// Es gäbe jetzt 2 Wege:

		// 1) User authentifiziert sich per Web

		// 	Problem:
		// 	- Die Authentifizierung läuft schnell ab.
		// 	- Daher ist das per Skript nicht praktikabel.

		// 2) Authentifizierung über ein "Dienskonto" ("service account") mit einem P12-Schlüssel

		// 	Problem:
		// 	- Das Dienstkonto mit seiner speziell generierten Mailadresse kann sich zwar dauerhaft authentifizieren,
		// 	  hat aber keinen Zugriff auf die Kontakte des Users, der das Dienstkonto erstellt hat...

		// Hinweise:

		// - Erstellen eines Projekts bei Google und erzeugen der Schlüssel:		https://console.developers.google.com/project
		// - API Client Library for PHP:											https://developers.google.com/api-client-library/php/start/get_started
		// - Google Contacts API version 3.0:										https://developers.google.com/google-apps/contacts/v3/index#retrieving_all_contacts
		// - google-api-php-client:													https://github.com/google/google-api-php-client
		// - Beispielcode für Ahthentifizierung per Web:							https://github.com/google/google-api-php-client/issues/462
		// - Warum es nicht geht, sondern nur mit einer "Google managed domain":	http://stackoverflow.com/questions/14407415/accessing-google-contacts-api-via-oauth-2-0-and-private-key-aka-service-account
		// - Vielleicht geht es doch per Web-Authentifizierung???					http://stackoverflow.com/questions/13851157/oauth2-and-google-api-access-token-expiration-time

		//  >>> However, after a successful completion of the OAuth2 installed application flow,
		// 		you will get back a refresh token.
		// 		This refresh token never expires, and you can use it to exchange it for an access token as needed.
		// 		Save the refresh tokens, and use them to get access tokens on-demand
		// 		(which should then immediately be used to get access to user data).

# Debug-Modus. Wenn der TRUE ist werden nur Debug-Infos rausgeschickt:
define("DEBUGMODUS", FALSE);

# # # # VERSION MIT DER WEB-AUTHENTIFIZIERUNG

	# Diese Daten hier erhält man nach der Registrierung eines Projekts in der Developers Console (https://console.developers.google.com/):
	# Client-ID:
	$client_id 		= 'HIER KOMMT DEINE CLIENT-ID REIN';
	# Clientschlüssel:
	$client_secret 	= 'HIER KOMMT DEIN CLIENT-SCHLÜSSEL REIN';

	# Nach dem ersten Aufruf des Skripts per Webbrowser bekommt man einen RefreshToken,
	# den man hier einträgt. Mit diesem kann dann bei jedem weiteren Aufruf
	# ein neuer AccessToken geholt werden, mit dem dann wiederrum die Daten geholt werden:
	$refresh_token = 'HIER KOMMT DEIN REFRESH-TOKEN REIN';

	/*
	 * Copyright 2011 Google Inc.
	 *
	 * Licensed under the Apache License, Version 2.0 (the "License");
	 * you may not use this file except in compliance with the License.
	 * You may obtain a copy of the License at
	 *
	 *     http://www.apache.org/licenses/LICENSE-2.0
	 *
	 * Unless required by applicable law or agreed to in writing, software
	 * distributed under the License is distributed on an "AS IS" BASIS,
	 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	 * See the License for the specific language governing permissions and
	 * limitations under the License.
	 */

	# Falls das Skript per Kommandozeile ausgeführt werden soll sollen die Fehler unterdrückt werden (z.B. die Warnings wg. der Session):
	if ($refresh_token)	error_reporting(0);

	session_start();

	// # Session manuell killen, und damit neue Token erzwingen:
	// unset($_SESSION['access_token']);

	# Diese Library muss eingebunden werden: https://github.com/google/google-api-php-client
	require_once __DIR__ . '/google-api-php-client-master/examples/templates/base.php';
	require_once __DIR__ . '/google-api-php-client-master/src/Google/autoload.php';

	$scriptUri 		= "http://".$_SERVER["HTTP_HOST"].$_SERVER['PHP_SELF'];
	$redirect_uri 	= $scriptUri;

	/************************************************
	  Make an API request on behalf of a user. In
	  this case we need to have a valid OAuth 2.0
	  token for the user, so we need to send them
	  through a login flow. To do this we need some
	  information from our API console project.
	 ************************************************/
	$client = new Google_Client();
	$client->setClientId($client_id);
	$client->setClientSecret($client_secret);
	$client->setRedirectUri($redirect_uri);
	//$client->addScope("https://www.googleapis.com/auth/contacts.readonly");
	$client->addScope("https://www.google.com/m8/feeds");

	# HIER KOMMT DER ENTSCHEIDENDE PUNKT!
	# Der AccessToken soll OFFLINE sein, d.h. ich bekomme auch einen 'refresh_token',
	# mit dem ich mir dann immer wieder neu einen 'access_token' holen kann,
	# ohne die Web-Authentifizierung neu machen zu müssen.
	# Siehe https://developers.google.com/identity/protocols/OAuth2WebServer#refresh
	# PHP-Demo: http://stackoverflow.com/questions/9241213/how-to-refresh-token-with-google-api-client
	# Außerdem muss dazu noch approval_prompt=force gesetzt werden.
	# Siehe https://github.com/google/google-api-php-client/issues/263
	$client->setAccessType('offline');
	$client->setApprovalPrompt('force');

	/************************************************
	  Boilerplate auth management - see
	  user-example.php for details.
	 ************************************************/
	if (isset($_REQUEST['logout'])) {
		unset($_SESSION['access_token']);
	}
	if (isset($_GET['code'])) {
		$client->authenticate($_GET['code']);
		$_SESSION['access_token'] = $client->getAccessToken();
		$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
		header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
	}

	if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
		$client->setAccessToken($_SESSION['access_token']);
	} else {
		$authUrl = $client->createAuthUrl();
	}

	# Falls schon ein RefreshToken gesetzt ist kann das Skript per Kommandozeile ausgeführt werden:
	if ($refresh_token) {

		# Damit hole ich mir bei JEDEM Aufruf einfach einen neuen Access-Token ;-)
		$client->refreshToken($refresh_token);

		# Außerdem die Variable $authUrl löschen, sonst wird weiter unten nur die URL zum Login angezeigt:
		unset($authUrl);
	}

	/************************************************
	  If we're signed in, retrieve contacts
	 ************************************************/
	if ($client->getAccessToken()) {

		if (DEBUGMODUS) echo "<hr><h1>Session-Variablen:</h1><pre>".print_r($_SESSION, true)."</pre>";

		$_SESSION['access_token'] = $client->getAccessToken();

		$access_token = json_decode($client->getAccessToken())->access_token;
		if (DEBUGMODUS) echo "<hr><h1>Client:</h1><pre>".print_r($client, true)."</pre>";
		if (DEBUGMODUS) echo "<hr><h1>Access-Token:</h1><pre>".print_r(json_decode($client->getAccessToken()), true)."</pre>";
		if (DEBUGMODUS) echo "<hr><h1>Weitere Infos zum Access-Token:</h1><pre>".print_r(json_decode(file_get_contents('https://www.googleapis.com/oauth2/v3/tokeninfo?access_token='.$access_token)), true)."</pre>";

		# Beim ersten Aufruf soll einfach nur der Refresh-Token zurückgegeben werden, der muss dann hier ins Skript eingetragen werden:
		if (!$refresh_token) {
			echo "Bitte den Code unterhalb der Linie in das Skript in die Variable $refresh_token eintragen:<hr>";
			echo "<pre>".json_decode($client->getAccessToken())->refresh_token."</pre>";
			exit();
		}

		//$url = 'https://www.google.com/m8/feeds/contacts/default/full?max-results=3&alt=json&v=3.0&oauth_token='.$access_token;
		$url = 'https://www.google.com/m8/feeds/contacts/default/full?max-results=3000&alt=json&v=3.0&oauth_token='.$access_token;

		$response =  file_get_contents($url);

	}
	if (isset($authUrl)) {
		echo "<a class='login' href='" . $authUrl . "'>Zugriff auf Google Contacts autorisieren > KLICK!</a>";
	} else {
		if (DEBUGMODUS) echo "<hr><h1>Kontakte:</h1><pre>".print_r(json_decode($response), true)."</pre>";
		else echo $response;

	}

Dieses Skript muss auf den Raspberry Pi kopiert und so eingerichtet werden, dass es (vorübergehend) aus dem Netz erreichbar ist. Man sollte also irgend einen Webserver auf dem Pi laufen haben.

Außerdem braucht man den Google API PHP-Client, den man herunterladen und in sein Projekt einbinden muss. Könnte man auch per Composer machen, ist in diesem Fall aber vielleicht ein bisschen zu viel des Guten. Immerhin gibt es keine Abhängigkeiten von anderen Paketen.

Die Authentifizierung

Um dem Skript nun Zugriff auf die eigenen Daten zu gewähren, muss in der Google Developers Console ein neues Projekt angelegt werden: https://console.developers.google.com/ Das Projekt braucht einen beliebigen Namen. Außerdem muss man folgende APIs aktivieren:

  • Contacts API
  • Google Contacts CardDAV API
  • Google Partners API

Bin mir nicht wirklich sicher, ob man die „Google Contacts CardDAV API“ hier auch braucht, aber sie schadet nicht…

Anschließend brauchen wir Zugangsdaten, die wir (tataaa!) unter „Zugangsdaten“ generieren. Und zwar wählen wir „Client-ID für Webanwendung erstellen“. Wichtig ist an dieser Stelle, dass eine Weiterleitungs-URI gesetzt wird, die auf genau die URL zeigt, unter der unser PHP-Skript gerade im Netz erreichbar ist. Wenn wir die Client-ID erstellt haben bekommen wir unter anderem folgende Parameter: Eine „Client-ID“ und einen sog. „Clientschlüssel“. Diese beiden Werte müssen wir jetzt in unser PHP-Skript eintragen:


# Diese Daten hier erhält man nach der Registrierung eines Projekts in der Developers Console (https://console.developers.google.com/):
 # Client-ID:
 $client_id = 'HIER KOMMT DEINE CLIENT-ID REIN';
 # Clientschlüssel:
 $client_secret = 'HIER KOMMT DEIN CLIENT-SCHLÜSSEL REIN';

Wenn wir jetzt das PHP-Skript vom Web aus aufrufen, können wir den bekannten Authentifizierungsprozess starten: Das Skript leitet weiter zu einer Google-Authentifizierungsseite, dort sollte man dann alles gut finden und brav bestätigen, und am Ende wird man zurück zu unserem Skript geleitet. Das Skript ist jetzt authentifiziert, die Kontakte bei Google abzuholen. Allerdings verfällt der dazu vergeben Access Token schon innerhalb einer Stunde. Unpraktisch, wenn das Skript die Kontakte z.B. täglich ohne Zutun des Nutzers sichern soll. Daher wird von Google gleichzeitig ein sog. „Refresh Token“ angefordert. Mit Hilfe dieses Refresh Tokens kann das Skript bei jedem Aufruf einen neuen Access Token anfordern und anschließend mit diesem die Kontakte anfordern. Dazu muss man den Refresh Token irgendwo hinterlegen. Üblicherweise macht man das in einer Datenbank, der Einfachheit halber speichern wir ihn aber direkt im Skript. Das Skript zeigt den Refresh Token nach erfolgreicher Authentifizierung im Browser an, von dort kopieren wir ihn jetzt in unser Skript:


# Nach dem ersten Aufruf des Skripts per Webbrowser bekommt man einen RefreshToken,
# den man hier einträgt. Mit diesem kann dann bei jedem weiteren Aufruf
# ein neuer AccessToken geholt werden, mit dem dann wiederrum die Daten geholt werden:
$refresh_token = 'HIER KOMMT DEIN REFRESH-TOKEN REIN';

Die Ausführung

Ein Zugriff auf das Skript über das Internet ist nun nicht mehr nötig und sollte auch unterbunden werden. Denn ab jetzt soll das Skript nur noch per Kommandozeile oder Crontab ausgeführt werden, z.B. so:

php ~/skripte/backup-google-contacts.php | gzip > ~/backup/Google\ Kontakte/backup-google-contacts-`date +\%Y-\%m-\%d`.json.gz

Geschafft! Ich hoffe, der eine oder andere kann etwas mit dem Code-Chaos hier anfangen. Der Code sollte auf jeden Fall ein paar Anhaltspunkte für jemanden enthalten, der vor ähnlichen Problemen steht. Ich glaube, mir hätte das eine oder andere Code-Schnipsel weitergeholfen und mir die eine oder andere Nachtschicht erspart. Wobei, wäre vielleicht sogar schade gewesen, so ein Erfolgserlebnis ist ja auch was wert 😉

Mechanische Fourier-Transformation

Wenn man unschuldige Passanten auf der Straße danach fragen würde, was wohl eine Fouriertransformation ist, dann würde man vermutlich von dem einen oder anderen für einen esoterischen Spinner gehalten werden. Dabei wäre den Leuten nicht klar, dass beim Hören der Frage in ihrem Ohr genau eine solche Fouriertransformation stattgefunden hat: Der Schall, der auf ihr Ohr getroffen ist, bestand aus einer Überlagerung von vielen Sinus-Schwingungen, die sich in der Luft als Druckschwankungen ausgebreitet haben und schließlich auf’s Ohr getroffen sind. Was wir aber wahrnehmen ist nicht die Überlagerung von Sinusschwingungen, sondern Töne. Spielt man auf dem Klavier z.B. einen normalen Dreiklang mit unterschiedlich lauten Tönen (und vereinfacht jetzt mal ganz gewaltig), dann sendet das Klavier (auf einer Zeitachse betrachtet) gleichzeitig drei Sinusschwingungen unterschiedlicher Amplitude aus. Was wir hören sind aber drei unterschiedlich laute Töne, also Frequenzen. Und tataaa: Die drei gewichteten Frequenzen sind die Fouriertransformation (in dieser Richtung würde man es „Fourier-Analyse“ nennen) der zeitlichen sinusförmigen Dichteschwankungen der Luft.

Das Ohr kann das also, nahezu in Echtzeit fouriertransformieren. Computer können das auch, üblicherweise verwenden sie die sog. FFT, die „Schnelle Fouriertransformation“. Ohne diese Methodik wären viele simplen technischen Dinge des normalen Alltagslebens nicht möglich.

Ich arbeite seit Jahren mit Magnetresonanztomographie (MRT) und habe immer noch Schwierigkeiten, das Wort schnell und fehlerlos zu schreiben somit viel mit Fouriertransformation zu tun, denn in der MRT werden nimmt man seine Bilder nicht einfach Pixel für Pixel auf, so wie das z.B. eine Digitalkamera tut, sondern man nimmt die Daten im fouriertransformierten Raum des normalen Bildraums auf. Bisher war mir jedoch nicht klar, dass man eine Fouriertransformation auch mit relativ überschaubaren mechanischen Apparaten erzeugen kann. Bill Hammack, der fast schon legendäre EngineerGuy, hat da etwas sehr interessantes ausgegraben: Eine 100 Jahre alte Maschine, die sowohl einen Fourier-Synthese, als auch eine Fourier-Analyse durchführen kann. Natürlich nicht so präzise und schnell wie ein heutiger Rechner, aber dennoch erstaunlich gut. Und wenn du nicht das dicke Buch von Albert Michelson „Harmonic Analyzer“ zu der Maschine durchlesen willst (hier als PDF), dann empfehle ich dir die folgenden drei unterhaltsamen Videos. 15 Minuten Zeit sollten über die Feiertage schon mal drin sein für ein bisschen mathematische Grundbildung:

Raspberry Pi als Backupmaschine

Raspberry PiDer Raspberry Pi ist eine kleine Allzweckwaffe: Ein kompletter Rechner für ca. 35€, so groß wie eine Scheckkarte und mit einer sehr geringen Leistungsaufnahme (deren Kehrseite natürlich auch eine ziemlich geringe Leistung ist). Aber für manche Anwendungsfälle ist der Raspberry Pi genau das richtige Gerät. Ich kenne einige Leute, die sich vor lauter Begeisterung einen Raspberry Pi gekauft haben und nun nicht so richtig wissen, was sie mit dem schicken Teil nun eigentlich anfangen sollen. Mein Tipp: Als Backupmaschine verwenden.

Was der Raspberry Pi bei mir alles so backupt will ich in diesem Beitrag grob skizzieren. Voraussetzung ist ein fertig installiertes Raspian oder ein vergleichbares System, Grundkenntnisse im Umgang mit Linux (z.B. der crontab), dem Raspberry Pi an sich und zumindest eine gewisse Lernbereitschaft und Offenheit gegenüber z.B. Shell- und PHP-Skripten. Ich werde keine kompletten Anleitungen schreiben, eher so eine Gedankensammlung. Es muss und soll also weiter gebastelt und gegooglet werden!

Weiterlesen

Bulk Calendar – Mehrere Termine auf einmal bei Google Calendar eintragen

Ihr kennt das: Man will weder einen einzelnen Termin in seinen Google Calendar eintragen, noch einen Serientermin erstellen. Man hat beispielsweise eine Reihe an Seminaren mit verschiedenen Themen, irgendwelche Meetings an wechselnden Orten oder die Fussballtermine der Kinder gegen verschiedene Gegener. Man kann jetzt entweder auf die Details verzichten und doch einen Serientermine anlegen, oder aber man hat viel Handarbeit vor sich.

Genau diese Handarbeit kann man sich deutlich erleichtern, indem man die anzulegenden Termine in eine simple Google Tabelle einträgt und anschließend dort ein Skript startet, welches diese tabellarisch erfassten Daten in einen Kalender einträgt. Es ist schon etwas länger her, dass ich mich auf die Suche nach einem solchen Skript gemacht habe. Daher fehlt mir jetzt leider die Referenz auf das Original an dem ich anschließend noch ziemlich weitergebastelt habe.

Ich habe hier eine Kopie meiner Tabelle erstellt und freigegeben. Sieht in etwas so aus:

Bulk Calendar - Tabelle

Wenn du die Tabelle und das Skript verwenden willst erstellst du dir am besten erst mal eine Kopie der Tabelle (das Skript ist darin eingebettet):

Bulk Calendar - Kopie erstellen

Zuerst muss man logischerweise die gewünschten Daten in die Tabelle eintragen (Tabelle s.o.). Die grau markierten Zellen (Start- und Endzeitpunkt) dürfen nicht editiert werden. Wichtig ist, dass man die Formel in den grauen Spalten nur soweit nach unten kopiert, wie auch Daten in die Zeilen eingetragen sind:

Bulk Calendar - Formeln

Anschließend muss das Skript ausgeführt werden:

Bulk Calendar - Tools - Skriptmanager

Bulk Calendar - Skriptmanager Ausführen

Beim Ausführen des Skript gibt es diverse Kontroll-Dialoge, die man einfach im Skript nach Belieben auskommentieren kann.

Der Vollständigkeit halber hier noch der Code des Skripts. Wie immer quick’n’dirty, tut es aber für mich. Falls jemand damit weiter bastelt bin ich offen für Anregungen.


// Custom Menu:
// see https://developers.google.com/apps-script/guides/bound#custom_menus_dialogs_and_sidebars
function onOpen() {
  
//  var cals = CalendarApp.getAllOwnedCalendars();
//  Browser.msgBox('Es gibt genau %s Kalender zu Auswahl:', CalendarApp.getAllOwnedCalendars().length);
//  for (var c=0; c < cals.length; c++) {
//    ui.createMenu('Termine eintragen')
//      .addItem(cals[c].getName(), 'TermineInCalendarEintragen')
//      .addToUi();
//  }  
  
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('Termine eintragen')
      .addItem('Go!', 'TermineInCalendarEintragen')
      .addToUi();

}





function TermineInCalendarEintragen() {
 
  // Default-Kalender auswählen:
  //var cal = CalendarApp.getDefaultCalendar();
 
  // Besser: Auswahl an Kalendern anbieten.
  // Ansatz: Alle möglichen Kalender auflisten, per Eingabefeld den gewünschten eingeben lassen:
  var kalenderauswahl = 'In welchen Kalender sollen die Termine eingetragen werden?\\n\\n';
  // Alle eigenen Kalender abrufen:
  var cals = CalendarApp.getAllOwnedCalendars();
  
  Logger.log('Es gibt genau %s Kalender zu Auswahl:', cals.length);
  
  // Kalender durchgehen und Ausgabenachricht zusammenbasteln:
  //for (c in cals) {
  for (var c=0; c < cals.length; c++) {
    Logger.log("ID: " + cals[c].getName());
    kalenderauswahl = kalenderauswahl + '- ' + cals[c].getName() + '\\n';
  }
  kalenderauswahl = kalenderauswahl + '\\nBitte Kalendername EXAKT eingeben:';
  var kalendername = Browser.inputBox(kalenderauswahl)
  Logger.log("Kalender: " + kalendername);
  
  // Jetzt den Kalender mit dem übergebenen Namen auswählen:
  cal = CalendarApp.getCalendarsByName(kalendername);
 
  
  // Falls ein (brauchbarer) Kalendername eingegeben wurde:
  if (kalendername && cal.length > 0) {
    Browser.msgBox('Kalender "' + cal[0].getName()  + '" gewählt.')
    
    // Jetzt werden die Daten aus der Tabelle gezogen:
    var sheet = SpreadsheetApp.getActiveSheet();
    var startRow = 2;
    var numRows = SpreadsheetApp.getActiveSheet().getLastRow()-1;  // -1, weil die Kopfzeile keine Daten enthält
    Browser.msgBox('Es sollen ' + numRows + ' Termine eingetragen werden.')
    // Datenbereich auswählen:
    var dataRange = sheet.getRange(startRow, 1, numRows, 8);
    // Daraus Daten in Variable schreiben:
    var data = dataRange.getValues();
    // Jetzt jede Zeile durchgehen:
    for (d in data) {
      // Daten aus dieser Zeile wählen:
      var row = data[d];
      
      // Jetzt einzelne Felder davon:
      var title  = row[0];
      var tstart = row[4];
      var tstop  = row[5];
      var loc    = row[6];
      var desc   = row[7];
      
      // Jetzt wird der Termin eingetragen:
      // https://developers.google.com/apps-script/class_calendarapp?hl=de#createEvent
      cal[0].createEvent(title, tstart, tstop, {description:desc,location:loc});
      
      // ... und noch eine Meldung ausgegeben. Denn manchmal steigt das Skript aus, und so weiß man, wie weit es war.
      Browser.msgBox( parseInt(d)+1 + '. Termin in Kalender "' + kalendername + '" eingetragen:\\n\\n' + title + '\\n\\n' +  tstart + '\\n\\n' + desc )
    }
    // Am Ende nochmal Meldung, dann ist klar, dass alles durchgelaufen ist:
    Browser.msgBox('Fertig!');
  }
  else {
    Browser.msgBox('Kein Kalender ausgewählt oder Kalender falsch angegeben...')
  }
}


Passwortmanager: KeePass oder nix

Passwörter braucht man heutzutage ja an jeder Ecke, und im Umgang mit Passwörtern kann man so einiges falsch machen: Die Hitliste der am häufigsten verwendeten Passwörtern verschlägt einem die Sprache. Und das ist nur die Spitze des Eisbergs.

Was bisher geschah

Ganz so fahrlässig hatte ich das bisher nicht gehandhabt. Ich hatte schon ein ziemlich ausgeklügeltes System, bei dem ich… Lassen wir das besser. Immerhin hatte ich nicht bei jedem Dienst das selbe Passwort. Richtig verlässlich fand ich das alles trotzdem nicht. Vor allem kann man – vergibt man Passwörter nach einem System – nicht mal so einfach hier oder da ein Passwort ändern, nur weil von diesem Dienst gerade die Passwörter entwendet wurden. Ungefährlich ist ein sorgloser Umgang mit Passwörtern auch nicht, in regelmäßigen Abständen wird z.B. mit geklauten Passwörtern viel Schaden angerichtet (nicht nur finanzieller). Wenn du denkst, das wäre alles halb so wild, dann gib doch mal testweise nur ein einziges deiner Passwörter an deine fünf besten Freunde: Dein Mailpasswort. Na also.

Mein Plan

Ich wollte das endlich besser geregelt haben. Nein, nicht besser, sondern gut. Dazu braucht man 1) für jeden Dienst ein separates Passwort, welches 2) zufällig generiert und ausreichend lange ist. Wenn man diese Anforderungen stellt, führt kein Weg an einem Passwortmanager vorbei. Ein Passwortmanager funktioniert so, dass man alle seine tollen Passwörter in eine Art digitales Safe speichert und sich nur den einen (richtig gut ausgedachten!) Schlüssel dazu merken muss, das Masterpasswort.

Weiterlesen

Geduld aber ist euch not

via ehrensenf.de

„Geduld aber ist euch not“ steht im Hebräerbrief. Das kann man ruhig mal aus dem Zusammenhang reißen und einfach so allgemeingültig dahersagen, kein Problem. Denn wer sich schon einmal wie das Poker Face rechts im Film gefühlt hat oder sich gar in schöner Regelmäßigkeit so fühlt, der weiß um die dringende Notwenigkeit von Geduld. Andernfalls endet es wie im Film.

Das iPad ist nur eine Fernbedienung

Das iPad ist also kein Computer im Sinne einer Universalmaschine mehr, sondern eine Abspielplattform für die Inhalte der Medienkonzerne. Das iPad macht aus dem Two-Way-Web wieder eine Einbahnstraße und zwar eine, für deren Nutzung gezahlt werden muss.

Das führt letztlich zur Entmündigung der Nutzer. Der für das soziale Netzwerk Twitter arbeitende Autor und Programmierer Alex Payne, eigentlich ein Mac- Enthusiast, formuliert es drastisch: „Was mich am meisten am iPad stört, ist dies: Hätte ich als Kind anstelle eines richtigen Computers nur ein iPad gehabt, wäre ich nie ein Programmierer geworden.“ Möglicherweise läutet das iPad das Ende einer „Hacker-Ära“ ein und entmündigt uns mit klinisch reinen Anwendungen, die zwar praktisch sein mögen, aber jede Kreativität im Keim ersticken. Für mich jedenfalls steht fest: Ein Computer, auf dem keine einzige Programmiersprache läuft, ist kein Computer, sondern eine Fernbedienung.

via faz.net

Bitte den ganzen Artikel lesen. Danke.

Der Artikel drückt sehr gut aus, was mich neben all den technischen Unzulänglichkeiten am meisten am iPad (und ebenso am iPhone etc.) stört.

Mal wieder was faszinierendes?

Es zwitschert kurz, eine Direktnachricht (an die deutsche Twitter-Ausgabe muss ich mich erst noch gewöhnen) flattert herein und lässt sich auf meinem Rechner nieder. Der gute @alexkupsch schreibt „mal wieder was faszinierendes im touch-pad-bereich…“ und verlinkt obiges Video. „Mal wieder was faszinierendes“ ist das ihm typische Understatement in Perfektion. Guckst du! Und sag vorher Bescheid, wenn du nachher aufgeweckt werden willst. Kann sein, dass das nötig ist.