DevHouse Cologne

*seufz* Schon wieder ein neuer Begriff. Der mamblende Bumi weist auf ein DevHouse Cologne hin. Ich weiß nicht ob ich's richtig verstanden hab, man trifft sich und arbeitet gemeinsam an Projekten die man im Normalfall alleine in einem vielfachen der Zeit machen würde. Hört sich sehr kreativ und produktiv an finde ich.

JQuery: Collapsible menu v2

I'm currently setting up a website for Marion and use JQuery for some things like collapsing menus. I found an example for this at Rowan's PixelCarnage which is fine but only toggles the first element after the click-handler. My version does better using the parent().children() trick to toggle everything and then toggles the click handler again. Also the menu can be done recursively. I've kept it very general to use a h2 or something for handler but to stay accessible I strongly advise you to use a-tags only.

The javascript

$(document).ready(
	function() {
	// hide everything on the same level as the .handler
	$(".menu > .handler").parent().children().toggle();
	
	$(".menu > .handler").click(function() {
		$(this).parent().children().toggle();
		$(this).toggle();
	});
	}
);

Alternative to show only one at once

$(document).ready(
	function() {
	// hide everything on the same level as the .handler
	$(".menu > .handler").siblings().hide();
	
	$(".menu > .handler").click(function() {
		$(".menu > .handler").siblings().hide();
		$(this).siblings().show();
	});
	}
);

Another alternative to show/hide all at once

As wished from Thamás, a simple way to have link for showing/hiding all menus. I didn't use HTML but inserted the links via JQuery because they are not needed when there's no Javascript or JQuery at all. Javascript:
function expandAll() {
	$(".menu > .handler").siblings().show();
}
function hideAll() {
	$(".menu > .handler").siblings().not(".show").hide();
/* note that I've added a not(".show") here to make sure you can still
   show some paragraphs if you want that. simply give them a class="show" */
}

$(document).ready( function() { $(".menu:first").before('\
	<a href="javascript:expandAll();">expand all</a> \
	<a href="javascript:hideAll();">hide all</a> ');
});

Some example HTML

<div class="menu">
	<a href="#" class="handler">menu 1</a>
	<p>Content menu 1</p>
	<p>second paragraph</p>
</div>

<div class="menu">
	<h2 class="handler">this is a h2</h2>
	<p>and as you see you don't need an a-tag as handler</p>
	<p>more content.</p>
	<div class="menu">
		<a href="#" class="handler">submenu</a>
		<p>content submenu</p>
	</div>
</div>

Verbesserungen an wp-feedstats

In meiner kleinen Reihe der Wordpress-Plugin-Verbesserungen kommt heute WP-FeedStats (dt. Version bei Frank Bueltge) dran und ich sag's gleich: es gibt viel zu tun. Was mir negativ an WP-FeedStats auffiel war dass es meine SQL-Queries auf normalen Blogseiten von 34 (was ansich ja schon viel ist) auf 50 hochtrieb. Ja wirklich, ein Plugin das nur greifen sollte bei Zugriff auf die Feeds verursacht zusätzliche 16 Queries auf stinknormalen Blogseiten. Hilfe musste her und nebenbei traf ich auf eine weitaus größere Ressourcen-Verschwendung, mehr dazu im Abschnitt zu den Hooks. Wer sich das fertige, verbesserte Plugin ansehen bzw. verwenden (download, umbenennen in "wp-feedstats.php" und upload) will (aber Achtung, Frank Bueltge hat eine neuere Version): wp-feedstats.phps

Erste Schwachstelle: globale Variablen

Globale Variablen sind super, sie sind überall, man kann sie überall verwenden und man kann sie überall ändern. Aber man sollte die Finger von ihnen lassen und sie sparsam verwenden. WP-FeedStats hat knapp zehn gobale Variablen die obendrein beim einbinden des Plugins auch alles gesetzt werden mit Hilfe von get_option(). Es ist ganz und gar nicht schlimm get_option() bzw. get_settings() in einer Funktion mehrfach aufzurufen. Denn für's Abfragen von Optionen hat Wordpress einen Cache. Den Cache darf man dann aber auch nie vergessen, Änderungen an Optionen sollten immer gleich an update_option() übergeben werden sonst sind sie beim nächsten Mal einfach weg. Einfach war der Verzicht auf die Globals bei WP Feedstats nicht, denn jetzt mussten auch die unzähligen Verwendugnen der Datenbank mit $wpdb->prefix . 'fs_...' ersetzt werden.

Zweite Schwachstelle: Fehlende oder falsch Verwendung von Hooks

Hooks sind gewissermaßen Kleiderhaken an der man seine Jacke aufhängen kann. Wird der Hook aufgerufen, also der Langfinger kommt und geht einen Kleiderhaken nach dem anderen durch, dann wird die Funktion ausgeführt und die drei Havannas die ich in meiner Jackentasche hatte sind futsch. Es gibt in Wordpress für so ziemlich alles einen Hook. Es gibt so viele dass es gleich mehrere Listen gibt die alle einen anderen aktuellen Stand haben. Wer seinen Hook nicht findet der kann den Quellcode auch noch auf do_action() und apply_filter() durchsuchen. WP-Feedstats lässt beim Einbinden des Scripts zwei Funktionen aufrufen die im Grunde die Datenbank installieren wollen (immerhin wurden die maybe-Funktionen verwendet). Ich denke mal wir können uns darauf verlassen dass die notwendigen Tabellen in der Datenbank noch sind und daher reicht es diese zwei Funktionen beim Hook activate_ einzuhängen: add_action('activate_' . trim(__FILE__), 'fs_generateDB'); Weitaus gravierender war es die zentrale Funktion zum Datensammeln in den Hook the_title_rss einzuhängen. Richtig wäre es hier gewesen (wie auch bei den meisten anderen Statistik-Plugins) den Hook parse_query zu verwenden. the_title_rss wird von PHP mehrmals aufgerufen, je nachdem wieviele Posts man im Feed stehen hat. Ich hab natürlich auch nachgemessen und was glaubt ihr wohl was der Unterschied war? Bei 30 Posts waren es 40 Queries ohne das Plugin, 40 mit dem Hook parse_query und mit the_title_rss unglaubliche 280 (zweihundertachtzig) Queries.

Dritte Schwachstelle: Lokalisierung (Datum)

Gern verwenden Plugin-Autoren wenn sie ein Datum anzeigen wollen einfach date(); ohne dran zu denken dass der User ja im Wordpress ein eigenes Datumsformat vergeben kann. Also immer date(get_option('date_format')); (bzw. das Gleiche mit time_format) verwenden.

Weitere Möglichkeiten

Ein paar Sachen kann man am Plugin noch ändern. Zum einen die Verwendung von gettext statt eines Arrays mit Übersetzungstabelle. Als zweites kann man die Funktionen auch in einer Klasse zusammenfassen, das ist meist sinnvoll wenn mehrere Funktionen auf viele verschiedene Daten zugreifen. Bei WP-Feedstats könnte ich mir vorstellen eine Klasse für die Statistiken einzuführen. Diese Klasse hätte dann Methoden zum Speichern und Abrufen beliebiger Daten. In Konsequenz sollte man die Funktion ruhig allgemein halten um sie auch andertweitig zu verwenden. Im Plugin verbleiben dann Funktionen zur Initialisierung, Erhebung der Daten und Ausgabe der Statistik. UPDATE 21:42: Das Plugin hat beim Aktivieren nicht die notwendigen Funktionen aufgerufen. UPDATE 13. Januar: Frank Bueltge hat meine Änderungen aufgenommen und die Version 2.0 veröffentlicht.

Plugin-Programmierung, wie man's nicht macht (yigg)

Weil's letztens so lustig war das Wordpres-Plugin von Linklift auseinander zu nehmen kommt heute Yigg dran. Ich hab jetzt nur mal vier Punkte aufgeführt die aber durchaus als allgemeingültig betrachtet werden dürfen. Generell sollte man meine Artikel dieser Art als Hilfe für andere Einsteiger ansehen, nicht als Kritik an den Programmierern.
  1. Ganz Gallien ist in drei Teile geteilt… (Dateien)
  2. Bei den Helvetiern war bei weitem am angesehensten und reichsten Orgetorix (Caching)
  3. Es gab im ganzen nur zwei Wege… (Datenbank)
  4. Cäsar wird gemeldet, die Helvetier hätten… (Benutzereingaben)

Ganz Gallien ist in drei Teile geteilt…

Fangen wir mal an: function wpAdminActive() { return (strpos ($_SERVER['PHP_SELF'], 'wp-admin')); } So ganz versteh ich nicht warum der Programmierer diese Funktion geschrieben hat, schön schaut anderes aus. Die Funktion die in der die Zeile vorkommt wird zweimal aufgerufen, einmal außerhalb class YiggWordPressPlugin: if ($_GET['autocomplete'] && $plugin->wpAdminActive ()) $plugin->completeTag ($_POST['yiggTags']); und einmal in der Klassenmethode apiCall($methodName): if ($transport->fault && $this->wpAdminActive ()) $this->setFault ($transport->fault); Der Aufruf außerhalb ist schnell erklärt, über AJAX wird nämlich das Plugin aufgerufen um Infos von der Yigg-Website zu holen. Ich persönlich würde hier die Dreiteilung der Datei yigg.php durchführen. In der ersten datei ist die ganze class YiggWordPressPlugin zu finden und die beiden anderen sind zum einen für die normale Einbindung in Wordpress zuständig, zum anderen für den Aufruf über AJAX. Beim dritten Script kann man gerne auf das Laden des gesamten Wordpress-Unterbaus verzichten. Das einzige Problem dass hier dann auftritt ist der API-Key, der muss dann in die HTML-Ausgabe damit AJAX ihn seinen Requests mitgeben kann. Alternativ kann man ihn auch gleich in das Script schreiben dass die Requests empfängt. Sicherheitstechnisch dürfte die zweite Methode für den API-Key sicherer sein, aber ich denke es ist einfacher sich bei Yigg einen Key abzuholen als die Keys fremder Leute zu sammeln... Man könnte auch versuchen sich den Key beim Laden der Wordpress-Seite per AJAX bei Wordpress abzuholen.

Bei den Helvetiern war bei weitem am angesehensten und reichsten Orgetorix

Und bei Wordpress ist's die option_table die man gern als Cache für kleinere Daten verwendet. Im Yigg-Plugin wäre das durchaus angebracht. Wer sich nämlich wundert warum es etwas länger lädt wenn man einen neuen Post schreiben will, der sollte wissen dass das Yigg-Plugin jedesmal die Kategorien neu lädt. Jedes Mal. Wie oft ändert sich so eine Kategorie wohl? Alle paar Stunden 3600*6? Alle paar Tage 3600*24*3 oder doch nur einmal im Monat 3600*24*30? Also einmal die Daten abholen in die Datenbank speichern, dazu noch ein timestamp und gut ist. Ich seh auch überhaupt nicht ein warum ich von PHP eine AJAX-Anweisung schreiben lasse die dann wieder das gleiche PHP-Script aufruft um Daten abzuholen. Da dreht man sich doch im Kreis!

Es gab im ganzen nur zwei Wege, auf denen die Helvetier …

… Zusatzinformationen in der Datenbank abspeichern konnten. Die Daten die Yigg abspeichern muss sind die Tags und die Kategorie des Posts. Der eine Weg ist die post-Tablle einfach um ein paar Felder zu erweitern. Sehr zukunftsorientiert aber alles wird einmal gehen, selbst Yigg und daher empfehle ich die zweite Möglichkeit. Man muss einfach noch zwei Tabellen anlegen in die die Tags gespeichert werden. Warum zwei Tabellen? (Hier folgt ein Exkurs in die Theorien zu Datenbanken) Zum einen werden Tags (sprich Zeichenketten) sehr häufig verwendet und zum anderen kommen sie mehrfach vor. In der einen Tabelle speichern wir also eine tagid und den Tag ab, in der anderen Tabelle (die Kreuztabelle) die tagid und die postid. Die erste Tabelle bleibt recht klein während die andere wohl schnell wachsen wird. Man kann beides in nur eine Tabelle speichern (Jerome's Keywords ist so ein Beispiel dafür) aber der Speicherbedarf ist höher. Ein weiterer Nachteil von nur einer Tabelle, Abfragen (z.B. welche Tags wurden schon verwendet) sind etwas langsamer und es ist nicht so leicht einen Tag umzubenennen. Wenn wir schon dabei sind über Tags und Datenbanken zu reden. Warum werden Tags immer nur so eindimensional dargestellt? Bei del.icio.us gibt's die Möglichkeit seine Tags zu gruppieren, samt Überschneidungen. Um das in der Datenbank darzustellen fügen wir noch eine weitere Tabelle hinzu. Hier haben wir eine tagid und eine parent_tagid. Die Bezeichnungen des parent_tagid steht auch in der Tabelle der Tags, mehr ist nicht notwendig.

Cäsar wird gemeldet, die Helvetier hätten…

Das ist eigentlich einen Bug-Report wert, Yigg speichert nämlich auch ungültige API-Keys in der Datenbank ab und will diese auch verwenden. Traue niemals einem Benutzer. (Die Überschriften entstammen dieser Übersetzung von De Bello Gallico)

Neues Wordpress-Plugin: WP-Feed304

So, jetzt veröffentliche ich auch mal ein Plugin, es heißt WP-Feed304. Auf die Idee gekommen bin ich über Michael vom Software Guide Blog. Ausgangspunkt was die Auslagerung von Feeds weil diese sehr oft abgerufen werden und damit Traffic verursachen obwohl sich meist nichts geändert hat. Was macht das Plugin? Es speichert Daten und zwar welcher Client (der Einfachkeit halber bestehend aus IP und User Agent) in der letzten Zeit einen meiner feeds abgerufen haben. Falls der Client jetzt nochmal abruft dann sieht mein Plugin das in seinen Daten und gibt einen 304 zurück und beendet die Ausführung des ganzen Scripts auf brutale Art und Weise (das werd ich noch ändern hoff ich). Die Protokollierung geschieht pro feed, man kann also sowohl den normalen als auch den Kommentarfeed problemlos je einmal abholen. An Blogs die sich wieder erwarten doch öfters ändern ist auch gedacht, hier werden die Zähler der entsprechenden Feeds dann auf Null gestellt. Bisher ist die Sache mit den Feeds noch etwas beschränkt, wird aber demnächst hoffe ich mal für alle gelten auch wenn diese eh nur selten abgerufen werden dürften. Zusätzlich gibt's auch noch die Möglichkeit die Feeds manuell auf Null zu setzten bzw. überhaupt auch mal zu schaun welche Clients und IPs denn die Feeds abrufen. Verbesserungen werden noch einige kommen, so watch out!

Objective-C: Schwerer Anfang und Erleuchtung

Ich hab mir für die Weihnachtsferien vorgenommen ein erstes eigenes Programm für meinen Mac zu schreiben, obwohl ich jetzt doch recht viel an meinem Blog rumbastel. Anyway, das erste was ich festestellen musste war dass ich wohl eine neue Programmiersprache lernen muss. Objective-C ist die erste Wahl wenn man mit Cocoa programmieren will und verglichen mit Java gefällt es mir sehr. Bisher bin ich nicht weit gekommen, nur ein paar Klassen als Grundgerüst entworfen und als nächstes kommt die Implementierung eines XML-Parser (irgendwann wird's sicher heißen: „Am Anfang was das XML. … und der Parser sah dass es gut war“) dran. Die Cocoa-eigene Bibliothek wollt ich dafür eher nicht verwenden, lieber libxml2. libxml2 ist in C geschrieben, ich programmiere in Objective-C. Ziemlich lange hatte ich jetzt ein Problem dass es gar nicht gibt weil sich nämlich C-Code als Objective-C kompilieren lässt . Objective-C ist ja nur ein paar ganz wenige Erweiterungen aber keine Veränderungen! Das muss einem schon gesagt werden. Noch ist der Parser nicht fertig, aber wenn's soweit ist werd ich hier den Code posten und ihn für euch zerlegen.

Linklift Plugin auf dem neuesten Stand der Technik

So, jetzt gleich nochmal Linklift, ein Post den ich schon vor Tagen machen wollte. Von Linklift gibt's ein Wordpress-Plugin das ich natürlich gleich verbessern musste, schließlich ist der state-of-the-art nicht umsonst so erstrebenswert. Der Post hier basiert auf der Version des Plugins vom Dezember 2006, die neue Version liegt schon bei Linklift, aber da hat man wohl nicht genug Zeit für... Linklift will seine Daten eigentlich in einer XML-Datei abspeichern was ja nicht verwerflich ist aber da nur wenig Daten anfallen kann man das ganze auch in die Datenbank packen (ich hab z.B. auch die 404s der letzten Woche so gespeichert) und dafür nimmt man set_option bzw. get_option. Aber ganz langsam, machen wir eine Tour durch's Plugin. Als erstes können wir ein paar Sachen löschen weil wir sie nicht mehr brauchen werden, weit oben steht: /* "LL_data.xml" is the local XML-file used to store textlink-data * that has been downloaded from the LinkLift-Server. */ define("LL_TEXTLINK_DATAFILE", "LL_data.xml"); Als nächstes dürfen die beiden Funktionen ll_retrieve_xml_from_file_system() und ll_write_xml_to_file_system($xml) Bekanntschaft mit dem Backspace machen. Kommen wir zur wichtigsten Funktion, ll_textlink_code(): Hier können wir den Teil von // checking local XML file ganz am Anfang der Funktion bis inklusive (filesize(LL_TEXTLINK_DATAFILE) < 40)) löschen und durch folgendes ersetzen: $xml = get_option('LL_TEXTLINK_DATA'); $xml_time = get_option('LL_TEXTLINK_TIME'); if (xml_time < time() - 3600 || strlen($xml) < 40 ) { In der gleichen Funktion kann dann ab // storing/retrieving data to/from local XML-file bis $xml = ll_retrieve_xml_from_file_system(); alles gelöscht werden. Stattdessen speichern wir mit diesen zwei Zeilen die aktuellen Daten in der Datenbank: if (strlen($xml) > 40) { update_option('LL_TEXTLINK_DATA', $xml); update_option('LL_TEXTLINK_TIME', time()); } Den Aufmerksamen fällt sicher auf dass die Daten nur abgespeichert werden wenn sie angewachsen sind. Großer Punkt für Linklift denn ich selber hätt's wohl immer abgespeichert. Und jetzt noch ein paar Fleißaufgaben für die Programmierer bei Linklift:
  • die Optionen werden bei Wordpress üblicherweise mit Kleinbuchstaben benannt.
  • Zwar ist add_option keine Pflicht sondern wird beim erstmaligen Aufruf von update_option aufgerufen um den Eintrag in der Datenbank zu erstellen, aber man kann add_option noch einen Parameter mitgeben damit die beiden Werte gleich zu Beginn geladen werden und sollte sich damit dann immerhin zwei Queries sparen. Wer eine sehr belastete Website hat kann das Flag manuell setzen oder mich einfach um den Code fragen.
  • Ausblenden der Werbung für angemeldete Besucher, empfiehlt sich generell für alle Statistik-Tools und Werbeeinblendungen und lässt sich mit einem if und is_user_logged_in() machen.

alt: #191

I've set up a Wiki for Bonsai. but it's German-only. I've also written a little forum, some things still missing, but look here and test it for me. The data is saved in a tar-archive. Cool, eh? And a bit older is this simply gallery software. I'll put them up as soon as they are finished.