bloginista
Massimiliano Siddi
Home
  • Blog

Boxmodul für papaya CMS entwickeln

13. Juli 2009 04:24 von madmax
In diesem kleinen Tutorial möchte ich zeigen, wie einfach es ist, ein Boxmodul für papaya CMS zu schreiben. Boxmodule sind die einfachsten Inhaltsmodule in papaya CMS. Sie können einfach mit Seiten verknüpft werden und dienen in der Regel dazu, spezielle Kontextinformationen mit verschiedenen Seiten zu verknüpfen. Typische Anwendungsfälle sind beispielsweise Navigationen (Seitennavi, Brotkrümelnavi) oder Teaserboxen (News, neueste Foreneinträge). Ich habe eine Box entwickelt, mit der die aus der UNIX-Welt bekannten Fortune Cookies in einer Box ausgegeben werden.

Fortune Cookies besteht aus einer Datenbank, die Zitate von berühmten Persönlichkeiten aus den verschiedensten Bereichen wie Literatur, Politik oder Wissenschaften sowie Witze enthält. Optional kann so ein Fortune-Cookies-Eintrag also auch Quellenangaben enthalten. Die Idee dahinter ist, dass eines dieser Einträge beim Starten der Anwendung per Zufall aus der Datenbank ausgelesen und dem Nutzer angezeigt wird. In gewisser Weise entspricht das zufällig ausgewählte Zitat dem Spruch, den man in Glückskeksen finden kann.

Verzeichnisstruktur für Paket anlegen

Module werden in papaya CMS in Form von Verzeichnissen verwaltet. Sie enthalten neben den PHP-Dateien einige Metadaten wie die modules.xml, in der alle notwendigen Informationen über die Module enthalten sind. Falls das Paket auch Anwendungen enthält, die eigene Datenbanktabellen benötigen, wird für jede Datenbanktabelle eine XML-Datei mit der Strukturbeschreibung für die Tabelle mitgeliefert. Optional können Standard-Daten als CSV-Datei mitgeliefert werden.

Eigene Pakete werden in einem Verzeichnis unterhalb von ./papaya-lib/modules/ abgelegt, beispielsweise mein_paket/. Das Verzeichnis mein_paket/ hat dann folgende interne Verzeichnisstruktur:

./ ./praefix_modulname.php ./modules.xml ./DATA ./DATA/table_xml.xml ./DATA/tabledata.csv Direkt im Verzeichnis enthalten sind die PHP-Dateien und die modules.xml. Optionale Standard-Daten sowie die Tabellenstrukturbeschreibung sind im Unterverzeichnis ./DATA enthalten.

In papaya CMS gibt es eine Namenskonvention für eigene Module. Der Name des Moduls erhält immer einen Präfix, der eine Aussage über den Typ des Moduls macht. Die gängigsten Präfixe sind in der folgenden Tabelle aufgeschlüsselt:

Präfix Bedeutung
actbox_ Boxmodule erhalten dieses Präfix.
content_ Seitenmodule erhalten dieses Präfix.
base_ Datenbankschnittstelle für die Anwendung.
admin_ Administratormodul, mit dem die Daten im papaya-Backend gepflegt werden können. Dieses Modul erweitert in der Regel die base-Klasse des Pakets.
output_ Modul für die Ausgabe der Daten in XML-Format. Kann sowohl für die Datenausgabe in Seiten und Boxen als auch für die Ausgabe in Backendanwendungen genutzt werden.

Für dieses Tutorial relevant sind lediglich die Präfixe base_ und actbox_, da wir ein Boxmodul schreiben, das Daten aus einer Datenbanktabelle ausliest.

Datenbank für Fortune Cookies anlegen

Bevor mit dem eigentlichen Programmieren begonnen werden kann, soll noch die Datenbank mit den Daten für die Anwendung angelegt werden:

CREATE TABLE papaya_fortune_cookies( fcookie_id BIGINT(20) PRIMARY KEY NOT NULL auto_increment, quote TEXT NOT NULL, source1 VARCHAR(255), source2 VARCHAR(255) );Anschließend kannst du die Tabelle mit den Fortune-Cookies-Daten befüllen. Für die Fortune-Cookie-Anwendung habe ich einfach die Data-Files aus dem Fortune4All-Projekt genommen und in die Datenbank eingelesen. Dazu war es notwendig, die Daten in SQL umzuwandeln.

Basissystem erweitern

papaya CMS ist stark modular aufgebaut. Eigene Module schreibt man dadurch, das bestehende Klassen des Basissystems erweitert werden. Das folgende UML-Diagramm stellt nun die Klassenstruktur für Box-, Seiten- und Datenbankmodule dar:

UML-Klassendiagramm Boxmodul Datenbankmodul
Die wesentlichen Methoden der unmittelbaren Basisklassen base_actionbox, base_content und base_db sind im Diagramm berücksichtigt worden.

Datenbankmodul schreiben

Bevor wir mit dem Boxmodul beginnen können, müssen wir zunächst die Datenbankklasse schreiben. Dieser Schritt ist notwendig, da wir eine Anwendung schreiben wollen, die Daten aus einer eigenen Datenbanktabelle auslesen wird. Um also eine Datenbankklasse zu schreiben, legen wir im Paketverzeichnis eine PHP-Datei mit dem Namen base_fcookies.php an:

<?PHP //Einbinden der Basisklasse require_once(PAPAYA_INCLUDE_PATH.'system/sys_base_db.php'); //Klassendeklaration base_fcookies extends base_db { } ?>Im folgenden Schritt wird anschließend der Konstruktor hinzugefügt:

<?PHP //Einbinden der Basisklasse require_once(PAPAYA_INCLUDE_PATH.'system/sys_base_db.php'); //Klassendeklaration base_fcookies extends base_db { function __construct() { parent::__construct(); $this->tableFortuneCookies = PAPAYA_DB_TABLEPREFIX.'_fortune_cookies'; } } ?>Im Konstruktor wird zuerst explizit der Konstruktor der Vaterklasse aufgerufen. Anschließend wird im Klassenattribut tableFortuneCookies der Name der Datenbanktabelle gespeichert. Dieser setzt sich zusammen aus dem Präfix für die Datenbanktabellen, die in der Konstanten PAPAYA_DB_TABLEPREFIX gespeichert ist. Diese Konstante wird in der conf.inc.php definiert und hat standardmäßig den Wert papaya. Da sich das Präfix durchaus ändern kann, wird in Datenbankabfragen niemals der volle Tabellenname angegeben. Stattdessen sollte man stets die im Konstruktor definierten Tabellennamenvariablen benutzen.

Im folgenden Schritt wird nun die Funktion geschrieben, mit der die Datenbankabfrage erfolgt. Wir erinnern uns, dass wir eine Fortune-Cookies-Implementierung für papaya CMS entwickeln wollen. Ein abzudeckender Anwendungsfall besteht dabei im zufälligen Auswählen eines Zitats aus der Datenbank. Der Einfachheit halber könnten wir dazu die in MySQL eingebaute Funktion RAND() benutzen. Diese würde aber nicht mit anderen Datenbanken wie SQLite und PostgreSQL kompatibel sein. Da papaya CMS jedoch über eine Datenbankabstraktionsschicht verfügt, können wir uns über die Methode databaseGetSQLSource('RANDOM') die zur aktuellen Datenbank passende Random-Funktion besorgen:

<?PHP //Einbinden der Basisklasse require_once(PAPAYA_INCLUDE_PATH.'system/sys_base_db.php'); //Klassendeklaration base_fcookies extends base_db { function __construct() { parent::__construct(); $this->tableFortuneCookies = PAPAYA_DB_TABLEPREFIX.'_fortune_cookies'; } function getRandomFortune($count = 1) { $randFunc = $this->databaseGetSQLSource('RANDOM'); $sql = "SELECT fcookie_id, quote, source1, source2 FROM %s ORDER BY $randFunc LIMIT %d"; $params = array($this->tableFortuneCookies, (int)$count); $return = array(); if ($res = $this->databaseQueryFmt($sql, $params)) { while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) { $return[$row['fcookie_id']] = $row; } } return $return; } } ?>Die zufällige Auswahl des Zitats wird durch die Funktion getRandomFortune() implementiert. Die Funktion hat genau einen optionalen Parameter, der die Menge der zufällig auszuwählenden Zitate bestimmt. Wird kein Parameter übergeben, wird immer ein Zitat aus der Datenbank gelesen. Der SQL-Code enthält zudem Platzhalter für den Namen der DAtenbanktabelle sowie für den Parameter. Die Werte für diese Platzhalter werden in einem Array Namens $params gespeichert und der papaya-Funktion databaseQueryFmt() als zweiten Parameter übergeben. Der erste Parameter für diese Funktion besteht aus dem SQL-Code selbst.

Boxmodul für die Ausgabe schreiben

Das Boxmodul für die Ausgabe ist relativ einfach implementiert. Wir legen dazu erst eine PHP-Datei mit dem Namen actbox_fcookies.php an:

<?PHP //Einbinden der Basisklasse require_once(PAPAYA_INCLUDE_PATH.'system/base_actionbox.php'); //Klassendeklaration class actbox_fcookies extends base_actionbox { }Da wir auf die Datenbanktabelle mit den Fortune Cookies zugreifen müssen, binden wir noch unsere Basisklasse ein:

<?PHP //Einbinden der Basisklasse require_once(PAPAYA_INCLUDE_PATH.'system/base_actionbox.php'); //Einbinden der Forunte-Cookies-Basisklassse require_once(dirname(__FILE__).'/base_fcookies.php'); .. Anschließend überladen wir das $editFields-Attribut der Basisklasse. $editFields ist ein Array, in dem die Eingabefelder definiert werden, in die später im Administrationsbereich von papaya CMS Daten eingegeben werden können. In diesem Fall benötigen wir nur ein einzeiliges Eingabefeld, in das Nutzer im Backend von papaya CMS angeben können, wie viele Fortune Cookies gleichzeitig angezeigt werden sollen:

<?PHP ... class actbox_fcookies extends base_actionbox [ //editFields var $editFields = array( 'cookies_count' => array('Number of cookies', 'isNum', TRUE, 'input', 1, 1) ); }Wie zu erkennen ist, handelt es sich um ein assoziatives Array, dessen einziger Schlüssel 'cookies_count' auf das innere Array zeigt, in dem die Feldeigenschaften wie Feldbeschriftung, Validitätscheck (isNum erzwingt die Eingabe numerischer Daten und lehnt alle unpassenden Eingaben mit einer Fehlermeldung ab), TRUE (wenn es sich um ein Pflichfeld handeln soll, andernfalls FALSE), den Feldtyp (in diesem Fall input, also ein einzeiliges Eingabefeld), die maximale Anzahl erlaubter Zeichen, in diesem Fall genau 1, und zuletzt den Standardwert für das Feld, in diesem Fall auch die 1.

Im folgenden Schritt legen wir ein Klassenattribut für das Fortune-Cookies-Basisobjekt an und erstellen den Konstruktor der Klasse:

<?PHP ... class actbox_fcookies extends base_actionbox { ... var $baseCookies = NULL; function __construct() { $this->baseCookies = &new base_fcookies(); } } ?>Der Konstruktor macht nichts anderes als eine Instanz der Basisklasse base_fcookies zu erzeugen.

Im nächsten Schritt wird die Funktion getParsedData aus der Vaterklasse überladen. Diese Funktion wird vom Basisystem immer aufgerufen, um die Inhalte der Box auszulesen:

&lt?PHP .. class actbox_fcookies extends base_fcookies { ... function getParsedData() { $this->setDefaultData(); return $this->getFortuneXML(); } } Diese Methode ruft zunächst die in der Vaterklasse definierte Methode setDefaultData() auf. Diese Methode liest alle Standardfeldwerte aus dem $editFields-Array in das interne data-Array ein. Anschließend wird die Member-Methode getFortuneXML() aufgerufen. Diese Methode liest einen oder mehrere zufällige Fortune-Cookie-Einträge aus der Datenbank aus und setzt jeden Eintrag in ein spezielles XML-Element. Alle ausgelesenen Einträge werden in ein übergeordnetes XML-Element gesetzt und an die aufrufende Instanz, in diesem Fall die Funktion getParsedData() zurückgegeben. Im folgenden wird die Implementation dieser Funktion vorgestellt:

<?PHP ... class actbox_fcookies extends base_fcookies { ... function getFortuneXML() { $result = ''; $fortune; if ($this->data['cookies_count'] > 1) { $fortue = $this->baseCookies->getRandomFortune($this->data['cookies_count']); } else { $fortune = $this->baseCookies->getRandomFortune(); } if (isset($fortune) && is_array($fortune) && count($fortune) > 0){ $result .= ''."\n"; foreach ($fortune as $entry => $cookie) { $result .= sprintf(''."\n", (int)$entry); $result .= sprintf('%s'."\n", papaya_strings::escapeHTMLTags($fortune[$entry]['quote'], TRUE)); $source1 = $fortune[$entry]['source1']; if ($source1 !== '') { $result .= sprintf('%s'."\n", $source1); } $source2 = $fortune[$entry]['source2']; if ($source2 !== '') { $result .= sprintf('%s'."\n", $source2); } $result .= ''."\n"; } $result .= ''; } else { $result .= ''; } return $result; } }?>Die Methode ist schnell erklärt: Zunächst wird die Variable $result angelegt und mit einem leeren String initialisiert. Anschließend wird die Variable $fortune deklariert. In der folgenden Zeile wird getestet, ob der Wert des Eingabefeldes 'cookies_count' größer 1 ist. Dies ist dann der Fall, wenn der Nutzer das Boxmodul im Backend von papaya CMS angelegt hat und in das Feld einen Wert größer 1 eingegeben und abgespeichert hat. Ist dies der Fall, wird das Ergebniss der Funktion getRandomFortune($this->data['cookies_count']) in die Variable $fortune eingelesen, wobei die Funktion mit dem Feldwert als Parameter aufgerufen wird. Andernfalls wird die Funktion ohne Parameter aufgerufen (der Standardparameterwert dieser Funktion ist 1).

Im nächsten if-Zweig wird getestet, ob das zurückgegebene Objekt definiert ist, ein Array ist und mindestens einen Eintrag besitzt. Wenn dies der Fall ist, wird die Variable $result mit dem XML-Elementen initialisiert, die alle Daten aus dem $fortune-Array enthalten. Das fertige Element wird am Ende der Funktion an die aufrufende Instanz zurückgegeben.

modules.xml für die Modulregistrierung schreiben

Damit die Module aus dem Paket in papaya CMS verwendet werden können, müssen diese in der Datei modules.xml eingetragen werden. Die modules.xml ist eine XML-Datei, die Metainformationen aller Module des Pakets enthält. Nachdem diese Datei erstellt worden ist, muss in der Modulverwaltung im Backend von papaya CMS ein Modulscan ausgeführt werden. Durch den Modulscan werden die Module aus der modules.xml eingelesen und in papaya CMS registriert.

Im Basisverzeichnis des Pakets muss zuerst die Datei modules.xml angelegt und anschließend mit dem Texteditor geöffnet werden. Anschließend wird die XML-Dokumentendeklaration sowie das Wurzelelement <modulegroup> angelegt:

<?xml version="1.0"?> <modulegroup> </modulegroup>Füge das Element <name> mit dem Namen des Pakets sowie das Element <description> mit einer kurzen Beschreibung des Pakets als direkte Kindelemente von <modulegroup> ein:

<?xml version="1.0"?> <modulegroup> <name>Fortune Cookies</name> <description>Fortune Cookies for papaya CMS</description> </modulegroup>Im nächsten Schritt wird jetzt das Element <modules> eingefügt. Dieses Element enthält für jedes Modul aus dem Fortune-Cookies-Paket ein <module>-Element:

<?xml version="1.0"?> <modulegroup> <name>Fortune Cookies</name> <description>Fortune Cookies for papaya CMS</description> <modules> <module type="" guid="" name="" class="" file="" outputfilter=""></module> </modules> </modulegroup>Nun musst du für jedes Attribut die notwendigen Inhalte eintragen, um die Eigenschaften des Moduls zu spezifizieren:

<?xml version="1.0"?> <modulegroup> <name>Fortune Cookies</name> <description>Fortune Cookies for papaya CMS</description> <modules> <module type="box" guid="006cfd4f1f56d7e45b996af6a9830a08" name="Fortune cookies" class="actbox_fcookies" file="actbox_fcookies.php" outputfilter="yes"></module> </modules> </modulegroup>Füge nun ein Beschreibungstext innerhalb des <module>-Elements ein:

<?xml version="1.0"?> <modulegroup> <name>Fortune Cookies</name> <description>Fortune Cookies for papaya CMS</description> <modules> <module type="box" guid="006cfd4f1f56d7e45b996af6a9830a08" name="Fortune cookies" class="actbox_fcookies" file="actbox_fcookies.php" outputfilter="yes"></module>Fortune Cookies in a Box</modules> </modulegroup>Jetzt kannst du die Datei abspeichern. Das Paket kann nun im Backend von papaya CMS in der Modulverwaltung registriert werden. Näheres dazu erfährst du aus dem Online-Handbuch auf der papaya-Website. Der Vollständigkeit halber sind in der folgenden Tabelle noch alle Attribute des <module>-Elements beschrieben:

Attribut Funktion
type Modultyp
guid Eindeutiger 32stelliger hexadezimaler Schlüssel. Dafür kann beispielsweise eine MD5-Checksumme benutzt werden.
name Der kurze und aussagekräftige Name des Moduls.
class Name der PHP-Klasse des Moduls.
file Name der PHP-Datei, die die Modulklasse enthält. Der Dateiname ist grundsätzlich gleich dem Klassennamen plus der Endung „.php“.
outputfilter "yes", wenn ein Ausgabefilter notwendig ist, andernfalls "no". Das Attribut kann auch ganz wegfallen, wobei es so interpretiert wird, als hätte es den Wert "yes". Dieses Attribut ist nur für Content-Module vom Typ page und box relevant.
glyph Icon für Adminmodule

Die Anwendung ist jedoch noch nicht komplett. Es fehlen nämlich noch die XML-Dateien, die die Tabellenstruktur beschreiben.

XML-Datei für die Tabellenstruktur erzeugen

Die XML-Datei mit der Tabellenstrukturbeschreibung zu erzeugen ist relativ einfach. Dazu kann einfach die Funktion Tabelle exportieren aus der Modulverwaltung von papaya CMS benutzt werden. Zunächst einmal muss jedoch die Tabelle in der modules.xml eingetragen werden, damit papaya CMS auch weiß, welche Tabelle zu der Anwendung bzw. zum Paket gehört. Dazu wird neben dem modules-Element noch ein tables-Element eingefügt, das für jede Tabelle des Pakets ein entsprechendes table-Element enthält:

<?xml version="1.0"?> <modulegroup> <name>Fortune Cookies</name> <description>Fortune Cookies for papaya CMS</description> <modules> <module type="box" guid="006cfd4f1f56d7e45b996af6a9830a08" name="Fortune cookies" class="actbox_fcookies" file="actbox_fcookies.php" outputfilter="yes"></module>Fortune Cookies in a Box</modules> <tables> <table name="fortune_cookies" /> </tables> </modulegroup>Ganz wichtig zu beachten ist hierbei, dass der Tabellenname ohne das Präfix papaya_ im Attribut name des Elements table aufgeführt wird. Das Präfix wird nämlich automatisch durch papaya CMS dem Tabellennamen hinzugefügt. Nach dem Abspeichern der Datei wird in der Modulverwaltung von papaya CMS eine Warnmeldung dargestellt:

Modulverwaltung Warnmeldung
Die Tabelle ist zwar vorhanden, allerdings kann papaya CMS die Tabellenstruktrudatei nicht finden. Der Clou ist nun, dass papaya CMS die fehlende XML-Datei aus der bestehenden Datenbanktabelle erzeugen kann. Wenn du in der Modulverwaltung auf Tabelle exportieren klickst, zeigt der Browser auch prompt den Download-Dialog für diese Strukturdatei an. Der Name der Datei hat das Format table_[Tabellenname]_[timestamp].xml, beispielsweise table_fortune_cookies_2009-07-05.xml. Beim Tabellennamenteil fällt auf, dass auch hier freilich der Tabellennamenpräfix papaya_ weggelassen worden ist. Diese Datei speichern wir im Unterverzeichnis ./DATA des Paketverzeichnisses ab. Anschließend ändern wir noch den Dateinamen um, indem wir die Timestamp-Endung wegnehmen:

root@linux:~# mv table_fortune_cookies_2009-07-05.xml table_fortune_cookies.xmlWenn wir nun erneut das Backend aufrufen, werden Tabellenstrukturinformationen angezeigt:

Modulverwaltung mit Tabellenstrukturinformationen
Im letzten Schritt können wir dem Paket nun Default-Daten in Form der Fortune-Cookies-Daten als CSV-Datei in das ./DATA-Verzeichnis beilegen.

CSV-Datei für Standarddaten erzeugen

In papaya CMS existiert bereits eine Funktion, mit der man vorhandene Daten aus einer Tabelle im CSV-Format exportieren kann. Die exportierte Datei lässt sich zudem direkt in das ./DATA-Verzeichnis einfügen und kann mit dem Paket als Standard-Daten weitergegeben werden.

Um Standarddaten zu exportieren, gehst du wie folgt vor:
  • Wähle in der Modulverwaltung das Paket aus, die die Tabelle mit den zu exportierenden Daten enthält. Der Abschitt "Paketinhalt" wird dargestellt.
  • Klicke im Abschnitt "Paketinhalt" im Bereich Tabelle auf die Tabelle, deren Daten du exportieren möchtest. Ggf. musst du den Bereich "Tabelle" durch Klick auf das Plus-Icon vor dem Titel "Tabelle" aufklappen.
  • Klicke auf die Tabelle mit den zu exportierenden Daten. In der rechten Spalte des Content-Bereichs werden nun die Abschnitte "Felder" und "Index" dargestellt. Im Bearbeitungsmenü wird darüber hinaus der Menüpunkt Daten exportieren dargestellt.
  • Klicke im Bearbeitungsmenü auf Daten exportieren. Der Browser zeigt den Download-Dialog an.
  • Speichere die Datei auf deinem Rechner.
Der Dateiname hat das Format [Tabellenpräfix]_[Tabellenname]_[Datum].csv. Um die Datei nun als CSV-Datei mit Standarddaten benutzen zu können, müssen wir den Dateinamen anpassen und die Datei in das Unterverzeichnis ./DATA kopieren Dies können wir mit einem Befehl erledigen:

root@linux:~# mv papaya_fortune_cookies_2009-07-12.csv /VollerPfad/DATA/table_fortune_cookies.csvAus dem Tabellenpräfix "papaya" wurde "table" gemacht, während das Datum einfach weggelassen worden ist.

Und das war's auch schon. Die Dateien kann einfach wieder aus der CSV-Datei importieren, indem man in der Modulverwaltung von papaya CMS die entsprechende Tabelle auswählt und im Bearbeitungsmenü auf Datei imporieren klickt. Optional kann man eine CSV-DAtei mit Daten hochladen, die in die ausgewählte Tabelle eingespielt werden. Wenn die CSV-Datei im DATA-Verzeichnis abgelegt worden ist, wird das Feld "Standarddaten-Datei" dargestellt, das den Namen der CSV-Datei enthält. Durch Klicken auf Importieren werden die Daten aus dieser Datei in die Tabelle eingespielt.

Weitere Informationen

Weiterführende Informationen zur Entwicklung eigener Module könnt ihr auf der Homepage von papaya CMS erhalten:

Posted in papaya CMS, Programing

Keine Kommentare


(Kommentarbereich geschlossen)

← Ältere Einträge Neuere Einträge →