DevHouse Cologne
JQuery: Collapsible menu v2
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
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 vonget_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 aufdo_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 einfachdate(); 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 vongettext 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)
- Ganz Gallien ist in drei Teile geteilt… (Dateien)
- Bei den Helvetiern war bei weitem am angesehensten und reichsten Orgetorix (Caching)
- Es gab im ganzen nur zwei Wege… (Datenbank)
- 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 dieoption_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 diepost-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
Objective-C: Schwerer Anfang und Erleuchtung
Linklift Plugin auf dem neuesten Stand der Technik
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_optionkeine Pflicht sondern wird beim erstmaligen Aufruf vonupdate_optionaufgerufen um den Eintrag in der Datenbank zu erstellen, aber man kannadd_optionnoch 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.