Tutorial: WordPress Plug-in (Sidebar Widget)

Auch wenn es im weiten Internet schon einiges an Tutorials für WordPress Plug-ins gibt, und diese mir auch geholfen haben, hat mir doch immer etwas gefehlt.

Plug-ins für WordPress sind schnell und einfach zu schreiben – wenn man mal weiß wie’s geht. Dennoch sind einige Vorkenntnisse von Vorteil: Programmieren mit PHP und Vorwissen in HTML und CSS (je nach Art des Plug-ins).

Anhand des Reoccuring Date Countdown Plug-ins will ich nicht nur das einbinden des Plug-ins in WordPress und die Präsentation als Widget sondern auch die mögliche Konfiguration über die Widgeteinstellungen und das speichern, laden und entfernen von eigenen Daten aus der Datenbank behandeln (dazu ist kein SQL nötig).

Plug-in Initialisierung

Wie bei Plug-ins allgemein üblich, interagieren auch bei WordPress die Plug-ins über Hooks mit dem Hauptsystem. Über diese Hooks ruft WordPress PHP-Funktionen des Plug-ins auf (Actions). Dazu muss WordPress also mitgeteilt werden, wie unsere Funktionen heißen und wann sie aufgerufen werden.

//Registers the plugin function for plugin init
add_action( 'plugins_loaded', 'rodc_init' );

Die Funktion add_action(..) informiert WordPress, dass bei der Ausführung von plugins_loaded, also beim Laden aller Plug-ins ins System, auch die Funktion rodc_init ausgeführt wird. In rodc_init wird unser Plug-in initialisiert, also alle Funktionen aufgerufen und alle Einstellungen vorgenommen, die benötigt werden damit der Plug-in einsatzbereit ist.

function rodc_init() {
    register_sidebar_widget( 'Reoccuring Day Countdown',
        'rodc_widget' );
    register_widget_control( 'Reoccuring Day Countdown',
        'rodc_control' );
    register_deactivation_hook( __FILE__,
        'rodc_unload');
...
}

Mit den register_*(..) Funktionen wird WordPress bekannt gemacht, was das Plug-in tun wird und welche Funktionen es WordPress zur Verfügung stellt – also welche Funktionen von WordPress zu gegebenen Zeit aufgerufen werden sollen.

  • register_sidebar_widget(..) erklärt dass unser Plug-in ein Widget mit dem Namen Reoccuring Day Countdown anbieten wird und zur Darstellung die Funktion rodc_widget aufgerufen werden soll (callback)
  • register_widget_control(..) macht das selbe für die Einstellungen des Widgets (in WordPress unter Design > Widgets).
  • register_deactivation_hook(..) teilt WordPress mit, dass die Funktion rodc_unload aufgerufen werden soll, wenn das Plug-in deaktiviert wird – da wir eventuell aufräumen und Daten aus der Datenbank entfernen wollen.

Nun müssen natürlich die Funktionen, die WordPress bekannt gemacht wurden auch implementiert werden. Was hilft es aber, eine Funktion zur Konfiguration des Plug-ins anzubieten, wenn noch keine Möglichkeit implementiert ist, auch Daten speichern zu können? Dazu bietet WordPress die Möglichkeit Optionen in der Datenbank abzulegen. Dabei übernimmt WordPress das ablegen und auslesen – es ist also kein SQL nötig. In der rodc_init() Funktion soll WordPress also auch darauf vorbereitet werden, dass Konfigurationsdaten in der Datenbank abgelegt werden.

function rodc_init() {
    ...
    add_option( 'rodc_options', $defaultvalues );
}

add_option(..) fügt unsere Daten so in die Datenbank ein, dass sie über die Funktion get_option('rodc_options') wieder aufgerufen werden können. $defaultvalues ist ein vorher instantiiertes assoziatives Array beliebiger Größe¹ das default Werte enthält (nicht dargestellt). add_option(..) überschreibt aber keine vorhandenen Daten, das bedeutet dass die später geänderten Optionen auch nach neuem initiieren des Plug-ins erhalten bleiben. Wie Optionen wieder entfernt werden können, wird beim deaktivieren (rodc_unload()) gezeigt.

Widget Control

function rodc_control() {
    $option = get_option( 'rodc_options' );
    $old_options = $option;

    //check if something was posted
    if ( $_POST[ 'rodc_submit' ] )
    {
        //get the new values
        $option[ 'title' ] =
            strip_tags( stripslashes(
                $_POST[ 'rodc_title' ] ) );
        $option[ 'upcoming_text' ] =
            strip_tags( stripslashes(
                $_POST[ 'rodc_upcoming_text' ] ) );
    ...
}

get_option(..) holt die gespeicherten Optionen aus der Datenbank, die zum Vergelich mit eventuell  neuen Konfigurationsoptionen nochmals in die Variable $old_options kopiert werden. Um Konfigurationsdaten zu sammeln wird HTML Code verwendet, der dann in der Konfiguration des Wiedgets angezeigt wird (Design > Widgets). Die Daten werden hier per HTTP POST übermittelt, ausgelesen, überprüft und bei Änderungen mit der Funktion update_option(..) gespeichert.

function rodc_control() {
    ...
    if ( $_POST[ 'rodc_submit' ] ) {
        ...
        if ( $option != $old_options ) {
            ...
            update_option( 'rodc_options', $option );
        }
    }

    ...
}

Wie HTML Formulare verwendet werden können, um Benutzereingaben zu erhalten, wird auf SelfHTML gut beschrieben und deshalb hier ausgelassen. Als voreingestellte Werte sollten hier natürlich die bisher geladenen Optionen dienen.

Widget Display

Zur Darstellung des Widget wird in unserer Funktion rodc_widget(..) HTML Code ausgegeben. rodc_widget(..) wird von WordPress jedesmal aufgerufen, wenn eine Seite mit dem Widget dargestellt wird. Auch wenn hier berechnungen gemacht werden ist es also sinnvoll, längere Operationen in Auszulagern oder nur wenn nötig auszuführen und dafür das Ergebnis in den Optionen zu speichern. Der Funktion zur Darstellung des Widgets wird von WordPress ein Argument übergeben, das weitere Variablen enthält die für die Darstellung des Widgets, abhängig vom WordPress Theme, gedacht sind.

function rodc_widget( $args ) {
    extract( $args );
    $option = get_option( 'rodc_options' );
    ...
    $rodc_title = $option[ 'title' ];
    $rodc_upcoming_text = $option[ 'upcoming_text' ];
    ...
    //print the HTML code for the widget
    echo $before_widget;
    echo $before_title;
    echo $rodc_title;
    echo $after_title;
    ...
    echo $after_widget;
}

Ausgelassen wurde im letzten Codeausschnitt mögliche Berechnungen und das holen aller zur Darstellung nötigen Optionen sowie die eigentlich Ausgabe des Widget HTML Codes. extract( $args ) befreit die enthaltenen Darstellungsvariablen des Themes aus dem der Funktion übergebenen Argument. Diese sind $before_widget$after_widget, $before_title und $after_title. Damit werden die Formattierung des Widgets dem Theme überlassen, die Formattierung des Widget Bodys obliegt aber dem Plug-in Entwickler. Die verschiedenen echo $before_widget; geben diese Formattierungen an geeigneter Stelle bei der Ausgabe des HTML Codes zur Darstellung des Widgets mit aus.
$option = get_option( 'rodc_options' ); holt wie auch in der Widget Control Funktion die gespeicherten Daten aus der Datenbank und speichert sie in ein assoziatives Array – das zuvor in rodc_init() angelegt und in rodc_control() mit neuen Daten gefüllt wurde.

Widget Unload

Ein guter Plug-in sollte, meiner Meinung nach, sich auch wieder de-Registrieren, wenn er aus dem System entfernt wird. Dazu wurde mit

register_deactivation_hook( __FILE__, 'rodc_unload');

in der Funktion rodc_init() eine Methode WordPress bekannt gemacht, die beim deaktivieren des Plug-ins ausgeführt wird.

function rodc_unload() {
    remove_action( 'plugins_loaded', 'rodc_init' );

    // if option is not set to keep the config,
    // remove option from database

    $option = get_option( 'rodc_options' );
    if ( !$option[ 'keep_config' ] ) {
        delete_option( 'rodc_options' );
    }

    unregister_sidebar_widget(
        'Reoccuring Day Countdown' );
    unregister_widget_control(
        'Reoccuring Day Countdown' );
}

In rodc_unload() wird zu jeder Funktion die etwas bei WordPress registriert hat, die entsprechende unregister_*(..) Funktion gerufen. Zu register_deaktivation_hook(..) scheint es jedoch keine Unregister Funktion zu geben. So wie die Registrierungsfunktionen entfernt werden, wird auch die Action entfernt. Ob jedoch die Optionen gelöscht werden sollen, entscheidet ein Parameter in den Optionen. Dies muss allerdings manuell in die Widget-Konfiguration (in der Funktion rodc_control()) eingebaut werden. Dafür kann jedoch der Benutzer des Plug-ins entscheiden, ob bei einer Deaktivierung des Plug-ins die eingestellte Konfiguration erhalten bleiben (WordPress Update) oder gelöscht werden soll (bei geplanter Deinstallation des Plug-ins). So ist es möglich, vermüllung der Datenbank mit recht geringen Aufwand Seitens des Entwicklers zu vermeiden – was noch zu selten gemacht wird.

Plug-in Metadaten

WordPress braucht zum Einbinden des Plug-ins in das System noch ein paar Metadaten, da das Plug-in erst nach aktivierung durch den Benutzer ausgeführt wird, vorher jedoch schon Informationen über das Plug-in im System angezeigt werden sollen. Diese Metadaten wie auch die Lizenz unter der das Plug-in veröffentlicht wird, wird über Kommentare als Header der PHP-Datei bekanntgegeben. Für den Reoccuring Day Countdown Plug-in sieht das wie folg aus:

<?php
/*
Plugin Name: Reoccuring Day Countdown
Plugin URI: http://www.massive-squad.de
Description: A reminder for a reoccuring day, with a time countdown as a widget.
Author: Gregor Hengst
Version: 1.0.3
Author URI: http://www.massive-squad.de
*/
/* Copyright 2009 Gregor Hengst (email : grossgeister@gmx.de)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

...

Zusammenfassung

Plug-ins geben über Funktionen der WordPress API dem System bekannt, welche Funktionen des Plug-ins zu welchen Zeiten (bei welchen “Hooks”) aufgerufen werden sollen (Callback). Für Widgets sind insbesondere add_action(..), register_sidebar_widget(..), register_widget_control(..) und zum entfernen register_deactivation_hook(..) relevant. Mit add_option(..) können Daten in der Datenbank abgelegt werden, die über get_option(..) gelesen und mit update_option(..) aktualisiert werden können.

Für ein sauberes Plug-in sollten alle Registrierungen und eventuell auch die gespeicherten Optionen bei deaktivieren des Plug-ins aufgehoben bzw. entfernt werden. Dafür gibt es in der WordPress API unregister_*(..) Funktionen.

Bei der Darstellung des Sidebar Widgets sollten die Formattierungsoptionen, die der Darstellungsfunktion in einem Argument übergeben werden beachtet werden.

Plug-ins sind natürlich nicht auf Sidebar Widgets beschränkt. Es gibt eine große Anzahl an möglichen Hooks für nahezu alle WordPress Bereiche, an die sich Plug-ins anhängen können. Mehr Informationen bietet die WordPress Dokumentation (siehe Links am Ende).

Das gesamte Plug-in zum Anschauen gibt es hier.

——————————
¹ Zumindest habe ich nichts von einer Limitierung durch WordPress gelesen. Die Grenzen der Datenbank gelten natürlich.

Links/Quellen:
http://codex.wordpress.org/Developer_Documentation
http://codex.wordpress.org/Plugin_API
http://codex.wordpress.org/WordPress_Coding_Standards
http://codex.wordpress.org/Writing_a_Plugin

5 Kommentare zu “Tutorial: WordPress Plug-in (Sidebar Widget)”

  1. hc

    Hallo,

    vielen Dank für dieses tolle Tutorial! Ich habe danach (und mit anderen Quellen) ein Widget gebaut und zusammengefrickelt, dass die Mondphasen anzeigt.
    http://www.greier-greiner.at/yatib/

    WordPress hab ich zZ 2.8.6
    Leider funtioniert der activation-Hook bei mir nicht, d.h., es werden keine default-Werte nach dem Aktivieren gesetzt. Der deactivation-Hook funktioniert aber ganz normal, die Daten werden sauber aus wp_options entfernt. Ich habe diesen Code stehen:

    ….
    register_activation_hook(__FILE__, ‘parallax_at_mondphase_activation’);
    register_deactivation_hook(__FILE__, ‘parallax_at_mondphase_deactivation’);

    function widget_parallax_at_mondphase_init()
    {

    …..
    ..

    function parallax_at_mondphase_activation() {

    $option = get_option(‘widget_parallax_at_mondphase’);

    if( false === $option )
    {
    // Set up default options

    $defoptions = array(‘bgrCol’ => ‘C3DFB5′, ‘bgrAlpha’ => 100, ‘fontCol’ => ’000000′, ‘darkSideCol’ => ’080000′, ‘darkSideAlpha’ => 85, ‘frameCol’ => ’000000′, ‘frameAlpha’ => 100);
    $option['uninstall'] = false;

    add_option(‘widget_parallax_at_mondphase’, $defoptions);
    }
    }

    function parallax_at_mondphase_deactivation() {

    $options = get_option(‘widget_parallax_at_mondphase’);

    if( is_array($options))
    {
    delete_option(‘widget_parallax_at_mondphase’);
    }
    }

    Die Optionen werden bei der Aktivierung nicht geschrieben…:(
    Hättest du da einen Tipp, was ich falsch mache. Es ist mein erstes Plugin, bitte nicht gleich schlagen ;)

    thx,
    harald

  2. Gregor

    Hallo Harald,
    freut mich, dass mein Tutorial dir geholfen hat. Konkrete “Fehler” kann ich auf Anhieb nicht erkennen – ich habe bisher auch nur dieses eine WordPress Plug-in geschrieben. Ich sammle einfach mal, was mir auffällt – vielleicht bringt dich das auf die richtige Spur.

    Kommt der Code innerhalb des if( false === $option ) Branch zur Ausführung und parallax_at_mondphase_activation() wird auch ausgeführt? (Ich bin ohne register_activation_hook ausgekommen und der sehr strenge === Vergleich kommt mir ungewohnt vor.)

    $option['uninstall'] = false; bewirkt meiner Ansicht nach übrigens nichts, da $option nach einer lokalen Variable in parallax_at_mondphase_activation() aussieht, Du aber $defoptions speicherst.

    Ich weiß nicht, wie WordPress mit den Optionenbezeichnern umgeht – Du verwendest für die Optionen den selben Namen wie in register_activation_hook (Nur eine Idee).

    add_option fügt die Optionen nur in die Datenbank, wenn unter dem Namen noch keine vorhanden sind. (Sonst: update_option verwenden.)

    Ansonsten, hm. Ich hatte mit den Options erst auch meine liebe Not, setze sie jedoch an etwas anderer Stelle als Du: in der Funktion die von
    add_action( 'plugins_loaded', 'FUNCTION_NAME' );
    aufgerufen wird. An dieser Stelle rufe ich auch die register_* Funktionen.

  3. hc

    Hallo Gregor,

    danke für deine Hinweise!

    Hab jetzt mal den Code etwas aufgeräumt und siehe da, es geht!

    Die default options werden jetzt einfach vorne weg per
    function parallax_at_mondphase_activate() {…
    gesetzt. Das funktioniert, sobald der User das Widget auf die Sidebar zieht.

    Die Deaktivierung geht auch, wenn das Plugin deaktiviert wird, ist die Zeile in wp_options verschunden.

    Hier mal das Wesentliche:

    register_activation_hook( __FILE__, ‘parallax_at_mondphase_activate’ );
    register_deactivation_hook(__FILE__, ‘parallax_at_mondphase_deactivate’);

    // Add default options to the moonphase widget

    function parallax_at_mondphase_activate() {

    $data = array(‘title’ => ‘Mondphase’,
    ‘bgrCol’ => ‘C3DFB5′,
    ‘bgrAlpha’ => 50,
    ‘fontCol’ => ’000000′,
    ‘darkSideCol’ => ’080000′,
    ‘darkSideAlpha’ => 75,
    ‘frameCol’ => ’000000′,
    ‘frameAlpha’ => 100);

    update_option(‘widget_parallax_at_mondphase’, $data);
    }

    function parallax_at_mondphase_deactivate() {

    $options = get_option(‘widget_parallax_at_mondphase’);

    if( is_array($options))
    {
    delete_option(‘widget_parallax_at_mondphase’);
    }
    }

    // Putting the plugin’s functions inside the init function to ensure the
    // required Sidebar Widget functions are available.

    function widget_parallax_at_mondphase_init() {

    /* Custom code starts here */
    /* —————————- */

    /* Custom Function */
    function parallax_at_mondphase()
    {
    ….// SWF einbinden

    }

    /* ————————– */
    /* Custom code ends here */

    function widget_parallax_at_mondphase($args)
    {
    ….// Hier der Widget Code

    }

    // This is the function that outputs the form to let users edit
    // the widget’s setting.

    function widget_parallax_at_mondphase_control()
    {

    // Collect our widget options.
    $options = $newoptions = get_option(‘widget_parallax_at_mondphase’);

    // This is for handing the control form submission.
    if ( $_POST['widget_parallax_at_mondphase-submit'] )
    {
    // Clean up control form submission options
    $newoptions['title'] = strip_tags(stripslashes($_POST['widget_parallax_at_mondphase-title']));
    $newoptions['bgrCol'] = strip_tags(stripslashes($_POST['widget_parallax_at_mondphase-bgrCol']));
    $newoptions['bgrAlpha'] = strip_tags(stripslashes($_POST['widget_parallax_at_mondphase-bgrAlpha']));
    $newoptions['fontCol'] = strip_tags(stripslashes($_POST['widget_parallax_at_mondphase-fontCol']));
    $newoptions['darkSideCol'] = strip_tags(stripslashes($_POST['widget_parallax_at_mondphase-darkSideCol']));
    $newoptions['darkSideAlpha'] = strip_tags(stripslashes($_POST['widget_parallax_at_mondphase-darkSideAlpha']));
    $newoptions['frameCol'] = strip_tags(stripslashes($_POST['widget_parallax_at_mondphase-frameCol']));
    $newoptions['frameAlpha'] = strip_tags(stripslashes($_POST['widget_parallax_at_mondphase-frameAlpha']));

    }

    // If original widget options do not match control form
    // submission options, update them.
    if ( $options != $newoptions )
    {
    $options = $newoptions;
    update_option(‘widget_parallax_at_mondphase’, $options);
    }

    // Code to build the backend output

    ….

    };

    // This registers the widget.
    register_sidebar_widget(‘Mondphasen’, ‘widget_parallax_at_mondphase’);

    // This registers the widget control form.
    register_widget_control(‘Mondphasen’, ‘widget_parallax_at_mondphase_control’);

    }

    add_action(‘plugins_loaded’, ‘widget_parallax_at_mondphase_init’);
    ?>

  4. hc

    Hi,

    ich bin’s nochmal. Inzwischen bin ich schon sehr zufrieden mit dem Backend settings. Aber ich möchte gerne, dass der User jene Textfelder, welche Hex-Farbcode verlangen, mit einem Color-Picker Script bedienen kann.

    Hab mich jetzt durch die ganzen “jquery” Sachen gewühlt, bin aber nicht so recht schlau daraus geworden.

    Also jquery kann man ja mittels

    wp_enqueue_script(‘jquery’);

    einbinden. Wie nutze ich dann die color-picker.js direkt im Code meines Widgets? Hast du da eine Ahnung?

    Hab schon mit anderen Javascripts rumgespielt, und das funktiniert auch teilweise:
    http://www.greier-greiner.at/yatib/wp-content/uploads/color_picker_01.png

    Aber nach dem Speichern der Daten ist der Script nicht mehr verfügbar:
    http://www.greier-greiner.at/yatib/wp-content/uploads/color_picker_02.png

    Dachte dann, warum eigentlich nicht die von WP mitgebrachte jquery nutzen? Nur die Anwendung/Ansprache der Felder ist mir nicht klar.

    thx,
    hc

  5. Gregor

    Freut mich, dass ich dir helfen konnte und dein Plug-in funktioniert.
    Mit jQuery hab’ ich leider keine Erfahrungen. Warum WordPress JavaScript in den Config-Settings des Widgets nur vor dem speichern kann ich nur Vermutungen anstellen. Da die Seite nach dem Speichern nicht neu aufgebaut wird, wird evtl. auch das JavaScript nicht wieder aktiviert und geht dabei… verloren?
    Wie gesagt, habe das noch nicht versucht und auch mit jQuery das WP mitbringt habe noch nichts gemacht – war bisher nicht notwendig für unseren Blog.

Einen Kommentar schreiben:

Du mußt angemeldet sein um einen Kommentar abgeben zu können.