Skip to content

Hello World AddOn Tutorial

Sweil edited this page Oct 15, 2014 · 3 revisions

AddOn-Programmierung für das FS2

Ziel dieses Tutorials soll es sein dir die Eigenheiten bei der AddOn-Programmierung für das Frogsystem 2 näher zu bringen. Dabei geht es vor allem um die Integration in das System.

Es kann nicht die Aufgabe dieses Tutorials sein dir PHP, SQL oder sogar HTML beizubringen, dazu gibt es bereits genügend andere wirkliche gute Tutorials im Internet. Deswegen ist die eigentliche Programmierung, des mit diesem Tutotrial erstellten AddOns, nur rudimentär und wird sich ausschließlich um Varianten des bekannten "Hello World" drehen.

Voraussetzung für dieses Tutorial ist lediglich eine funktionierende Frogsystem 2 Installation der aktuellen Version. Und natürlich musst du auch direkten Zugriff auf das Datei-System und den MySQL-Server haben.

Stand: 2.alix5, der Großteil ist aber kompatibel zu 2.alix6

Der Anfang

Wie üblich wollen wir auch hier damit beginnen, eine einfache Datei zu erstellen, die uns die Nachricht "Hello World" ausgibt. Für Ausgaben bietet sich in einem CMS natürlich der öffentliche Bereich einer Seite an.

Über Adressen

Im Frogsystem 2 sind das die Seiten, die über die URL mit ?go= aufgerufen werden. Die entsprechenden Dateien dazu befinden sich im Data-Ordner ⇒[FS2]/data/.

Nachdem evtl. Alias-Weiterleitungen durchgeführt wurden, sucht das System nach einer Datei in diesem Ordner, die genauso heißt, wie das, was hinter dem ?go= steht. Der Übersicht halber lassen wir aber die Dateiendung ".php" weg. Das Frogsystem 2 fügt diese dann automatisch hinzu.

Andere Datei-Typen können theoretisch genauso eingebunden werden, bei ihnen muss die Dateiendung aber immer zwingend mit angegeben werden.

Eine Sonderstellung besitzt hier natürlich die Artikel-Funktion, für die unabhängig davon, ob eine Datei im Data-Ordner liegt, ?go=-Adressen angelegt werden.

Die Reihenfolge, nach der ?go=-Adressen überprüft werden, ist also:

  1. Aliasse
  2. Dateien im Data-Ordner
  3. Artikel-Adressen Findet sich keine Datei und kein Artikel, wird die 404-Fehlerseite ausgegeben (die aber wie andere Dateien auch im Data-Ordner liegt).

Das erste Script

Jetzt erstellen wir aber erstmal unser erstes Script. Also einfach mal im Data-Ordner die Datei "helloworld.php" anlegen und diesen Inhalt einfügen: Wenn wir die Datei jetzt über ?go=helloworld aufrufen, erhalten wir eine Ausgabe, die so ähnlich wie auf dem Bild aussehen sollte (roter Rahmen). Das führt uns zu einem ersten wichtigen Prinzip der FS2-Programmierung.

Ungewollte Ausgabe

Um Inhalte an den vorgesehenen Stelle auszugeben, kann im Frogsystem 2 nicht einfach der PHP-Befehl echo verwendet werden. Am richtigen Platz ausgeben werden nur die Inhalte, die am Ende des Scripts in der Variable $template gespeichert sind.

Daher gibt es zwei Möglichkeiten um unseren Fehler zu korrigieren. Wir könnten echo weiter nutzen und die Ausgabe mit dem PHP-Ausgabepuffer regeln, oder einfach echo durch $template ersetzen. In diesem Tutotial werden wir der Einfachheit halber die zweite Möglichkeit einsetzen. Dennoch sind beide Varianten prinzipiell gleichwertig.

Für Seiten im Admin-CP gilt die Regkung mit $template nicht. Hier muss i.d.R. mit echo gearbeitet werden. (Dazu kommt später noch mehr.)

Versuchen wir es also einmal mit diesem Code: $template = "Hello World";

Korrekte Ausgabe

Wenn wir uns jetzt die Seite anschauen, sieht das Ganze doch schon viel besser aus (oder sollte es zumindest).

Datenbank & Name

Nun wollen wir ja eigentlich mehr machen, als nur einen einfachen Text ausgeben und damit die Welt zu begrüßen. Eigentlich würden wir gerne alles mögliche begrüßen, und über das AdminCP einstellen können, wer gerade an der Reihe ist.

Die Datenbank

Das FS2 setzt im Hintergrund auf eine MySQL-Datenbank. Am einfachsten lässt sich diese über das Tool phpmyadmin verwalten, aber jeder andere Zugang ist natürlich genauso möglich.

Grundsätzlich verwendet das FS2 mehrere Tabellen, die dann die dann die entsprechenden Daten enthalten. Jede Tabelle beginnt dabei mit einem Präfix, dass während der Installation festgelegt wurde (Standard: fs2_, wird hier synonym für alle Präfixe verwendet). Es folgt dann der Name des Unter-Systems oder AddOns, für welche die Tabelle die größte Relevanz besitzt. Mit weiteren Unterstrichen können dann noch Spezialisierungen der Tabellen angegeben werden. So gibt es z.B. eine Tabelle fs2_news, die den Hauptinhalt von News speichert, aber auch eine Tabelle fs2_news_config, in der die Einstellungen für das News-System zu finden sind.

Mit Relevanz ist einfach die Zuordnung zu einem Unter-System oder AddOn gemeint. Denn natürlich wird die Tabelle fs2_news auch benötigt um die Anzahl der geschrieben News zu ermitteln, sie deshalb dem Statistik-System zuzuordnen wäre aber nicht zielführend.

Wir wollen nun also konfigurieren können, wenn wir mit unserem AddOn begrüßen. Deswegen erstellen wir uns jetzt eine neue Tabelle fs2_hello_config mit den Feldern id (Typ: TINYINT( 1 ), Standard: 1) und hello_name (Typ: VARCHAR( 100 ), Standard: "World"). Außerdem erstellen wir uns gleich einen Datensatz dazu. Wir können beides einfach mit Hilfe von phpmyadmin machen oder diesen Befehl verwenden:

CREATE TABLE `fs2_hello_config` (
    `id` TINYINT( 1 ) NOT NULL DEFAULT '1',
    `hello_name` VARCHAR( 100 ) NULL DEFAULT 'World'
);
INSERT INTO `fs2_hello_config` (`id`,`hello_name`)
VALUES ('1', 'World');

Ein Name muss her

Bei der Erklärung, wie Tabellen Namen aufgebaut sind, ist dir vielleicht aufgefallen, dass dort von einem Addon-Namen die Rede ist. Diese Namen sind wichtig, damit sich sich verschiedenen Addons nicht in die Quere kommen.

Wenn wir unser Addon auf der offiziellen Seite einstellen möchten, wird unter anderem auch darauf geachtet, dass wir einen eindeutigen Addon-Namen gewählt haben. Festlegen müssen wir einen Titel, der Groß-/Kleinschreibung oder Sonderzeichen enthalten darf und einen Tag, der als Präfix z.B. bei Tabellen dient, klein geschrieben werden muss und keine Sonderzeichen enthalten darf, aber immer noch mit dem Titel in Verbindung stehen sollte.

Der Tag zu einem Namen sollte zwar möglichst kurz und einfach sein, muss aber auch trotzdem noch den Bezug zum Titel behalten. Kai's tollem Sudoku-Addon den Tag sudoku zuzuordnen wäre also nicht erlaubt, kai-sudoku hingegen schon.

Für unser kleines Addon haben wir aber noch keine richtigen Namen gefunden. Da wir ja irgendwelche Sachen begrüßen wollen, nennen wir es einfach "Hello!". (Das Ausrufezeichen macht den Titel erst so richtig cool!) Den zugehörigen Tag "hello", haben im SQL-Teil schon mit eingebaut.

Verwaltung & Rechte

Im nächsten Schritt erstellen wir eine einfache Konfigurationsseite um den Namen des zu Begrüßenden komfortabel ändern zu können und erstellen dazu die entsprechenden Rechte für Mitarbeiter.

AdminCP-Seiten

Alle Seiten, die im AdminCP verwendet werden, sind unter "[FS2]/admin/" abgelegt. Sie beginnen mit dem Namen der Gruppe oder des Addons, gefolgt von einer genaueren Bezeichnung der Funktion der Seite.

Die fest integrierten Seiten verwenden zusätzlich noch das Präfix "admin_". Warum das so ist, weiß aber leider niemand so genau.

Wir legen uns also eine Datei mit dem Namen "hello_config.php" an und füllen sie mit folgendem Inhalt:

///////////////////////
//// Update Config ////
///////////////////////
 
if (
        $_POST['hello_name'] && $_POST['hello_name'] != ""
    )
{
    // security functions
    $_POST['hello_name'] = savesql ( $_POST['hello_name'] );
 
    // MySQL-Queries
    mysql_query ( "
                    UPDATE `".$global_config_arr['pref']."hello_config`
                    SET
                        `hello_name` = '".$_POST['hello_name']."'
                    WHERE `id` = '1'
    ", $db );
 
    // system messages
    systext ( $TEXT["admin"]->get("changes_saved"),
        $TEXT["admin"]->get("info"), FALSE, $TEXT["admin"]->get("icon_save_ok") );
 
    // Unset Vars
    unset ( $_POST );
}
 
/////////////////////
//// Config Form ////
/////////////////////
 
if ( TRUE )
{
    // Display Error Messages
    if ( isset ( $_POST['sended'] ) ) {
        systext ( $TEXT["admin"]->get("changes_not_saved")."<br>".$TEXT["admin"]->get("form_not_filled"),
            $TEXT["admin"]->get("error"), TRUE, $TEXT["admin"]->get("icon_save_error") );
 
    // Load Data from DB into Post
    } else {
        $index = mysql_query ( "
                                SELECT *
                                FROM ".$global_config_arr['pref']."hello_config
                                WHERE `id` = '1'
        ", $db);
        $config_arr = mysql_fetch_assoc($index);
        putintopost ( $config_arr );
    }
 
    // security functions
    $_POST['hello_name'] = killhtml ( $_POST['hello_name'] );
 
 
    // Display Form
    echo'
                    <form action="" method="post">
                        <input type="hidden" name="go" value="hello_config">
                        <input type="hidden" name="sended" value="1">
                        <table class="configtable" cellpadding="4" cellspacing="0">
                            <tr><td class="line" colspan="4">Allgemeine Einstellungen</td></tr>
                            <tr>
                                <td class="config">
                                    Ziel der Begrüßung:<br>
                                    <span class="small">Person oder Sache, die begrüßt werden soll.</span>
                                </td>
                                <td class="config">
                                    <input class="text input_width" name="hello_name" maxlength="100" value="'.$_POST['hello_name'].'">
                                </td>
                            </tr>
                            <tr><td class="space"></td></tr>
                            <tr>
                                <td class="buttontd" colspan="2">
                                    <button class="button_new" type="submit">
                                        '.$TEXT["admin"]->get("button_arrow").' '.$TEXT["admin"]->get("save_changes_button").'
                                    </button>
                                </td>
                            </tr>
                        </table>
                    </form>
    ';
}

siehe auch: Beispiel & Erklärung einer Konfigurationsseite im AdminCP

Zugriffsrechte & Links anlegen

Im AdminCP des Frogsystem 2 werden die Navigations-Menüs entsprechend der Zugriffsrechte eines eingeloggten Users gesetzt. Dazu muss jeder Seite ein entsprechendes Recht zugeordnet werden. Prinzipiell muss man für jede Seite ein Recht anlegen. Es kann nicht ein und dasselbe Recht für zwei Seiten genutzt werden. Es ist allerdings möglich Sub-Rechte zu definieren, die zusätzlich innerhalb einer Seite verwendet werden sollen.

Schauen wir uns dazu einmal die Tabelle fs2_admin_cp an:

  • page_id Der Name des Rechts, i.d.R. Dateiname ohne Endung
  • group_id ID der Linkgruppe, der die Seite zugeordnet ist
  • page_title Der rechte Teil des Seitentitels
  • page_link Link-Name, wie er im Menü erscheint
  • page_file Name einer im Ordner "[FS2]/admin/" liegenden Datei (bei Sub-Rechten page_link des übergeordneten Rechts)
  • page_pos Position innerhalb der Linkgruppe
  • page_int_sub_perm Setze "1" für ein Sub-Recht

Tabelle fs2_admin_cp

Bis auf die Linkgruppe sollte alles klar sein. Linkgruppen fassen die einzelnen Links innerhalb des Menüs optisch zu einer Gruppe zusammen. Diese Gruppen werden in der Tabelle fs2_admin_groups festgelegt:

  • group_id ID der Linkgruppe, wird in fs2_admin_cp verwendet
  • group_title Gruppenüberschrift
  • menu_id ID eines Bereichs des oberen Menüs
  • group_pos Position innerhalb eines Bereichs

Tabelle fs2_admin_group

Auch diese Tabelle sollte größtenteils selbsterklärend sein. group_id nutzt einfach einen fortlaufenden Index. menu_id bezieht sich auf das obere Menü, das selbst in der Tabelle fs2_admin_cp durch die Startseiten erzeugt wird, welche "-1" als group_id nutzen (und somit als Bereich inkl. zugehöriger Startseite definiert werden).

Addons sollten sich entweder in einen der bestehende Bereiche oder den Sonderbereich "AddOns" einbinden. Ein eigener Bereich muss bei der Überprüfung gesonderte Auflagen erfüllen.

Da es sich bei unserem AddOn um Inhalt handelt, wollen wir es auch entsprechend in den content-Bereich eingliedern, wir erstellen also folgende Gruppe:

INSERT INTO `fs2_admin_groups`
    (`group_title`, `menu_id`, `group_pos`)
VALUES
    ('Hello!', 'content', '99');

Die 99 als Position innherhalb des Bereichs soll lediglich dafür sorgen, dass unser AddOn immer ganz unten angezeigt wird.

Mit der soeben erzeugten Gruppe können wir jetzt auch unserer eigentliches Recht bzw. den Link anlegen.

INSERT INTO `fs2_admin_cp`
    (`page_id`, `group_id`, `page_title`, `page_link`, `page_file`, `page_pos`, `page_int_sub_perm`)
VALUES
    ('hello_config', '27', 'Konfiguration', 'Konfiguration', 'hello_config.php', '1', '0');

group_id variiert natürlich und muss später nur intern vom Installationsscript ermittelt werden. Einmal aus- und einloggen und wir finden unsere brandneue Konfigurationsseite im AdminCP. (Gilt nur für Admins, bei allen anderen Benutzern muss vorher natürlich noch über die Benutzerverwaltung das neue Recht gesetzt werden.)

Hello! Konfigurationsseite

Ausgabe anpassen

Zu guter Letzt bauen wir in unsere Ausgabeseite helloworld.php noch die Abfrage des Config-Wertes aus der Datenbank ein:

//////////////////////
//// Config Array ////
//////////////////////
$index = mysql_query ( "
    SELECT *
    FROM `".$global_config_arr['pref']."hello_config`
    WHERE `id` = '1'
", $db );
$config_arr = mysql_fetch_assoc ( $index );
 
$template = "Hello " . stripslashes ( $config_arr['hello_name'] ) . "!";

siehe auch: Beispiel & Erklärung einer Frontend-Seite

Ausgabe mit Wert aus Datenbank

Da wir uns dazu entschieden haben das Programm "Hello!" zu nennen, sollten wir eigentlich die Datei auch in "hello.php" umbenennen.

Da uns das aber zu viel Arbeit ist und da wir das Alias-System kennen lernen sollen, beleiben wir bei dem alten Namen und legen einen Alias an. Das geht ganz einfach unter System→Aliasse, die Maske ist selbsterklärend.

Alias hinzufügen

Templates

Nach dem wir schon den Inhalt der Begrüßung variabel gestaltet haben, wollen wir zum Schluss auch noch das Design über ein Template gestalten. Dazu müssen wir eine spezielle Template-Seite für das Admin-CP anlegen und abschließend die Ausgabe-Datei auf dieses neue Template zugreifen lassen.

Template bearbeiten

Die Template Bearbeitung findet im Admin-CP statt. Dazu wird ein normales PHP-Skript mit dem Namen hello_template.php angelegt, das aber intern auf eine besondere API zugreift. Wir speichern das Skript mit folgendem Inhalt:

// Set $go-Value
if ( $go == "tpl_hello" ) {
    $TEMPLATE_GO = "tpl_hello";
} else {
    $TEMPLATE_GO = "hello_template";
}
$TEMPLATE_FILE = "hello.tpl";
$TEMPLATE_EDIT = array();
 
 
$TEMPLATE_EDIT[] = array (
    name => "HELLO",
    title => "Hello!-Design",
    description => "Darstellung der Hello!-Begrüßungsseite",
    rows => 20,
    cols => 66,
    help => array (
        array ( tag => "hello_name", text => "Name der Person oder Sache, die begrüßt werden soll." ),
    )
);
 
// Intialise Editor
echo templatepage_init ( $TEMPLATE_EDIT, $TEMPLATE_GO, $TEMPLATE_FILE );

siehe auch: Beispiel & Erklärung einer Template-Seite im AdminCP

Die im Skript angegebene Template-Datei hello.tpl wird vom System beim ersten Speichern automatisch erzeugt.

Die Einbindung der Seite funktioniert wie gehabt über das Rechte-System. Da wir wollen, dass die Template-Bearbeitung sowohl in der AddOn-Gruppe "Hello!", als auch unter Styles→Templates angezeigt wird, legen wir zwei Einträge an:

INSERT INTO `fs2_admin_cp`
    (`page_id`, `group_id`, `page_title`, `page_link`, `page_file`, `page_pos`, `page_int_sub_perm`)
VALUES
    ('hello_template', '27', 'Template bearbeiten', 'Template', 'hello_template.php', '2', '0'),
    ('tpl_hello', '22', '„Hello!“ bearbeiten', 'Hello!', 'hello_template.php', '99', '0')

Eintrag in zwei Menü-Gruppen

Diese doppelte Eintragung ist nur möglich, da durch die erste if-Abfrage im Skript beide Rechte überprüft werden!

Template verwenden

Beim ersten Bearbeiten erhalten wir den Hinweis, dass die Template-Datei wie gewünscht vom System erstellt wurde.

Fehlende Template-Datei wird neu erstellt

Für unsere Zwecke reicht es vorerst aus, wenn wir ein einfaches Template einfügen und speichern:

<div align="center">Hello <b>{..hello_name..}</b>!</div>

Das Ausgabe-Skript wird nun noch wie folgt angepasst:

//////////////////////
//// Config Array ////
//////////////////////
$index = mysql_query ( "
    SELECT *
    FROM `".$global_config_arr['pref']."hello_config`
    WHERE `id` = '1'
", $db );
$config_arr = mysql_fetch_assoc ( $index );
 
//////////////////////
//// Get Template ////
//////////////////////
$template = new template();
$template->setFile("hello.tpl");
$template->load("HELLO");
$template->tag("hello_name", stripslashes ( $config_arr['hello_name'] ) );
$template = $template->display();

siehe auch: Beispiel & Erklärung einer Frontend-Seite

Wenn wir uns jetzt die Ausgabe anschauen, können wir feststellen, dass unser AddOn endlich fertig ist:

Ausgabe einer Frontend-Seite in Abhängigkeit von einem Template

Installation & Deinstallation

Jedes AddOn muss ein eigenes Installations und Deinstallations-Skript mitbringen. Da für die aktuelle Version (Alix 5) keine allgemeine Form dieser Skritpe vorgesehen ist, sind wir beim Erstellen mehr oder weniger frei. Dennoch gibt es einige Hinweise die wir beachten müssen:

Allgemein

  • AddOns sollten möglichst keine bestehenden Systeme (d.h. Dateien und Tabellen) verändern
  • Alles was eingefügt wurde, muss auch wieder entfernt werden können

Installation

  • Installations-Skripte sollten keine Dateien verschieben
  • Dateien sollten bereits über die Ordnerstruktur eingefügt werden
  • Es sollte ein Original-Template im Ordner "[FS2]/styles/default" abgelegt werden
  • Installations-Skripte könnten erkennen ob evtl. vorhandene Dateninhalte aus einer vorherigen Installation des AddOns existieren und diese auf Wunsch übernehmen oder löschen

Deinstallation

  • Prinzipiell müssen alle in die Datenbank eingefügten Daten entfernt werden
  • Es könnte aber eine Option angeboten werden, inhaltliche Daten zu behalten
  • Dateien sollten nur auf Nachfrage mit genauer Auflistung gelöscht werden

siehe auch: Beispiel & Erklärung eines De- bzw. Installations-Skripts

Clone this wiki locally