Kleines Google Kalender Backup Script in PHP

01 03 2011

Seit ich ein Android-Smartphone besitze, verwalte ich meine Termine aus Bequemlichkeit im Google Kalender. Das funktioniert prima und Google schützt auch gut vor versehentlich gelöschten Einträgen, aber um Backups möchte ich mich trotzdem zusätzlich noch mal selber kümmern. Das klappt auch recht einfach, weil Google von allen angelegten Kalendern .ics-Dateien unter einer geheimen privaten URL anbietet. Die muss man nur regelmäßig herunterladen und abspeichern. Eine kurze Suche fördert dieses Shell-Script ans Tageslicht, das ich flugs nach PHP portiert habe. PHP deswegen, weil Shellscripte unter Windows nicht funktionieren und ich Windows-Batch-Scripte unglaublich kompliziert finde. Ein PHP Interpreter ab Version 5.3 für Windows muss nur heruntergeladen und mit zwei kleinen Änderungen an der php.ini angepasst werden, fertig ist die Laufzeitumgebung. Keine Installation systemweiten Krams, portabel auf allen Plattformen, läuft sogar auf jedem normalen Webspace. Zugriff auf PHP hat also im Grunde so ziemlich jeder. Davon abgesehen kenne ich mich mit PHP am besten aus.

Nun zum Script selbst. Der spannende Teil sind folgende cURL-Anweisungen, alles drumherum kümmert sich um Konfiguration und die Schleife über alle Kalender.

$ch = curl_init($url);
$fh = fopen($filename, 'w');
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // only needed with https:// URLs
curl_setopt($ch, CURLOPT_SSLVERSION, 3); // only needed with https:// URLs
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // only needed with https:// URLs
curl_setopt($ch, CURLOPT_FILE, $fh);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_exec($ch);
curl_close($ch);
fclose($fh);

Howto: Google Calendar PrivatadresseWenn man die Standard-URLs benutzt, die Google einem als private Adresse gibt, braucht man nicht mal die drei SSL-Einstellungen. Für mich kommt ein Download ohne SSL aber nicht in Frage, dazu muss man nur das Protokoll der privaten Kalender URLs auf https:// umstellen. Diese URLs bekommt man in den Kalendereinstellungen des jeweiligen Kalenders, wie im Screenshot zu sehen ist.

Das vollständige Script inkl. Readme.txt gibt es hier, die Lizenz ist cc-by-sa. Unter Windows lässt sich das leicht als geplanter Task einrichten. Ich habe dazu einen Ordner in den Eigenen Dateien erstellt, das angepasste Script hinein und eine aktuelle PHP 5.3 Distribution in den Unterordner php gelegt. Im Taskplaner rufe ich nun sonntags die dortige php-win.exe auf und übergebe den vollen Pfad des Backup-Scripts als einzigen Parameter. Das Script erzeugt stets einen Unterordner mit Datum und Uhrzeit und legt alle konfigurierten Kalender dort ab. Dank PHP kann man das auch auf einem Webserver oder generell überall machen, wo PHP ab 5.3 mit cURL läuft.

Viel Spaß damit.

Ach ja, bevor Fragen aufkommen: Die Google Kontakte lassen sich auf diese Weise nicht herunterladen. Wenn man mit Cookies in cURL hantieren will, kann man sich bei Google einloggen und ein Backup der Kontakte saugen. Aber das ist nicht ganz trivial. Alternativ kann man die Google Data API anzapfen, das ist gut dokumentiert, aber liefert die Daten nur in einem ATOM-XML-Format.


Terminfindungswebdienst

28 11 2010

Seit ein paar Jahren veranstalte ich mit ein paar Kollegen in unregelmäßigem Abstand einen kleinen Jour-Fixe, die Terminabsprache findet in einer Mailingliste statt. Die Erfahrung damit zeigt, dass das schon bei 6 Leuten unübersichtlich wird und eine klare Diskussion für Alternativtermine nicht ganz leicht ist. Nun kommt noch ein mehr oder weniger regelmäßiger Spieleabend dazu, an dem auch noch Leute teilnehmen werden, denen ich die korrekte Bedienung einer Mailingliste gar nicht erst zutraue. Es muss also eine alternative technische Unterstützung hinzukommen. Spontan fällt mir da nur Doodle ein, aber deren tabellenartiges System geht mir nicht weit genug, weil es die komplexen Zusammenhänge von Zusagen nicht abbilden kann. Dass man mehrere Daten zur Wahl hat und nur entweder sagen kann, ob man kann oder nicht, reicht eben nicht. Also überlege ich, sowas schnell mal selbst zu schreiben. Aber was braucht es für ein Featureset? Nach ein wenig Nachdenken bin ich auf folgende Funktionen gestoßen:

  • Es muss geschlossene Gruppen geben, die per E-Mail abonnierbar sind. Neue Terminvorschläge (mit oder ohne Alternativen) und Änderungen gehen als Benachrichtigung raus, ein Klick auf einen Link in der Mail sollte dabei direkt einen Status setzen können, ohne, dass man sich extra einloggen muss. Es sollte zudem auch offene Gruppen/Events mit und ohne Einladung geben.
  • Bei mehreren Terminvorschlägen für ein Event muss es eine intuitive Priorisierung geben, vielleicht per Drag and Drop, vielleicht anders. Man muss ausdrücken können, dass einem Termin A lieber als Termin B ist, man aber bei Termin C gar nicht kann. Zudem muss unterschieden werden zwischen benachrichtigt, aber nicht reagiert und einer expliziten Ablehnung.
  • Es muss eine Möglichkeit geben, vielleicht bzw. unter Vorbehalt zuzusagen. Daraus ergibt sich auch die Anforderung, dass es einen leicht zugänglichen Kommentar zu jedem eigenen Voting geben muss. So ein Kommentar sollte nicht chronologisch aufgeführt werden, sondern als Metainformation zum Voting.
  • Man muss seine Votings ändern können, ein Zeitstempel für die letzte Änderung muss dabei veröffentlicht werden. Außerdem sollten Events auch vor ihrem eigentlichen Datum als abgeschlossen markiert werden können.
  • Es sollte die Möglichkeit geben, dass der Eventbesitzer bestimmte oder alle Personen als "Muss dabei sein" oder "Wäre schon wichtig" markieren kann. Zudem sollte er den Usern der Gruppe freistellen können, solche Bedingungen für sich (und ggf. auch andere) zu definieren.
  • Es sollte einen Kommentarbereich geben, wo kurze Diskussionen ausgetragen werden können (etwa "Was essen wir?).
  • Die Votingtabelle sollte via iFrame anderswo einbindbar sein.
  • Die Softwarebasis sollte unter einer Open-Source-Lizenz veröffentlicht werden, so dass jedermann so ein System für sich aufsetzen kann. Möglicherweise kann man sowas auch als Cloud-Dienst für andere anbieten.
  • Es muss eine API (mindestens JSON) geben, über die der Dienst (wahlweise) auch von Außen bedienbar wird. Authentifizierung könnte per oAuth gemacht werden, standardmäßig aber per Cookie und bei Mails via Token. Sicherheit bei der Authentifizierung sollte im Zweifel gegenüber Komfort bei der Nutzung nachrangig behandelt werden.
  • Die Oberfläche sollte optisch angenehm sein und sich mit ausgeprägter AJAX-Unterstützung flüssig bedienen lassen. Ein Fallback mit Mindeststandard für Nutzer ohne JavaScript könnte implementiert werden, aber es dürfen dadurch keinerlei Komforteinbußen bei Nutzern mit JavaScript entstehen. Der Internet Explorer 6 (und ggf. auch 7) wird als Browser nicht berücksichtigt, solche Nutzer werden entweder gewarnt (und können es dann wenigstens mal versuchen) oder sofort abgewiesen. Ausnahme: Der Request wird trotzdem ausgeführt, klicks auf Links in Benachrichtigungsmails müssen immer funktionieren. Technisch wird auf CSS3 und HTML5 gesetzt, als JavaScript-Framework kommt jQuery zum Einsatz, serverseitig basiert das System auf PHP 5.3 (und MySQL oder SQLite via PDO) und ggf. auf einem Framework (Symfony, Zend, Flow3). Gegen ein Framework spricht, dass man das System ggf. kapseln könnte, um es als PlugIn für verschiedene CMSe zu portieren. So komplex sollte das nicht sein und das MVC-Paradigma und andere Design-Patterns kann man auch so nutzen. Der Login-Mechanismus (samt Gruppen- und Userverwaltung), der Dispatcher (für das Routing der Requests und vor allem der API) und das Model müssten dann modular aufgebaut und austauschbar sein, die Output-Templates sind es ja sowieso schon.
  • Ggf. könnte das System nicht nur für Events genutzt werden, sondern auch für die allgemeine Entscheidungsfindung. Man stellt also etwa Pizza, Asia, Pommesbude, Döner und Nudeln zur Wahl und bekommt am Ende eine Entscheidung mit dem besten Kompromiss. Da wird besonders deutlich, wie wichtig eine Einstufung jenseits von ja und nein ist.

Habe ich eine wichtige Funktion vergessen? Gibt es Input? Meldet Euch einfach bei mir, aber denkt daran, dass ich kein ICQ mehr benutze.


phpBB3 und die Sommerzeit

10 11 2010

Einmal im halben Jahr gibt es in vielen Ländern der Welt eine Zeitumstellung, wahrscheinlich hat das jeder schon mal mitbekommen. Bei der phpBB3 Forensoftware ist das ebenfalls berücksichtigt, ist aber leider nicht praxistauglich implementiert. Man kann zwar als Admin boardweit die Zeitumstellung vornehmen, aber das ändert nur die Einstellung für Gäste, denn zusätzlich wird die Sommerzeit pro Benutzer gespeichert, was nicht automatisch angepasst wird. Es müsste also jeder User in sein Profil gehen und dort seine eigene Zeitumstellung vornehmen. Unparktikabel und nicht zu Ende gedacht. Folgende SQL-Abfragen erledigen das für das Board und alle User in einem Rutsch:

Auf Winterzeit umstellen:

UPDATE phpbb3_config SET config_value = 0 WHERE config_name = 'board_dst';
UPDATE phpbb3_users SET user_dst = 0;

Auf Sommerzeit umstellen:

UPDATE phpbb3_config SET config_value = 1 WHERE config_name = 'board_dst';
UPDATE phpbb3_users SET user_dst = 1;

Die Tabellennamen müssen ggf. noch angepasst werden, wenn andere Prefixe als phpbb3_ benutzt werden.

Nachtrag 16.11.2010: Ich frage mich immer wieder, wieso die User ihre Sommerzeit selber einstellen können/müssen, statt dass sie einfach einen Haken für "Ich lebe in einem Land mit Sommerzeit" setzen können. Dieses Flag kann dann einfach berücksichtigt werden, wenn das Board umgestellt wird oder sich gar selber umstellt.


Ist Joomla ein Hort für Stümper?

05 10 2010

Vorweg: Ich habe mal im Jahr 2005 für ein Projekt einen genaueren Blick in das CMS Joomla geworfen und mich klar dagegen entschieden. Ich weiß nicht mehr im einzelnen, wieso, aber die Entscheidung war sehr klar und fiel seinerzeit zugunsten von TYPO3 aus, das ich seitdem hervorragend bewährt hat. Ich weiß also im Grunde nicht viel über Joomla. Irgendwann habe ich mal beim Multimediatreff in Köln einen Vortrag zur Joomla-Entwicklung gehört, bei dem der (oder die?) Vortragende keinen leichten Stand hatte: Das Publikum war offensichtlich der Meinung, dass Joomla eher zu den weniger ernstzunehmenden Content-Maganement-Systemen gehört; zudem überzeugte auch der Vortrag kaum vom Gegenteil. Auch sonst haben Joomla-Integratoren einen schweren Stand in meiner Peergroup. Genau genommen habe ich nicht eine Empfehlung für Joomla von jemandem gesehen, den ich in Sachen Webentwicklung üblicherweise ernst nehme. Vielleicht ist das Zufall, aber ich kenne auch niemanden, der jemanden kennt, der Joomla einsetzt und dabei – wie auch immer definiert – gut ist.

Wo mir Joomla aber häufig begegnet, ist bei sehr günstigen Angeboten für Webseiten (sowohl Wettbewerb für, als auch zur beratenden Prüfung durch mich). Fast immer sind das 1-Mann-Agenturen, deren Angebote und vor allem deren Auftreten mich nicht von gesteigerter technischer Kompetenz überzeugt. Ganz häufig sind das Quereinsteiger, die sich autodidaktisch irgendwie HTML und CSS angefressen und im Zuge der Joomla-Integration auch ein paar Brocken sehr schlechtes PHP angeeignet haben. Ich möchte das nicht generell kritisieren, jeder fängt mal an und lernt den Kram im Laufe der Zeit irgendwie mehr oder weniger gut. Das Problem ist, dass bei den Leuten, die mir im Joomla-Kontext begegnet sind, das weniger dominiert. Das heißt nicht, dass jeder Joomla-Integrator ein Stümper ist; nur eben, dass er sich in einem Umfeld mit auffällig vielen Stümpern bewegt.

Frage ist: Warum ist das so? Ist Joomla ein schlechtes CMS? Meiner Meinung nach ja, aber das ist wenig objektiv. Aber irgendwas muss doch da dran sein, wenn es so beliebt ist. Module zusammenstöpseln und eine irgendwie lauffähige Seite bekommen, ohne sich groß einarbetien zu müssen, können auch andere Systeme. Was ist es also, was all die Stümper und Anfänger so anzieht? Warum setzt man ein System ein, dessen nach vielen Jahren endlich erscheinende neue Version ungefähr das halbe Featureset von anderen Systemen vor fünf Jahren implementiert? Stichwort Rechtesystem oder tabellenfreier HTML-Output der Core-Funktionen. Weil es so einfach in der Integration ist und man vom Wissensstand her nicht in der Lage ist, ein besseres System einzusetzen? Dann stellt sich aber die Frage: Warum nimmt man Geld für eine Dienstleistung, wenn der eigene Wissensstand gerade mal für das einfachste Werkzeug reicht? Immerhin muss man den Joomla-Integratoren der Kreisliga lassen, dass sie meistens auch nur Kreisliga-Preise aufrufen. Wenn ich aber bei günstigen Nebenberuflern die Wahl habe zwischen einem ambitionierten Studenten und einem Feierabend-Joomla-Bastler, gewinnt der ambitionierte Student. Joomla im Angebot ist für mich ein klar schlechtes Zeichen, das würde ich nicht buchen. Gemeine Vorurteile? Vielleicht, aber auch das ist bei der Wahl eines CMS für Angebote für Geld zu berücksichtigen.

Aber mit der nächsten Version kommen doch endlich alle wichtigen Features… Ja, schön. Schon mal die Planungen für TYPO3 5.0 gesehen? Nein? Weil das noch nicht relevant ist, weil es in ferner Zukunft passiert? Genau. Handfeste Defizite in der aktuellen Version mit Planungen für die Zukunft zu entschuldigen, ist nicht zielführend, weil die anderen sich ebenfalls weiterentwickeln. Während in Villabajo noch mit einem benutzbaren Rechtesystem gekämpft wird, redet man in Villarriba von Aspektorientierter Programmierung, MVC, Content-Repositories und (vielleicht endlich mal praxistauglichem) Frontend-Editing.

Empfehlungen, die ich übrigens über die Jahre immer wieder gehört habe, sind ModX (das als Geheimtipp gilt), TYPO3 (das ich selber häufig nutze), Wordpress (weil alle es nutzen, es ein tolles Backend hat und es so schön nah am Code operiert), Textpattern (das ich vom Konzept her gar nicht mochte, das aber von seinen Fans vehement vertreten wird), Drupal (vor allem im Community-Kontext) und verschiedene kleinere recht charmante Systeme.


Objektorientierte Entwicklung vs. PHP 4

05 05 2010

Zur Zeit arbeite ich an einem Wordpress-Projekt und bin nach der Umstellung auf PHP 5.3 (vorher war versehentlich PHP 4 auf dem Webspace aktiv) über etliche Deprecated-Warnungen gestolpert. Die explizite Zuweisung von Objekten per Referenz (also mit =& statt dass das Objekt mit = geklont wird) in der wp-settings.php ist schuld, denn dieses im Grunde erwartungskonforme Verhalten ist seit PHP 5 der Normalfall, Objekte werden jetzt nur noch geklont, wenn man explizit clone benutzt, so dass die explizite Zuweisung per Referenz überflüssig ist und angemahnt wird.

Nun ist es bei einem guten Hoster ein Handgriff ein, die E_DEPRECATED-Warnungen in der php.ini zu unterdrücken, von daher ist das alles halb so wild. Die Sache ist aber ein Symptom eines großen Dilemmas: Will man – aus welchen Gründen auch immer – kompatibel zu PHP 4 bleiben, muss man manchmal Code schreiben, der in PHP ab 5.3 eine Deprecated-Warnung wirft. Das TYPO3-Backend beispielsweise warf bis vor kurzem (vielleicht auch immer noch) ebenfalls Unmengen an Deprecated-Warnungen, weil es intensiven Gebrauch von eregi-Funktionen macht. Die kann man mit gutem Geschwindigkeitsgewinn und ohne Schwierigkeiten mit PHP 4 gegen preg-Funktionen austauschen, hier ist es also lediglich eine Sache von Fleiß; ganz davon abgesehen, dass TYPO3 sowieso seit Jahren kein PHP 4 mehr unterstützt. Die Sache bei Wordpress und einigen anderen Projekten mit Objektorientierten Elementen ist aber eine Zwickmühle.

Wobei es in meinen Augen keineswegs eine Zwickmühle ist, da die Lösung auf der Hand liegt: Man wirft einfach den PHP 4 Support über Bord und hält die Anfeindungen einiger Ewiggestriger aus, die nicht wissen, wie sie bei ihren Webspace eine aktuelle PHP-Version umstellt. Diese Anfeindungen kommen leider vor, und sind einer der Gründe, wieso ich zu Serendipity keinen Code mehr beitrage. Serendipity bewahrt wie Wordpress noch immer die PHP 4 Kompatibilität und steht einer zukunftsgerichteten Weiterentwicklung damit massiv im Weg. Wordpress wird wohl ab Version 3.0 (also ab demnächst) PHP 5 vorraussetzen, damit es endlich nach vorne gehen kann. Das ist im Falle von Wordpress auch bitter nötig, wenn man sich den mitunter grauenerregenden Code anguckt. Die aktuelle Beta 1 wirft aber leider immer noch die Deprecated-Warnungen.

Es gibt so etwas wie Codehygiene. Ich frage mich, wieso sich so viele Softwareprojekte mit aller Macht dagegen stemmen, ihren Code ein wenig zu warten. Eregi-Funktionen gelten schon seit mindestens fünf Jahren als um Größenordnungen langsamer, als ihre preg-Pendants. Wieso zur Hölle liest man dann als Abhilfe für Deprecated-Meldungen immer und überall, dass man die Warnungen ausschalten soll? Wo ist das Problem, sich einmal eine Nacht hinzusetzen und die veralteten und langsamen eregi-Funktionen zu eliminieren? Wieso wird unter Verzicht auf fundamental wichtige Programmiertechniken der Objektorientierung auch mehrere Jahre nach Einstellung des Supports für PHP 4 so sehr daran geklebt? PHP 4 ist veraltet, langsam und behindert massivst eine saubere Programmierung. Das ist sogar so offensichtlich, dass es jedem auffallen müsste, der nur ansatzweise objektorientiert mit PHP programmiert. PHP 4 fehlt es an fundamentalen Funktionalitäten an allen Ecken und Enden, nicht nur bei der Objektorientierung. Bitte, hört auf mit der falsch verstandenen Rückwärtskompatibilität und blickt mal nach vorne.

P.S. Ich bin übrigens latent auf der Suche nach einem neuen Blogsystem. Serendipity möchte ich den Rücken kehren, weil sich an der Front scheinbar nichts mehr tun wird. Auf PHP 5 umstellen? Neue Programmierkonzepte zulassen? Nix da, alles bleibt, wie es ist. Ein System, dessen Core so ungerne angefasst wird, ist nicht meins. Davon abgesehen, dass der Core in meinen Augen sowieso gar kein HTML ausgeben sollte. S9Y ist super stabil und funktioniert bei mir seit nunmehr fast sechs Jahren ohne jedes nennenswerte Problem vor sich hin, aber in etwa so lange hat sich auch nicht mehr wirklich etwas nennenswertes weiter entwickelt; das stimmt zwar nicht ganz, aber die Änderungen blieben insgesamt doch sehr dezent. Wordpress ist leider keine Alternative, auch wenn das Backend großartig ist. Ein Blick in den Core an beliebiger Stelle sollte ausreichend Anlass geben, das System nicht zu wollen. Was ist eigentlich aus Habari geworden?


Wordpress hat schlimmen Code, andere aber auch

07 09 2009

Wer ernsthaft beruflich oder auf hohem Niveau in seiner Freizeit mit PHP arbeitet und älter als 15 ist, dem stehen wahrscheinlich beim Blick in den Quellcode von Wordpress die Haare zu Berge. Code is art ist ein wunderschöner Wordpress-Slogan, der in dem Kontext aber wirklich mehr als unangebracht ist. Schlimmer als der zusammengezimmerte Kern von Wordpress, der aktuell mal wieder mit einem notdürftigen Flicken gegen den grassierenden Wordpress-Wurm repariert wurde, sind aber die meisten PlugIns. Viele sind offenbar von blutigsten PHP-Anfängern schnell und ergebnisorientiert runterprogrammiert worden, was von Wordpress ja geradezu provoziert wird und was auch großen Anteil an der Beliebtheit des Systems trägt. Jeder ahnungslose Anwender kann mit ein paar Zeilen PHP-Code schnell das erreichen, was er gerade braucht. Das erinnert mich an meine Anfänge mit PHP im Jahr 2001 mit dem damals herausragenden phpBB 1.4. Hier war im Grunde alles hartkodiert und wenn einem irgendwas nicht gefiel, hackte man irgendwie im Quellcode herum. Ein Templatesystem wurde erst mit Version 2 eingeführt und auch hier hackte man noch alle möglichen MODs in das System, was ein Update praktisch unmöglich machte. Folge waren haarsträubende Sicherheitslücken, die nicht gestopft wurden. Wordpress vermeidet dieses Modding immerhin mit einem sehr flexiblen PlugIn-System, der Zugang für Dilettanten und Anfänger wurde dadurch aber noch einfacher. Das Ergebnis sehen wir zur Zeit, Gerrit van Aaken hat das schon zusammengefasst: Bei Wordpress hilft nur ein schmerzhafter Neuanfang, so wie TYPO3 das momentan auch vollzieht und wie phpBB das auch schon zwei mal hinter sich hat.

Nun will ich nicht so viel auf Wordpress rumhacken, wenn ich es selber gar nicht benutze. Im Grunde ist das System großartig, wenn man die miese Codebasis ignoriert. Ich will auf etwas ganz anderes hinaus, nämlich die Codequalität von Serendipity-PlugIns. Wann immer ich ein Seitenleisten-PlugIn installiert habe, musste ich dessen Code bearbeiten, weil die HTML-Ausgabe standardmäßig schlimm aussah oder gar fehlerhaft war. Das PlugIn-System von S9Y ist auf den ersten Blick recht kompliziert, was totale Dilettanten vom PlugIn schreiben abhält; das ist schon mal gut. Trotzdem ist die Codequalität der PlugIns oft eher mäßig. Fast überall wird zum Beispiel der HTML-Code mit echo direkt da ausgegeben, wo er anfällt, natürlich mit irgendwelchem HTML-Code drumherum, den der Autor gerade für angemessen hielt. Kein Wunder also, dass der Output nicht immer angemessen ausfällt. Ich habe vor einiger Zeit ein eigenes Seitenleisten-PlugIn für Twitter geschrieben, weil das alte u.a. genau dieses Problem zeigte. Ein Kernfeature meines PlugIns war der Einsatz einer Templating-Engine. Die zu implementieren war PHP-seitig ein Kinderspiel für jeden halbwegs erfahrenen PHP-Programmierer. Die Frage ist nun, warum zur Hölle von den paar offiziell verfügbaren PlugIns noch fast keines so ein Templating-System mitbringt? Es täte S9Y wirklich immens gut, wenn sich mal jemand hinsetzen würde und ein paar der PlugIns auf den aktuellen Stand bringen würde. Also zumindest eine Templating-Engine einbauen und die Ausgabe entsprechend anpassen, aber auch manch andere Routine könnte mal überarbeitet werden. Gegenüber Wordpress ist das Jammern auf hohem Niveau, aber auch S9Y ist merklich in die Jahre gekommen, vor allem seine PlugIns.

Wenn ich sowas lese, pflege ich zu antworten, derjenige soll nicht jammern, sondern selber anpacken. Das gilt auch für mich, sicher. Allerdings habe ich immens schlechte Erfahrungen mit meinem Twitter-PlugIn gemacht. Das einzige konstruktive Feedback kam vom unglaublich engagierten Chefprogrammierer selber, der mir meinen ursprünglich geplanten Arbeitsaufwand mit Änderungswünschen um das Mehrfache aufgeblasen hat. Am Ende war das neue PlugIn wirklich großartig geworden, ich bin richtig stolz darauf. Die ganzen angeforderten Änderungen kreisten alle um funktionale Anpassungen an das alte PlugIn, so dass mein neues PlugIn alle Funktionen des alten hatte, nur eben diesmal in gut. Dann kam das Problem: Es gab kein Feedback von Dritten und stattdessen hat mir ein ätzender Troll auch noch ans Bein gepisst und einen extra Thread im Forum aufgemacht, um mich als Schnösel zu dissen (weil ich PHP4 Nutzer, die PHP4 Kompatibilität einfordern, als Ewiggestrige bezeichnet habe und er sich davon angesprochen fühlte). Ich habe mein Engagement für S9Y daraufhin nach ein paar Tagen Diskussion eingestellt, denn für kostenlose und gute Programmierarbeit möchte ich echt alles andere haben als von irgendwelchen undankbaren Typen beschimpft zu werden. Mein PlugIn ist übrigens nie im Repository erschienen und stattdessen gibt es ein funktional wirklich krasses anderes PlugIn; hoffentlich ist wenigstens meine Arbeit da eingeflossen und war nicht völlig umsonst. Wie auch immer: Ich werde keine Arbeit mehr zu S9Y beitragen; wenn ich etwas neu baue, reiche ich das im Forum ein und wenn sich niemand drum kümmert, soll mir das egal sein.


Nur halb so schlimm: Eigene Short-URLs

05 09 2009

Vor einiger Zeit habe ich gefordert, dass größere und oft bei Twitter verlinkte Seiten eigene kurze URLs für ihren Content anbieten. Neulich erst habe ich mir dann Gedanken gemacht, wie man Short-URLs und so verüberflüssigen kann. Auf diese große Lösung mag ich nicht warten, aber zumindest bei der kleinen Lösung kann ich mit gutem Beispiel voran gehen. Also habe ich gerade schnell ein kurzes Script geschrieben, das kurze URLs für mein Blog in die vollen URLs umsetzt. Zum Beispiel lautet für diesen Eintrag die kurze URL http://spackblog.de/668, was mit 23 Zeichen deutlich kürzer ist als die verdammt lange volle URL und vor allem kürzer als 30 Zeichen, ab denen Twitter ungefragt mit bit.ly verkürzt.

Ich hatte eine Weile überlegt, diese Funktionalität als Serendipity-PlugIn zu veröffentlichen, aber das habe ich aus verschiedenen Gründen nicht gemacht. Ein Grund ist, dass mich ein undankbarer Vollspacko im S9Y-Forum angesaugt und als Schnösel bezeichnet hat, als ich mein letztes PlugIn dort vorgestellt habe. Ich will da gar nicht genauer drauf eingehen, Folge ist jedenfalls, dass meine Lust auf die extra Arbeit für ein S9Y-PlugIn dadurch doch sehr gesunken ist. Ein weiterer Grund ist, dass der generierte Link irgendwo im Template und noch mal im Header positioniert werden muss, damit es Sinn macht. Das kann ein PlugIn nicht flexibel alleine regeln. Man könnte den Link allenfalls dort hinpacken, wo momentan auch die Tags sind, aber das gefällt sicher nicht jedem. An die .htaccess des Blogs, oder wenn dieses in einem Unterverzeichnis steckt auch an die .htaccess der Domain muss man auch noch ran. Spricht alles gegen ein PlugIn. Sowas gehört in meinen Augen sowieso in den S9Y-Core.

Das Script funktioniert super simpel. Alles fängt mit einer Anweisung in der .htaccess an, die alles, was nach kurz-URL aussieht an das Script weiterreicht:

RewriteEngine On
RewriteBase /
RewriteRule ^(e|a|c)?([0-9]+)/?$ s9y_shorturl.php?type=$1&id=$2 [NC,L]

Man kann schon sehen, dass das Script auch kurze URLs für Kategorien und Autoren unterstützt. Der spannende Teil im Script selber ist die Datenbankabfrage, die die kurze URL aus der Datenbank ausliest:


// open a database-connection
$dbh = new PDO('mysql:host=' . $db_server . ';dbname=' . $db_database, $db_user, $db_pass);

// prepare the statement
$stmt = $dbh->prepare('SELECT permalink
  FROM s9y_permalinks
  WHERE type = :type
    AND entry_id = :entry_id
  LIMIT 1');

// execute the statement
$stmt->execute(array(':type' => $type, ':entry_id' => $entry_id));
  
// read the returned urlpart
$urlpart = $stmt->fetchColumn();

// close the database-connection
$dbh = null;

// redirect the user with a 301 status code (moved permanently)
header('Location: ' $blog_base_url . $urlpart, true, 301);
exit;

Der Rest vom Script besteht aus Fehlerbehandlung und Variablen-Vorbereitung, die ich hier mal weg lasse. Der ganze Code findet sich im Nachtrag zum Download. Damit ist der erste Teil schon fertig, der die kurzen URLs auflöst. Für die kurzen URLs werden einfach die von Serendipity vergebenen IDs genutzt, der Parameter type kann übrigens 'entry', 'category' oder 'author' sein. S9Y führt eine eigene Tabelle für die Zuordnung von IDs und URLs, die man nur abfragen muss.

Der zweite Teil ist leider Handarbeit im Template. Man muss dazu an der Stelle, wo der Link auftauchen soll folgenden Code in der entries.tpl platzieren: <a href="http://SHORTURL_DOMAIN/{$entry.id}">http://SHORTURL_DOMAIN/{$entry.id}</a>. SHORTURL_DOMAIN muss natürlich durch die jeweilige URL ersetzt werden, in meinem Fall ist das tatsächlich eine andere Domain als die, auf der das Blog läuft. Ich habe zusätzlich noch in der index.tpl im Headerbereich im {if $entry.id} Block einen Short-URl Autodiscovery Link eingefügt, wie er hier beschrieben ist.

Das ganze arbeitet extrem simpel und macht genau das, was es soll: Wenn jemand meinen Blog bei Twitter und Co. verlinken will, kann jetzt meine 23 stellige Kurz-URL hernehmen und sich so den Scheiß mit den verkürzten URLs sparen, bei denen man nicht weiß, wohin sie eigentlich linken. Alle Seiten sollten solche eigenen Kurz-URLs haben, dann wäre das mit Twitter nur noch halb so schlimm.

Nachtrag 24.02.2011: Weil eine Anfrage nach dem vollen Code kam, habe ich den mal hübsch gemacht und zum Download bereit gestellt. Das Script selber hat außer PHP 5.1 mit PDO keine Abhängigkeiten und stört auch andere Scripte nicht, solange sich die Rewrite-Regeln in der .htaccess vertragen. Wer ganz sicher gehen will, betreibt das alleine auf einer (Sub-)Domain. Viel Spaß damit. Ach ja, Lizenz ist cc-by-sa, das sollte genug Freiheiten lassen.


Toolbar Wut

20 08 2009

Ich bin genervt, weil immer mehr Programme einem irgendwelche Toolbars oder andere Browser Extensions aufdrängen. Aktueller Höhepunkt: Das zur Zeit verteilte Java Update (JRE 6 Update 15) bringt voreingestellt die Yahoo! Toolbar für den Firefox mit. Ich sitze ständig an fremden Rechnern und werfe den Mist überall wieder runter, das nervt total. Auch Skype macht mich krank, weil es bei jedem verfickten Update seine unfassbar bremsende Firefox Extension wieder installiert, sogar ohne Nachfrage. Eine Kundin rief mich mal völlig genervt an, dass ihr Internet seit ein paar Tagen so unglaublich träge geworden wäre. Was war los? Ich wäre schon fast hin gefahren (50km), habe aber vorher noch mal per Fernwartung auf ihren Rechner geguckt. Und siehe da, die Skype Extension hat für das Suchen von Telefonnummern auf Websites so unglaublich lange gebraucht, dass es sich merklich langsamer surfte. Schnell deaktiviert und schon fluffte es wieder.

Um mir meine Wut mal von der Seele zu schreiben, hier mal eine unvollständige Liste der von mir eingesetzten lästigen Programme:

  • Java Runtime Environment 6 Update 15: Vor einigen Versionen hat einem das Java Update einen Link zum Download von OpenOffice.org auf den Desktop gelegt. Das ist nervig, aber ohne echte Folgen. Die aktuelle Version bringt aber voreingestellt die Yahoo! Toolbar für Firefox mit. Das geht gar nicht: Wenn kostenlose Software wie IrfanView sich ein paar Euro mit Toolbars dazu verdient, ist ist das eine Sache. Wenn aber eine Runtime sowas mitbringt, ist das eine schlichte Unverschämtheit. Als ob Sun kein Interesse daran hätte, ihre Java Runtime auf möglichst vielen Systemen in aktueller Version installiert zu haben.
  • Skype installiert bei jedem Update (das sowieso praktisch eine Neuinstallation ist) eine Firefox Extension, die den Browser total ausbremst. Unfassbar. Dabei geht es nicht um Werbung, sondern um Absatzsteigertung: Die Extension macht Telefonnummern auf Websites anklickbar, so dass man diese direkt über Skype anrufen kann, was natürlich kostenpflichtig (und nicht mal wirklich billig) ist. Auch hier: Lästig und unverschämt, zumal die Installation ungefragt geschieht und sehr störende Nebeneffekte (angezogene Firefox Handbremse) mit sich bringt.
  • Die Daemon Tools binden CD-Images bequem als virtuelles Laufwerk ins System ein und tun das sehr zuverlässig seit vielen Jahren. Früher war der Hauptzweck, den Kopierschutz von Spielen zu emulieren, heute muss man (ich zumindest) alle Nase lang auf CD Images zugreifen, vor allem auf Netbooks. Die aktuelle Version bringt eine unfassbare Konfigurationsorgie mit, damit man weder Suchseiten noch Toolbars mitinstalliert. Das kann man alles abstellen, aber es ist extrem nervig. So nervig, dass ich seit vielen zufriedenen Jahren nun auf der Suche nach einem Open-Source Image-Mounter bin. Zudem sind die Daemon Tools nur noch für die private Nutzung kostenlos und kosten sonst richtig Geld. Warum kann eigentlich nicht mal Windows 7 ISO-Images mounten? Immerhin kann Windows 7 ISO-Images schon mal brennen.
  • Der Adobe Reader haut bei jedem (dringend nötigen!) Update immer wieder ungefragt sein Symbol auf den Desktop. Das wäre nur halb so bescheuert, wenn dieses Icon irgendeinen nennenswerten praktischen Nutzen hätte. Hat es aber nicht, oder kennt ihr jemanden, der einfach so den nackten Adobe Reader startet? Wozu auch? Seine einzige Funktion ist das Anzeigen von PDF-Dateien und die öffnet man mit einem Doppelklick. Wir sind ja nicht auf dem Mac, wo man alles per drag and drop öffnet und die Voschau des Systems PDF-Dateien anzeigt.
  • IrfanView ist ein großartiges Programm, aber leider seit einiger Zeit eine stete Quelle des bei der Installation vorausgewählten Google Desktops. Bei der Installation lässt dieser sich zwar leicht abschalten und ich habe hier vollstes Verständnis für das Ansinnen, ein paar zusätzliche Einnahmen zu generieren. Aber der Google Desktop ist aus Datenschutzsicht ein sehr spezieller Fall und deswegen sehr fragwürdig.
  • Winamp bringt, wenn man nicht aufpasst, auch eine Menge Zeugs mit. Das ist einer der Gründe, wieso ich Winamp seit einigen Jahren nicht mehr benutze.
  • Apple ist der König unter den nervigen Mitbringselschnürern. Installiert man notgedrungen iTunes, weil man dem iPhone oder einem iPod anheim gefallen ist, landet automatisch das verhasste Quicktime samt unglaublich überflüssigem Tray-Icon auf dem Rechner. Will man Quicktime alleine installieren, muss man gut aufpassen, um nicht iTunes gleich mitinstalliert zu bekommen. Das alleine wäre schon lästig, aber wenn man nicht aufpasst, hat man bald auch noch Safari auf dem Rechner, denn bei den regelmäßigen Updates von iTunes und Quicktime ist Safari immer wieder aufs neue vorausgewählt. Das geht wirklich gar nicht! Apple ist derart dreist, was das angeht, dass man echt nur den Kopf schütteln muss: Apple ist das neue Microsoft, nur viel schlimmer.
  • HP hat es sich bei mir ebenfalls verscherzt: Die den Multifunktionsgeräten beiliegende Softwareunverschämtheit belegt nicht nur einige hundert Megabyte Platz auf dem Rechner, indem sie ein ganzes Bündel an zusätzlicher und überflüssiger Software mitbringt. Das wäre alleine schon zum kotzen. Dass der Mist dann leider häufig nicht funktioniert und ich das Zeug über ein Jahr verteilt sage und schreibe 10 mal auf Anweisung des (durchaus guten) Hotline-Chats jeweils eine geschlagene Stunde lang neu installiert habe, weil wieder irgendetwas nicht mehr lief, war für mich Grund genug, auf absehbare kein HP Gerät mehr zu kaufen oder zu empfehlen.

Diese Liste ist alles andere als vollständig und spiegelt nur die Software wieder, die ich selber benutze oder bei Kunden sehe. Teenager-Rechner kann man übrigens gar nicht betreten vor lauter ICQ-, MSN- und Skype-Messengern, die beim Start online gehen und alle ihre eigenen Toolbars und was auch sonst immer mitbringen.


Statische Methode ruft Constructor auf

14 04 2009

Ich hocke gerade an einem mod_rewrite Problem mit dem vielversprechenden CMS Concrete5. Jetzt muss mir mal jemand erklären, wieso etwas so programmiert sein könnte, wie es da gemacht wurde:

Ganz am Anfang des Ablaufs (im Dispatcher) wird indirekt die Klasse "Request" initialisiert: $req = Request::get();. Diese statische Methode get() holt sich die Request-Parameter (da liegt übrigens mein Problem) und instantiiert dann mit $req = new Request($path); eine neue Instanz der eigenen Klasse und gibt diese zurück. Jetzt frage ich mich, warum zur Hölle instatiiert nicht der Dispatcher die Klasse und deren Constructor ruft die get() Methode auf (oder macht die paar Zeilen direkt im Constructor)? Warum wird von einer Klasse eine statische Methode aufgerufen, die eine neue Instanz der eigenen Klasse aufmacht und zurückgibt? Wozu also der Umweg über die statische Methode? Ist sowas irgendwo üblich? Wenn ja, was wird bezweckt?

Mein Respekt gilt schon mal allen, die bis hierher gelesen haben. ;)


Eine kleine Fuck IE6 PHP Klasse

24 02 2009

Aktuell soll ja wieder mal der IE6 endlich sterben. In Norwegen sind einige große Nachrichtenseiten mit einer IE6-Warnung unterwegs und der IE6 Death March ist auch unterwegs. Ich verkaufe schon seit einiger Zeit IE6-Anpassungen nur noch als extra zu bezahlende Zusatzoption, wobei es bei mir dank semantisch sinnvollem Code hier nur um optische Feinheiten geht, die ich für entbehrlich halte. Wie auch immer: Bisher hat kein Kunde Aufpreis für den IE6 zahlen wollen. Geht doch.

Nun baue ich gerade meine Lifestream-Seite auf und befreie mich bei der Gelegenheit von jedem IE6-Mist. Meine Contentboxen hat der IE6 schlicht gar nicht angezeigt (wohl aber konnte man die unsichtbaren Links anklicken), so dass ich mich entschieden habe, dem IE6 und älteren Versionen einfach alle Styles vollständig wegzunehmen und stattdessen eine rote Alarm-Box einzublenden. Die Inhalte bleiben vollständig nutzbar, es sieht nur total scheiße aus. Sehr befriedigend, kann ich so den IE6-Nutzern doch etwas von der Hässlichkeit vermitteln, die dem IE6 innewohnt.

Zu diesem Zweck habe ich eine kleine statische PHP Klasse geschrieben, die ich hiermit gerne zur Verfügung stellen möchte. Wer Lust hat, kann die einfach für sich anpassen und in seine Projekte einbinden. Ich habe mich gegen eine clientseitige Lösung mit CSS und/oder JavaScript entschieden, weil ich "gute" Besucher nicht mit dem für sie sowieso unsichtbaren IE6-Code belasten wollte. Ich denke, das ist eine gute Idee.

Die Klasse ist recht simpel gestaltet und schreit geradezu danach, bei Bedarf verfeinert zu werden (mir reicht das erst mal so). Also in Kürze:

  1. Einbinden der Klasse, etwa indem man den Code direkt in sein Projekt kopiert oder als Datei abspeichert und mit require_once('PFAD/fuck_ie6.class.php'); einbindet.
  2. Da die Klasse statisch ist, muss sie nicht instanziiert werden. Man ruft also die drei statischen Methoden wo man sie braucht.
  3. Folgende Methoden und Variablen stehen zur Verfügung:
    • fuck_ie6::is_ie6() gibt true zurück, wenn der Besucher mit einem IE4, 5 oder 6 unterwegs ist, sonst false. Praktisch, wenn man bestimmte Inhalte wie Stylesheets ohne Conditional Comments für diese Besucher aus- oder einblenden möchte. Im Prinzip ist das nur eine sehr simple RegEx.
    • fuck_ie6::print_style() und fuck_ie6::print_alert_box() printen den Inhalt der Variablen $alert_style und $alert_content in Abhängigkeit vom Browser des Besuchers. Die beiden Methoden ersparen einem also nur die if-Abfrage mit fuck_ie6::is_ie6() im Template ein.
    • Die beiden Variablen $alert_style und $alert_content enthalten den Style und den Code, die für die Alarm-Box genutzt werden sollen. Hier finden Anpassungen an die eigenen Wünsche statt.

<?php
/**
* a very simple static class for sniffing Internet Explorer 6 and below
*
* can detect whether the user comes with an annoying IE (version 4,5 or 6)
* additionally holds content and styles for a red alert box
*
* @author	Gregor Nathanael Meyer <Gregor [at] der-meyer.de>
* @license  http://creativecommons.org/licenses/by-sa/3.0/de/ Creative Commons cc-by-sa
* @version  0.1  first release
*/
class fuck_ie6
{
  /**
    * the style block used for the alert box
    * @static
    */
  public static $alert_style = '  <style>
  .errorBox {
    background: #fbe3e4;
    color: #8a1f11;
    border: 2px solid #fbc2c4;
    width: 80%;
    padding: 25px;
    margin: 0 auto;
    font-size: 1em;
    line-height: 1.3em;
  }
  </style>';
  
  /**
    * the HTML of the alert box
    * @static
    */
  public static $alert_content = '<p class="errorBox"><strong>Alarm:</strong> Offensichtlich bist Du mit einem alten Internet Explorer (6.0 oder älter) unterwegs. Da dieser Browser aus dem Jahr 2001 die auf dieser Seite genutzten modernen Webstandards nicht hinreichend unterstützt, habe ich das visuelle Beiwerk für Dich deaktiviert. Die Inhalte sind weiterhin erreichbar.</p>';
  
  
  /**
    * the IE6 detector function
    * uses just a simple RegEx to read the UA string
    * @static
    */
  public static function is_ie6()
  {
    return preg_match('#^Mozilla/4.0 \(compatible; MSIE [456]#i', $_SERVER['HTTP_USER_AGENT']) ? true : false;
  }
  
  /**
    * prints the style block in case of IE<=6
    * @static
    */
  public static function print_style()
  {
    if ( self::is_ie6() )
    {
      echo self::$alert_style;
    }
  }
  
  /**
    * prints the alert box content in case of IE<=6
    * @static
    */
  public static function print_alert_box()
  {
    if ( self::is_ie6() )
    {
      echo self::$alert_content;
    }
  }
}

Ein Beispiel könnt ihr bei meinem Lifestream sehen.