Binnen eines Tages konnte der beauftragte Contao-Experte Code vorlegen, der die Programmieraufgabe löst. Mängel bei der verfügbaren Dokumentation nehmen jedoch Außenstehenden die Chance, Erweiterungen in vergleichbarer Zeit auf die Beine zu stellen.
Contao[1] ist ein freies Content-Management-System, das im Gegensatz zu vielen seiner Kollegen seitenorientiert arbeitet. Dabei legt der Administrator zunächst die Seitenstruktur des Internetauftritts fest und füllt dann die Seiten nach und nach mit Inhalten. Contao erschien 2004 unter dem Namen Typolight. Nutzer verwechselten es jedoch häufig mit Typo3, zudem deutete “light” auf ein funktionsarmes System hin – fälschlich.
Typolight-Erfinder und -Hauptentwickler Leo Feyer wechselte deshalb mit der Version 2.6 zum weniger missverständlichen Namen Contao. Seit 2012 betreut die Weiterentwicklung die dafür in der Schweiz gegründete Contao Association [2]. Obwohl sich das leicht erweiterbare und flott agierende CMS steigender Beliebtheit erfreut, erreicht es bei Weitem nicht den Verbreitungsgrad seiner direkten Konkurrenten Joomla oder Drupal.
Contao steht unter der GNU LGPL, für 500 Euro lässt sich aber auch eine kommerzielle Lizenz erwerben, aus der Lizenznehmer sämtliche Hinweise auf Contao und Leo Feyer entfernen dürfen. Die Implementierung der Beispielaufgabe lief unter der bei Redaktionsschluss aktuellen Versionsnummer 3.1.2, als Testsystem diente Ubuntu 13.04.
Modul unter Modulen
Die Inhalte einer Webseite erzeugen in Contao so genannte Module. Sie zapfen die Datenbank an, gießen dann die fertigen Daten und Texte in einen kleinen Schnipsel HTML-Code und übergeben diesen dem Content-Management-System. Contao setzt dann aus den angelieferten HTML-Stücken nach einem Bauplan die komplette Seite zusammen. Auch die kleine Beispielaufgabe löst folglich ein Modul. Es fragt die anstehenden Feste beim Dienst ab, bereitet sie auf und gibt sie in einer schicken Tabelle aus.
Contao selbst enthält einen Extension-Creator, der das Code-Skelett eines neuen Moduls ausspuckt. Der Programmierer gibt lediglich seinem Modul einen neuen Namen, hakt ab, ob es sich um ein Modul für das Front- oder Backend handelt, und legt fest, welche Sprachen das Modul später sprechen soll. Anschließend muss er bloß noch die erzeugten Klassen-Skelette und Konfigurationsdateien ausfüllen. Der beauftragte Contao-Experte (siehe Kasten “Der Programmierer”) ist diesen Weg aber nicht gegangen.
Der Programmierer
Der Code für die Lösung der Aufgabe in diesem Artikel stammt von Andreas Schempp, Hauptentwickler des Contao-Shopsystems Isotope E-Commerce, Leiter der Contao Arbeitsgruppe Core-Entwicklung und im Vorstand der Contao Association [2].
Beruflich arbeitet er als Mitinhaber bei der Terminal42 GmbH, welche kleine und mittlere Agenturen bei deren Contao-Projekten in den Bereichen Entwicklung und Layout-Umsetzung unterstützt.
Der Anlasser
Den Kern eines Moduls bildet ein PHP-Skript. In ihm leitet der Programmierer von der Klasse »Modul« eine eigene ab und überschreibt die Methoden »generate()« und »compile()« . Den Contao-Konventionen zufolge muss die abgeleitete Klasse den gleichen Namen wie das Modul mit einem vorangestellten »Module« tragen. Wie in Listing 1 zu besichtigen ist, hat der Programmierer das Modul »LinuxMagazin« getauft, seine eigene Klasse heißt folglich »ModuleLinuxMagazin« . Der Name dieser Klasse stiftet übrigens zudem den Dateinamen des PHP-Skripts.
Wenn das Modul seine Ausgaben zusammenstellen und ausspucken soll, ruft Contao seine öffentliche Methode »generate()« auf. Diese prüft in Listing 1 zunächst, ob sie das Backend aufgerufen hat:
if (TL_MODE == 'BE') { ... }
Wenn dies der Fall ist, übergibt »generate()« lediglich ein paar Informationen an Contao, den Namen des Moduls beispielsweise, die dann wiederum das Backend anzeigt. Andernfalls soll das Modul seine Informationen im Frontend und somit den Besuchern präsentieren. Als Vorbereitung darauf lädt »generate()« ab Zeile 21 erst einmal mehrere Stylesheets aus dem Unterverzeichnis »assets« , welche später die Informationen des Moduls etwas aufhübschen.
Als Nächstes bindet »generate()« etwas Javascript-Code ein. Dieser stammt nicht vom Programmierer des Moduls, sondern aus dem in Contao bereits enthaltenen Javascript-Framework Mootools [3]. Der hinzugeholte Javascript-Code erzeugt die kleinen Kalendersymbole, über die Besucher später bequemer ein Datum auswählen können (in Abbildung 1 die kleinen grünen Symbole).
Zum Schluss ruft »generate()« noch ihre Kollegin in der Oberklasse auf. Die wiederum aktiviert die private Methode »compile()« , welche die eigentlichen Daten zusammenstellt und ausgibt. In der Implementierung aus Listing 1 holt sie zunächst die Daten im Json-Format von »berlin.de« ab und presst sie in Zeile 41 in ein handlicheres Array. Anschließend prüft die Methode, ob die ganzen Informationen auf eine Seite passen oder ob das Modul sie besser auf mehrere Seiten verteilen sollte.
Listing 1
ModuleLinuxMagazin-Klasse (Ausschnitte)
01 <?php
02 [...]
03 protected $strTemplate = 'mod_linuxmagazin';
04
05 public function generate()
06 {
07 if (TL_MODE == 'BE')
08 {
09 $objTemplate = new BackendTemplate('be_wildcard');
10
11 $objTemplate->wildcard = '### LINUX MAGAZIN DEMO ###';
12 $objTemplate->title = $this->headline;
13 $objTemplate->id = $this->id;
14 $objTemplate->link = $this->name;
15 $objTemplate->href = $this->Environment->script.'?do=themes&table=tl_module&act=edit&id=' . $this->id;
16
17 return $objTemplate->parse();
18 }
19
20 // Demo styles from berlin.de website
21 $GLOBALS['TL_CSS'][] = 'system/modules/linuxmagazin/assets/style.css';
22 $GLOBALS['TL_CSS'][] = 'system/modules/linuxmagazin/assets/bde_2010_land.css';
23 $GLOBALS['TL_CSS'][] = 'system/modules/linuxmagazin/assets/bde_2010.css';
24 $GLOBALS['TL_CSS'][] = 'system/modules/linuxmagazin/assets/land_form.css';
25
26 $GLOBALS['TL_JAVASCRIPT'][] = 'assets/mootools/datepicker/' . DATEPICKER . '/datepicker.js';
27 $GLOBALS['TL_CSS'][] = 'assets/mootools/datepicker/' . DATEPICKER . '/dashboard.css';
28
29 return parent::generate();
30 }
31
32 protected function compile()
33 {
34 [...]
35 $strUrl = 'http://www.berlin.de/sen/wirtschaft/service/maerkte/strassenfeste/index.php/index/all.json';
36 [...]
37 $objRequest = new Request();
38 $objRequest->send($strUrl);
39
40 [...]
41 $objData = json_decode($objRequest->response);
42 [...]
43
44 $perPage = (int) $this->Input->get('ipp') ?: 20;
45 $page = (int) $this->Input->get('page') ?: 1;
46 $skip = ($page - 1) * $perPage;
47
48 foreach (array_slice($objData->index, $skip, $perPage) as $objResult) {
49 $arrData[] = array_merge((array) $objResult, array(
50 'von' => date('d.m.Y', strtotime($objResult->von)),
51 'class' => ($i%2 ? 'odd' : 'even'),
52 ));
53
54 $i += 1;
55 }
56
57 [...]
58 $this->Template->results = $arrData;
59 $this->Template->caption = $objData->results->count . ' Ergebnisse gefunden';
60 $this->Template->summary = $objData->results->count . ' Einträge insgesamt';
61 $this->Template->action = ampersand($this->Environment->request);
62 [...]
63 }
64 }
Die Vorlage
Die nun vorliegenden Termindaten könnte das Modul einfach so unformatiert ausgeben. Besser ist es jedoch, sie in eine HTML-Tabelle zu verpacken. Den dazu notwendigen PHP-Code lagert Contao in eine eigene Datei aus, das so genannte Template. Dies bietet den Vorteil, dass die Darstellung der Ausgaben von der eigentlichen (Modul-)Logik getrennt ist.
Wie die Datei mit dem Template heißt, teilt das Modul über das Attribut »$strTemplate« mit (Zeile 3 in Listing 1). Die Klasse »ModuleLinuxMagazin« kann dann über das Objekt »$this->Template« die Termine der Straßenfeste in das Template schieben, was in Listing 1 ab Zeile 58 geschieht. Die erste Zeile macht das Array »$arrData« unter dem Namen »results« im Template verfügbar. Nach dem gleichen Prinzip injizieren auch die nachfolgenden Zeilen weitere Daten in das Template.
Im Template sind damit jetzt alle Termine im Array »$this->results« verfügbar. Es genügt dort somit eine »foreach()« -Schleife, um die Zeilen der Ergebnistabelle aufzubauen. Den Code zeigt Listing 2. Der Programmierer der Beispielaufgabe stellt der Tabelle zudem ein Formular voran, über das die Besucher später die Tabelle nach bestimmten Kriterien filtern lassen können (wie in Abbildung 1). Die eigentliche Filterung übernimmt das PHP-Modul anhand der »POST« -Daten.
Das komplette Template wandert in eine eigene Datei im Unterverzeichnis »templates« – normalerweise sogar in zweifacher Ausführung: Die ».html5« -Datei enthält Tags gemäß HTML-5-Standard, die zweite Datei mit der Endung ».xhtml« verwendet hingegen das ältere XHTML. Der Administrator von Contao darf später im Backend zwischen HTML 5 und XHTML umschalten. Abhängig von seiner Wahl kommt automatisch das HTML-5- oder XHTML-Template zum Einsatz. Der Programmierer des Beispiels hat jedoch auf die XHTML-Variante verzichtet.
Listing 2
Template (Ausschnitte)
01 [...]
02 <table class="seninn result">
03 <caption><?php echo $this->caption; ?></caption>
04 <thead>
05 <tr>
06 <th class="text" colspan="3">Name der Veranstaltung</th>
07 <th class="date"><a class="sortlink" href="/sen/wirtschaft/service/maerkte/strassenfeste/index.php?q=&bezirk=Marzahn-Hellersdorf&von_from=19.9.2013&von_to=&bis=&ipp=20&order=von#results" title="Hier klicken um zu sortieren">Beginn <span class="apps_1616_mono icon_updown">? </span></a></th>
08 <th class="text">Ende</th>
09 </tr>
10 <tr>
11 <th class="text" colspan="2">Straße</th>
12 <th class="smalltext">PLZ und Ort</th>
13 <th class="text" colspan="2">Zeiten</th>
14 </tr>
15 <tr>
16 <th class="text" colspan="2">Veranstalter</th>
17 <th class="longtext" colspan="2">E-Mail</th>
18 <th class="text">Internet</th>
19 </tr>
20 <tr>
21 <th class="text" colspan="5">Bemerkungen</th>
22 </tr>
23 </thead>
24 <tfoot>
25 <tr>
26 <td colspan="5"><?php echo $this->summary; ?></td>
27 </tr>
28 </tfoot>
29 <tbody>
30 <?php foreach ($this->results as $result): ?>
31 <tr class="<?php echo $result['class']; ?> line_1">
32 <td class="text" colspan="3"><strong><?php echo $result['bezeichnung']; ?></strong></td>
33 <td class="date"><?php echo $result['von']; ?></td>
34 <td class="text"><?php echo $result['bis']; ?></td>
35 </tr>
36 <tr class="<?php echo $result['class']; ?> line_2">
37 <td class="text" colspan="2"><?php echo $result['strasse']; ?></td>
38 <td class="smalltext"><?php echo $result['plz']; ?></td>
39 <td class="text" colspan="2"><?php echo nl2br($result['zeit']); ?></td>
40 </tr>
41 <tr class="<?php echo $result['class']; ?> line_3">
42 <td class="text" colspan="2"><?php echo $result['veranstalter']; ?></td>
43 <td class="longtext" colspan="2">{{email::<?php echo $result['mail']; ?>}}</td>
44 <td class="text"><a href="http://<?php echo $result['www']; ?>" class="block" target="_blank"><?php echo $result['www']; ?></a></td>
45 </tr>
46 <tr class="<?php echo $result['class']; ?> line_4">
Babelfisch
In der Umsetzung der Beispielaufgabe sind die Beschriftungen der Tabellenspalten auf Deutsch. Übersetzungen hat der Programmierer nicht vorgesehen. Normalerweise liegen diese im Unterverzeichnis »languages« , in dem es für jede Sprache ein weiteres Unterverzeichnis mit dem jeweiligen Sprachkürzel gibt. Die Datei namens »modules.php« steuert die Modulnamen, im Beispielprojektenthält jedoch die folgenden zwei Zeilen:
$GLOBALS['TL_LANG']['MOD']['linuxmagazin']= array('Linux Magazin Demo');
$GLOBALS['TL_LANG']['FMD']['linuxmagazin']= array('Linux Magazin Demo');
Sie sorgen dafür, dass später das Modul unter dem Namen »Linux Magazin Demo« erscheint. Übersetzungen würden Contao-Entwickler meist in die »default.php« -Sprachdatei platzieren.
Bürokratisch
Schließlich bringt die Implementierung die Datei »dca/tl_module.php« mit, die gewöhnlich beschreibt, wie die vom Modul genutzten Datenbanktabellen und ihre Beziehungen zueinander aussehen. Contao bezeichnet die Definitionen als Data Container Arrays, kurz DCA. Die Implementierung braucht davon jedoch keinen Gebrauch zu machen, sondern definiert dort nur eine so genannte Palette. Das ist eine Sammlung aus Formularfeldern:
$GLOBALS['TL_DCA']['tl_module']['palettes']['linuxmagazin'] = '{title_legend},name,headline,type;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID,space';
Dieses Formular zeigt später das Backend an. In diesem Fall besteht es aus ein paar Standardfeldern. Ihretwegen darf das Modul einige Contao-Features, wie die Überschrift und die Zugriffskontrolle, mitnutzen (Abbildung 2). Die recht kryptische DCA-Syntax ist in [4] erläutert.
Eingehängt
Dank der Datei »config/config.php« erkennt Contao das Modul als solches. Im konkreten Fall lautet ihr Inhalt nur:
$GLOBALS['FE_MOD']['miscellaneous']['linuxmagazin'] = 'ModuleLinuxMagazin';
Hier registriert die Datei ein Modul für das Frontend (»FE_MOD« ) und ordnet es in die Gruppe “Verschiedenes” (»miscellaneous« ) ein. Sie sorgt später im Backend in den Auswahllisten lediglich für etwas mehr Übersicht (siehe Abbildung 3). Des Weiteren trägt das Modul den Namen »linuxmagazin« . Die zugehörige Klasse heißt »ModuleLinuxMagazin« .
Damit Contao 3 Klasse und Template laden und aufrufen kann, bedarf es einer »config/autoload.php« wie in Listing 3. Die erste Anweisung teilt Contao mit, dass es die Datei »ModuleLinuxMagazin.php« laden soll, wenn die Ausgaben der Klasse »ModuleLinuxMagazin« her müssen. Analog registriert die zweite Anweisung das Template: Wenn das Modul das »mod_linuxmagazin« -Template verlangt, weiß Contao, wo es die Datei findet.
Zur »autoload.php« gesellt sich eine »autoload.ini« , welche unter anderem die Abhängigkeiten zu anderen Modulen verrät. Die »autoload.ini« für die Beispielaufgabe zeigt Listing 4. Beide Autoload-Dateien lassen sich im Backend mit dem Autoload-Creator automatisch generieren.
Listing 3
Der Autoloader
01 ClassLoader::addClasses(array 02 ( 03 'ModuleLinuxMagazin' => 'system/modules/linuxmagazin/ModuleLinuxMagazin.php', 04 )); 05 06 TemplateLoader::addFiles(array 07 ( 08 'mod_linuxmagazin' => 'system/modules/linuxmagazin/templates', 09 ));
Listing 4
autoload.ini
01 ;; 02 ; List modules which are required to be loaded beforehand 03 ;; 04 requires[] = "core" 05 06 ;; 07 ; Configure what you want the autoload creator to register 08 ;; 09 register_namespaces = false 10 register_classes = false 11 register_templates = false
Wählerischer Einspieler
Contao besitzt eine Erweiterungsverwaltung, um Module zu installieren und zu aktivieren. Sie zapft aber nur den offiziellen Erweiterungskatalog der Contao-Homepage an. Wer sein Modul aus anderen Quellen besorgt oder gar selbst programmiert, muss es per Hand in ein Unterverzeichnis von »system/modules« des Webservers kopieren und den Installationsassistenten aufrufen.
Letzterer richtet nicht nur Contao auf dem Webserver ein, er prüft und aktualisiert auch die Datenbanktabellen. Es bleibt unklar, warum die Contao-Entwickler diesen fehleranfälligen Weg gehen und der Erweiterungsverwaltung keine Upload-Funktion spendieren. Da die Implementierung der Beispielaufgabe die Datenbank nichtanfass, genügte es, sie in das entsprechende Contao-Verzeichnis zu kopieren.
Joomla
Die Redaktion wollte ursprünglich Joomla als Vertreter für ein per PHP zu erweiterndes CMS antreten lassen. Allerding ließ sich über Wochen kein Projektmitglied auftreiben, das bereit war, sich der Programmieraufgabe zu stellen. Dies und eigene Recherchen legen nahe, dass das Programmieren eigener Joomla-Module sehr aufwändig ist. Schon ein “Hello World”-Modul erfordert große Mengen Code zu verfassen, die auf ein ganzes Bündel Dateien zu verteilen sind.
Wertung
Insgesamt elf Dateien in fünf Verzeichnissen benötigt das Modul, um die Beispielaufgabe zu lösen (siehe Tabelle 1). Die meiste Arbeit leistet das Skript »ModuleLinuxMagazin.php« zusammen mit seinem Template »mod_linuxmagazin.html5« . Normalerweise würden noch weitere Dateien hinzukommen, etwa Übersetzungen. Dank Contaos eingebauten Erweiterungsgenerator spart der Einsteiger immerhin Tipparbeit.
Die Implementierung der Beispielaufgabe zeigte zudem, dass Contao-Programmierer tunlichst Kommentare hinterlassen und den Quellcode gut formatieren sollten. Insbesondere das Template tendiert schnell zu kryptischen Textwüsten. Davon abgesehen fällt der Einstieg in den Code erstaunlich leicht. Auch die Implementierung der Beispielaufgabe erschließt sich recht schnell, obwohl der Programmierer in der Kürze der Zeit keine Kommentare hinterlassen hat.
Voraussetzung ist allerdings, dass der Entwickler mit den Konzepten von Contao vertraut ist. Mangels Dokumentation gestaltet sich das jedoch für jeden Einsteiger schwierig. So war zum Redaktionsschluss zwar das Benutzer-, nicht aber das Entwicklerhandbuch und somit die einzige vollständige Dokumentation auf der Contao-Homepage verfügbar. Die meisten Anleitungen im Internet sind entweder veraltet oder werfen mehr Fragen auf, als sie klären.
Tabelle 1
Die Dateien des Moduls im Überlick
| Datei | Bedeutung |
|---|---|
| ModuleLinuxMagazin.php | Zentrale Klasse, die Contao aufruft |
| templates/mod_linuxmagazin.php | Template |
| languages/en/modules.php | Englische Übersetzungen |
| dca/tl_module.php | DCA-Definitionen |
| config/autoload.ini | Konfigurationsdatei für den Autoloader |
| config/autoload.php | Konfigurationsdatei für den Autoloader |
| config.php | Meldet das Modul bei Contao an |
| assets/ | Stylesheets |
Infos
- Contao: https://contao.org/de/
- Contao Association: https://association.contao.org
- Mootools: http://mootools.net
- Data Container Arrays: https://contao.org/de/manual/3.1/data-container-arrays.html








