Eine der größten Stärken des Typo3 CMS ist jener Anspruch an modularisiertem Aufbau, dem sich viele Applikationen heutzutage rühmen und doch nur selten gerecht werden. Typo3 verspricht hier nicht zuviel, denn der Entwickler Kasper Skårhøj hat in der Version 3.5, die im November 2002 veröffentlicht wurde, den Extension Manager eingeführt, der das Kernsystem besser strukturierte und viele Teile der Funktionalität in Extensions auslagerte. In der aktuellen Version 3.7 kann man nahezu jeden Teil des Systems durch eine der zahlreichen Schnittstellen (Plug-ins, Hooks, Services, XCLASS) umgestalten und erweitern. Im weiteren Verlauf des Artikels werden wir Schritt für Schritt eine solche Extension entwickeln.
Ein Frontend-Plugin entwickeln
Einen guten Einstieg in die Typo3-Extension-Programmierung findet man über die Entwicklung eines Frontend-Plug-ins (abgekürzt: FE-Plug-in), ein Content-Element, das beliebigen HTML-Code im Frontend ausgeben kann und z.B. Informationen aus der Datenbank in Listen- oder Detailansicht darstellt und über dynamische Funktionen, wie Detail-Ansichten, Sortier- und Suchfunktionen verfügen kann, die über GET-/POST-Parameter gesteuert werden. Diese Content-Elemente sind Datensätze in der Tabelle tt_content, können auf beliebigen Seiten innerhalb des Typo3-Seitenbaumes (einer Abpictureung der Tabelle pages) angelegt werden und sind dann mit dieser Seite assoziiert.Der Kickstarter
Der Kickstarter ist selbst eine Extension von Typo3 [1] und kann über den Extension Manager im Modul Tools vom Administrator per Klick installiert werden. Er ist, wie der Name vermuten lässt, eine Art Wizard zur Generierung eines Frameworks, mit dem man innerhalb von nur wenigen Minuten eine große Menge an Code generieren kann, der sich nahtlos in das System integriert.
|
Nach der Installation des Kickstarters steht im Extension Manager oben in der Select-Box eine weitere Option zur Verfügung: Make new extension. Wird diese ausgewählt, so erscheint der Kickstarter und präsentiert eine Liste von Konfigurationsmöglichkeiten. Als erstes muss ein Extension key registriert werde, ein eindeutiger Schlüssel, der im lokalen System und bei Veröffentlichung auch im offiziellen Extension Repository nicht zweimal vorhanden sein darf. Bei der Entwicklung von Extensions, die nicht veröffentlicht werden sollen, ist hier das Prefix user_ die richtige Wahl. Kommt allerdings eine Veröffentlichung in Betracht, dann sollte man dem Link unter dem Feld folgen und einen offiziellen Schlüssel auf typo3.org registrieren.Der Menüpunkt General Info beinhaltet einige grundlegende, selbsterklärende Informationen zur Extension, die der Extension Manager später in der Detailansicht der Extension darstellen wird. Nach dem Ausfüllen dieses Formulars klicken wir das Kreuz-Symbol bei Extend existing Tables. Wir wählen die Tabelle tt_content zur Erweiterung aus, sie ist in der Select-Box bereits voreingestellt.
|
Das zweite Feld wird eine Relation unseres Datensatzes mit mehreren Bildern herstellen. Wir wählen als Field type diesmal die Option Files aus. Nach Angabe des Field name (z.B. images) und dem Field title (z.B. Images) aktualisieren wir das Formular. Als Extensions wählen wir Web-Imagefiles, die Max number of files stellen wir auf 5, Size of selector box ebenfalls auf 5 und wir aktivieren die Thumbnail-Option, die später automatisch Thumbnails der ausgewählten Bilder in dem Backend-Formular erzeugen wird.Diese beiden Felder sollen die einzigen sein, um die wir die Tabelle tt_content erweitern. Was wir jetzt noch brauchen ist das eigentliche FE-Plug-in. Dazu klicken wir auf das Kreuz-Symbol bei Frontend Plug-ins. Der Titel für das Plug-in kann frei gewählt werden und wird später im Backend verwendet, wir können hier einfach Simple Plug-in eintragen. Die Select-Box unter Apply a set of extended fields enthält unsere Erweiterung der Tabelle tt_content als Option, welche wir hier auswählen. Über die nachfolgenden Radio-Buttons können wir die Art des Plug-ins wählen, das von uns gewünschte ist bereits voreingestellt (Add to Insert Plug-in list in Content Elements), deshalb wählen wir nur noch die Option Add icon to New Content Element wizard und schreiben in das Feld darunter eine kurze Beschreibung, die neben dem Icon im Content-Element Wizard erscheinen soll.
|
An dieser Stelle kann man den Kickstarter bereits verlassen, indem man die Extension über View result im lokalen Extension-Verzeichnis /typo3conf/ext speichert. Nachdem das getan ist, kann man im Extension Manager wieder in die Liste der Available Extensions zurückkehren und sehen, dass die frisch erstellte Extension sich in die Liste eingeordnet hat.
Formulare bearbeiten
Die erste Datei, die wir editieren werden, ist die ext_tables.php, die Informationen zur Generierung und Validierung der Formulare und zur Eingabe und Bearbeitung der Datensätze enthält. Der Array $tempColumns enthält z.B. Angaben zu unseren Erweiterungen der Tabelle tt_content, die hier mit dem Extension key geprefixt werden, damit sie global eindeutig sind. Im Array config finden sich die Konfigurationseinstellungen zu dem Feld, wir können bei user_simpleext_myrte z.B. die Größe des Textfeldes bestimmen (Listing 1).Listing 1
"user_simpleext_myrte" => Array ( "exclude" => 1,"label" => "LLL:EXT:user_simpleext/locallang_db.php:tt_content.user_simpleext_myrte","config" => Array ("type" => "text","cols" => "30","rows" => "5","wizards" => Array("_PADDING" => 2,"RTE" => Array("notNewRecords" => 1,"RTEonly" => 1,"type" => "script","title" => "Full screen Rich Text Editing|[...]","icon" => "wizard_rte2.gif","script" => "wizard_rte.php",),),)),
$TCA["tt_content"]["types"]["list"]["subtypes_addlist"][$_EXTKEY."_pi1"]="user_simpleext_myrte;;;richtext[cut|copy|paste|bold|italic|underline]:rte_transform[mode=ts_css|imgpath=uploads/tx_usersimpleext/rte/];1-1-1, user_simpleext_images";
Der Plugin-Code
Die Extension erzeugt immer noch die Dummy-Ausgaben des Kickstarters. Das kann in der Datei pi1/class.user_simpleext_pi1.php geändert werden. Diese Datei enthält die Klasse, die initialisiert wird, wenn das Content-Element gerendert wird. Die Funktion main() wird aufgerufen übernimmt hier die Ausgabe.In main() wird die Variable $content mit Inhalt gefüllt und an die Funktion $this->pi_wrapInBaseClass() übergeben, die aus der Klasse tslib_pibase (class.tslib_pibase.php) stammt. Das ist die Klasse, die durch FE-Plug-ins erweitert wird und zahlreiche Funktionen enthält, die zur Arbeit mit FE-Plug-ins unerlässlich sind.Die pi_wrapInBaseClass() tut nichts anderes, als unseren HTML-Code in einen eigenen <div>-Tag zu wrappen. Wir sind also an dieser Stelle in der Lage, beliebigen Output zu erzeugen, doch muss man vorsichtig sein, hier nicht alle Vorteile, die einem das Framework bietet, wieder zunichte zu machen. Es folgen einige Punkte, die man bei der Programmierung beachten sollte.Datenbank-Abstraktion
Typo3 besitzt eine eigene Abstraktions-Ebene für den Zugriff auf Datenbanken. Um zu anderen Architekturen kompatibel zu sein, sollte man daher statt der gewohnten Query-Funktionen, von den Typo3-eigenen exec_*-Funktionen Gebrauch machen, z.B. exec_SELECTquery() oder exec_INSERTquery(). Mehr Informationen dazu findet man unter [4].Links
Innerhalb des Outputs der Extension sollte man unbedingt auf die verschiedenen Linkfunktionen aus der Klasse pi_base zurückgreifen. Zwei davon befinden sich in der Ausgabe des Plug-ins, das vom Kickstarter erzeugt wurde: $this->pi_getPageLink() wird eine Seiten-ID als Parameter übergeben und liefert eine URL zu der Seite zurück, $this->pi_linkToPage() benötigt zwei Parameter, der erste ist ein String und der zweite eine Seiten-ID, die Funktion liefert dann einen kompletten <a>-Tag zurück, der um den String gewrapped ist.Mehrsprachigkeit
Immer wenn man Textinhalt erzeugt, sollte man darauf achten, dabei auf die Funtkion $this->pi_getLL() zurückzugreifen und die Sprachtoken aus der Datei pi1/locallang.php zu nutzen, d.h., statt eine Ausgabe zu erzeugen wie '<h1>Überschrift<h1>' die Alternative '<h1>.'$this->pi_getLL('headline')'.</h1>' zu wählen und den Token 'headline' in der Datei pi1/locallang.php einzufügen. Das ermöglicht eine vergleichsweise einfache Übersetzung der Extension in andere Sprachen.Konfiguration & Templates
Das Erscheinungspicture des FE-Plug-ins sollte so gut es geht außerhalb des Extension-Sourcecodes konfigurierbar sein und deshalb innerhalb der Klasse eines FE-Plug-ins viel mit der Variable $this->conf gearbeitet werden, dem Array, der die TypoScript-Konfiguration des Plug-ins enthält. Im Listing 2 befinden sich ein paar Zeilen TypoScript, die im Setup des Seiten-Templates vorhanden sein müssen, um uns z.B. den Zugriff auf die Variablen $this->conf['test'] und $this->conf['hello.']['world'] (man beachte den Punkt nach hello) zu geben.Die Verwendung von Templates ist ebenfalls zu empfehlen, denn es erleichtert die Anpassung eines Plug-ins sehr. Typo3 verfügt über eine eigene Template-Engine, die mit Token (hier: Marks) und so genannten Subparts umgehen kann. Auch Smarty ist bereits als Extension vorhanden und kann zum Erzeugen der Ausgabe benutzt werden. Man sollte auf jeden Fall darauf achten, in seinen Templates ausschließlich mit validem XHTML Transitional zu arbeiten (Typo3 erzeugt XHTML-kompatiblen Code) und die Ausgabe mit CSS zu formatieren.Listing 2
plugin.user_simpleext_pi1 {test = Hello World!hello.world = 1images = IMAGEimages.file.width = 100images{imageLinkWrap = 1imageLinkWrap {bodyTag = <body style=background-color:#000000;>wrap = <a href="javascript:close();">|</a>width = 500height = 500JSwindow = 1JSwindow.newWindow = 1JSwindow.expand = 20,20enable = 1}}}
Die Ausgabe
Um die Ausgabe unseres Plug-ins zu verändern, können wir die Zuweisung der Variable $content auskommentieren oder löschen und gegen unseren eigenen Content austauschen. Dazu müssen wir noch die Inhalte unseres Content-Elements aus der Tabelle tt_content holen, dazu benötigen wir $this->cObj->currentRecord, dort ist ein String in der Form [tablename]:[uid] enthalten. Wir schicken diesen String durch ein explode() und benutzen die Return-Werte für die getRecord()-Funktion der Klasse pi_base, die uns einen assoziativen Array mit den Werten unseres Datensatzes zurückliefert (Listing 3).Die Pfade der im Formular hochgeladenen Bilder befinden sich durch Kommazeichen separiert in einem BLOB und können von uns ebenfalls durch ein explode() extrahiert werden. Damit die Bilder nicht alle in Originalgröße von dem Redakteur in der Site platziert werden, entwickeln wir noch eine Funktion getImage(), die uns über die cObj-Funktion IMAGE einen <img>-Tag rendert, dem über das TypoScript aus Listing 2 zu magischen Fähigkeiten verholfen wird: Die IMAGE-Funktion kann durch die übergebene $imageConfig dazu gebracht werden, dem Bild eine Maximal-Breite bzw. -Höhe zuzuweisen und außerdem automatisch ein Klick-Pop-up mit dem Bild in einer größeren Variante zu erzeugen. (Um eine Default-Einstellung für das TypoScript der Extension zu haben, kopieren wir das Listing 2 in eine neue Datei ins Extension-Verzeichnis, die wir ext_typoscript_setup.txt nennen.) Nun können wir die Werte wie in Listing 3 beschrieben ausgeben und verzichten an dieser Stelle auf die Ausgabe über ein Template.Listing 3
class user_simpleext_pi1 extends tslib_pibase {var $prefixId = "user_simpleext_pi1";var $scriptRelPath = "pi1/class.user_simpleext_pi1.php";var $extKey = "user_simpleext";function main($content,$conf) {$this->conf=$conf;$this->pi_setPiVarDefaults();$this->pi_loadLL();$contentUID = explode(":",$this->cObj->currentRecord);$data = $this->pi_getRecord($contentUID[0],$contentUID[1]);$content = '<p>'.$data['user_simpleext_myrte'].'</p>';foreach(explode(",",$data['user_simpleext_images']) as $k => $i){$content.= $this->getImage($i);}return $this->pi_wrapInBaseClass($content);}function getImage($imageName){$imageConfig = $this->conf['images.'];$imageConfig['file'] = 'uploads/tx_usersimpleext/'.$imageName;return $this->cObj->IMAGE($imageConfig);}}
Coding Guidelines
Das Typo3-Projekt verfügt bereits über eine große Anzahl frei verfügbarer Extensions, die einer großen Entwicklergemeinde zu verdanken sind. Es wurde bereits vor etwa zwei Jahren ein Dokument [4] veröffentlicht, welches Richtlinien zur Strukturierung und Dokumentation des Sourcecodes in Typo3-Extensions vorgibt. Bevor man also seine eigene Extension in das öffentliche Extension Repository stellt, sollte man sich vergewissern, dass man sich so gut es geht an diese Richtlinien gehalten hat, auch im Hinblick auf die Bewertung durch das Team Extension Review, welches sich zum Ziel gesetzt hat, alle Extensions einer Qualitätsprüfung zu unterziehen und in Kategorien von No cigar bis Cohiba! einzuordnen. Extensions, die sich nicht an die Coding Guidelines halten, werden niedrigere Wertungen erhalten. Auf diese Weise wird eine Qualitätssicherung im Repository geschaffen, die allen Entwicklern und Anwendern zugute kommt.Weiterführende Informationen können außerdem in [5] [6] und [7] bezogen werden. Wer Typo3 bis jetzt noch gar nicht getestet hat, kann mit der auf Knoppix basierenden Live-CD von [8] mal hineinschauen.Thomas Murphy ist selbstständiger Softwareentwickler bei seinem Unternehmen the panem group in Berlin. Er ist erreichbar unter murphy@thepanemgroup.com.
Links
[1] typo3.org/extensions/repository/search/kickstarter/
[2] typo3.org/documentation/document-library/doc_core_api/
[3] www.mcuniverse.com/Customizing_RTE.842.0.html
[4] typo3.org/documentation/document-library/doc_core_cgl/
[5] typo3.org/documentation/document-library/Matrix/
[6] wiki.typo3.org
[7] typo3.org/documentation/mailing-lists/
[8] typo3.punkt.de/typo3live.html




