TYPO3-Extensions: Professionelle Frontend- und Backend-Programmierung 3446415572, 9783446415577 [PDF]


138 3 3MB

German Pages 466 Year 2010

Report DMCA / Copyright

DOWNLOAD PDF FILE

Papiere empfehlen

TYPO3-Extensions: Professionelle Frontend- und Backend-Programmierung
 3446415572, 9783446415577 [PDF]

  • 0 0 0
  • Gefällt Ihnen dieses papier und der download? Sie können Ihre eigene PDF-Datei in wenigen Minuten kostenlos online veröffentlichen! Anmelden
Datei wird geladen, bitte warten...
Zitiervorschau

alexander EBNER patrick LOBACHER bernhard ULBRICH

TYPO3 EXTENSIONS PROFESSIONELLE FRONTEND- UND BACKENDPROGRAMMIERUNG

Mit

Extubndase Fluid

Ebner, Lobacher, Ulbrich TYPO3-Extensions

v

Bleiben Sie einfach auf dem Laufenden: www.hanser.de/newsletter Sofort anmelden und Monat für Monat die neuesten Infos und Updates erhalten.

Alexander Ebner Patrick Lobacher Bernhard Ulbrich

TYPO3-Extensions Professionelle Frontend- und Backend-Programmierung

Die Autoren: Alexander Ebner, München Patrick Lobacher, München Bernhard Ulbrich, München

Alle in diesem Buch enthaltenen Informationen, Verfahren und Darstellungen wurden nach bestem Wissen zusammengestellt und mit Sorgfalt getestet. Dennoch sind Fehler nicht ganz auszuschließen. Aus diesem Grund sind die im vorliegenden Buch enthaltenen Informationen mit keiner Verpflichtung oder Garantie irgendeiner Art verbunden. Autoren und Verlag übernehmen infolgedessen keine juristische Verantwortung und werden keine daraus folgende oder sonstige Haftung übernehmen, die auf irgendeine Art aus der Benutzung dieser Informationen – oder Teilen davon – entsteht. Ebenso übernehmen Autoren und Verlag keine Gewähr dafür, dass beschriebene Verfahren usw. frei von Schutzrechten Dritter sind. Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Buch berechtigt deshalb auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.

Bibliografische Information Der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

Dieses Werk ist urheberrechtlich geschützt. Alle Rechte, auch die der Übersetzung, des Nachdruckes und der Vervielfältigung des Buches, oder Teilen daraus, vorbehalten. Kein Teil des Werkes darf ohne schriftliche Genehmigung des Verlages in irgendeiner Form (Fotokopie, Mikrofilm oder ein anderes Verfahren) – auch nicht für Zwecke der Unterrichtsgestaltung – reproduziert oder unter Verwendung elektronischer Systeme verarbeitet, vervielfältigt oder verbreitet werden.

© 2010 Carl Hanser Verlag München Gesamtlektorat: Fernando Schneider Sprachlektorat: Sandra Gottmann, Münster-Nienberge Herstellung: Stefanie König Coverconcept: Marc Müller-Bremer, www rebranding.de, München Coverrealisierung: Stephan Rönigk Datenbelichtung, Druck und Bindung: Kösel, Krugzell Ausstattung patentrechtlich geschützt. Kösel FD 351, Patent-Nr. 0748702 Printed in Germany ISBN 978-3-446-41557-7 www hanser.de/computer

Inhalt

Teil I – Klassische Extensionprogrammierung 1 1.1 1.2 1.3 1.4 1.5

Grundlagen............................................................................................................... 3 Aufbau und Struktur von TYPO3 ............................................................................................3 Aufbau einer Extension............................................................................................................6 Arten und Einteilung von Extensions.......................................................................................7 Der Extension-Key...................................................................................................................8 TYPO3 Coding Guidelines ......................................................................................................9 1.5.1 Namespace ...............................................................................................................10 1.5.2 PHP-Regeln..............................................................................................................11 1.5.3 Dateistruktur.............................................................................................................12 1.5.4 PHP-Syntax ..............................................................................................................15 1.5.5 phpDoc .....................................................................................................................21 1.5.6 Changelog.................................................................................................................22

2 2.1

Eine Extension planen und entwickeln................................................................ 23 Extensions planen ..................................................................................................................23 2.1.1 Wir planen eine Blog-Extension...............................................................................24 Der Kickstarter.......................................................................................................................26 2.2.1 Die Datenbank..........................................................................................................28 2.2.2 Datenbanktabellen und Formulare – eine Erklärung ................................................34 2.2.3 Extend existing Tables .............................................................................................36 2.2.4 Frontend-Plug-ins.....................................................................................................37 2.2.5 Backend-Module ......................................................................................................40 2.2.6 Integrate in existing Modules ...................................................................................41 2.2.7 Clickmenu items.......................................................................................................41 2.2.8 Services ....................................................................................................................42 2.2.9 Static TypoScript code .............................................................................................43

2.2

V

Inhalt

2.3 2.4

3 3.1 3.2 3.3

3.4 3.5 4 4.1 4.2

4.3 4.4

VI

2.2.10 TSconfig................................................................................................................... 43 2.2.11 Die Extension erstellen............................................................................................. 44 Extension-Konfiguration mit ext_emconf.php....................................................................... 45 Die weiteren Dateien der Extension....................................................................................... 50 2.4.1 Verzeichnis doc........................................................................................................ 50 2.4.2 Verzeichnis mod#..................................................................................................... 50 2.4.3 Verzeichnis modfunc#.............................................................................................. 51 2.4.4 Verzeichnis pi#......................................................................................................... 52 2.4.5 Verzeichnis Static..................................................................................................... 52 2.4.6 Datei Changelog....................................................................................................... 53 2.4.7 Datei README.txt .................................................................................................. 53 2.4.8 Datei ext_icon.gif ..................................................................................................... 53 2.4.9 Datei ext_localconf.php ........................................................................................... 53 2.4.10 Datei ext_tables.php ................................................................................................. 53 2.4.11 Datei ext_tables.sql .................................................................................................. 54 2.4.12 Datei ext_tables_static+adt.sql ................................................................................. 55 2.4.13 Dateien Icon_tx_extkey_tabellenname.gif ............................................................... 56 2.4.14 Datei locallang xml .................................................................................................. 56 2.4.15 Datei locallang_db xml ............................................................................................ 56 2.4.16 Datei tca.php ............................................................................................................ 56 Backend-Module erstellen .................................................................................... 65 Module konfigurieren und im Hauptmenü anordnen ............................................................. 65 Einen eigenen Navigationsbaum erstellen ............................................................................. 69 Backend-Modul erstellen ....................................................................................................... 75 3.3.1 Das Modul initialisieren ........................................................................................... 75 3.3.2 Den Modulinhalt aufbauen....................................................................................... 79 3.3.3 Extension-Konfiguration über ext_conf_template.txt .............................................. 80 3.3.4 Backend-Formulare aufrufen ................................................................................... 82 3.3.5 Eine Datensatzliste mit Bearbeitungsfunktionen erstellen........................................ 84 3.3.6 Aufgabe: Module für Kommentare, Blog und Blogroll............................................ 90 Backend User Object – $BE_USER ...................................................................................... 91 Das Systemlog ....................................................................................................................... 92 Frontend-Plug-ins.................................................................................................. 95 Das Frontend-Plug-in im Überblick....................................................................................... 95 Feldinhalte für die Ausgabe aufbereiten .............................................................................. 103 4.2.1 Das Bild für Teaser Image...................................................................................... 104 4.2.2 Ausgabe des Haupttextes in der Listenansicht, wenn erwünscht........................... 105 4.2.3 Haupttext mit RTE formatieren.............................................................................. 105 4.2.4 Haupttext statt Teasertext anzeigen, falls dieser nicht verfügbar ist...................... 106 4.2.5 Verlinken des Autorennamens mit der Mail-Adresse des Autors.......................... 106 4.2.6 Kategorien als Namen anzeigen und verlinken (Relationen).................................. 107 HTML-Templates für das Plug-in........................................................................................ 109 Die Kommentarfunktion mit AJAX und eID ....................................................................... 114 4.4.1 Ausgabe vorhandener Kommentare ....................................................................... 115

Inhalt

4.5 4.6 4.7 5 5.1 5.2

5.3

4.4.2 Das Kommentarformular hinzufügen .....................................................................118 Was sind Hooks?..................................................................................................................123 Flexforms .............................................................................................................................125 4.6.1 Erzeugen der Flexform ...........................................................................................126 Das Plug-in pi2.....................................................................................................................132 Dokumentation und Abschluß ............................................................................ 133 Code dokumentieren mit extdeveval ....................................................................................133 Extension-Dokumentation schreiben....................................................................................134 5.2.1 Die Vorlage ............................................................................................................135 5.2.2 Der Aufbau der Dokumentation .............................................................................135 5.2.3 Vorlagenstile ..........................................................................................................136 5.2.4 Die Dokumentation verfassen ................................................................................136 Upload ins TER....................................................................................................................137

Teil II – Funktionsreferenz zur klassischen Extensionprogrammierung 6 6.1

6.2

Datenbank ............................................................................................................ 141 $GLOBALS['TYPO3_DB'] .................................................................................................141 6.1.1 exec_SELECTquery ...............................................................................................141 6.1.2 exec_SELECT_queryArray....................................................................................141 6.1.3 exec_SELECTgetRows ..........................................................................................142 6.1.4 exec_SELECT_mm_query.....................................................................................142 6.1.5 exec_INSERTquery................................................................................................143 6.1.6 exec_UPDATEquery..............................................................................................143 6.1.7 exec_DELETEquery...............................................................................................143 6.1.8 sql_fetch_assoc.......................................................................................................143 6.1.9 sql_fetch_row .........................................................................................................144 6.1.10 searchQuery............................................................................................................144 6.1.11 listQuery .................................................................................................................144 6.1.12 splitGroupOrderLimit.............................................................................................145 6.1.13 quoteStr ..................................................................................................................145 6.1.14 fullQuoteStr............................................................................................................145 6.1.15 fullQuoteArray .......................................................................................................146 6.1.16 escapeStrForLike....................................................................................................146 6.1.17 cleanIntArray..........................................................................................................147 6.1.18 cleanIntList.............................................................................................................147 6.1.19 debug_lastBuiltQuery.............................................................................................147 tslib_pibase ..........................................................................................................................148 6.2.1 pi_exec_query ........................................................................................................148 6.2.2 pi_getPidList ..........................................................................................................149 6.2.3 pi_getRecord ..........................................................................................................150 6.2.4 pi_prependFieldsWithTable ...................................................................................150

VII

Inhalt

VIII

6.3

cObj ..................................................................................................................................... 151 6.3.1 DBgetDelete........................................................................................................... 151 6.3.2 DBgetInsert ............................................................................................................ 151 6.3.3 DBgetUpdate.......................................................................................................... 152 6.3.4 enableFields ........................................................................................................... 153

7 7.1

Dateisystem.......................................................................................................... 155 t3lib_div............................................................................................................................... 155 7.1.1 dirname .................................................................................................................. 155 7.1.2 fixWindowsFilePath............................................................................................... 155 7.1.3 formatSize .............................................................................................................. 156 7.1.4 get_dirs................................................................................................................... 156 7.1.5 getAllFilesAndFoldersInPath................................................................................. 157 7.1.6 getFileAbsFileName............................................................................................... 158 7.1.7 getFilesInDir .......................................................................................................... 158 7.1.8 getURL................................................................................................................... 159 7.1.9 isAbsPath ............................................................................................................... 159 7.1.10 isAllowedAbsPath.................................................................................................. 159 7.1.11 mkdir ...................................................................................................................... 160 7.1.12 mkdir_deep............................................................................................................. 160 7.1.13 removePrefixPathFromList .................................................................................... 160 7.1.14 resolveBackPath..................................................................................................... 161 7.1.15 rmdir....................................................................................................................... 161 7.1.16 split_fileref ............................................................................................................. 162 7.1.17 tempnam................................................................................................................. 162 7.1.18 unlink_tempfile ...................................................................................................... 162 7.1.19 upload_copy_move ................................................................................................ 162 7.1.20 upload_to_tempfile ................................................................................................ 163 7.1.21 verifyFilenameAgainstDenyPattern ....................................................................... 163 7.1.22 writeFile ................................................................................................................. 164 7.1.23 writeFileToTypo3tempDir ..................................................................................... 164

8 8.1

Strings, Arrays und Umgebungsvariablen ........................................................ 165 Allgemeine Funktionen........................................................................................................ 165 8.1.1 _GET...................................................................................................................... 165 8.1.2 _GETset ................................................................................................................. 165 8.1.3 _GP ........................................................................................................................ 166 8.1.4 _POST.................................................................................................................... 166 8.1.5 callUserFunction .................................................................................................... 166 8.1.6 clientInfo ................................................................................................................ 167 8.1.7 compat_version ...................................................................................................... 167 8.1.8 compileSelectedGetVarsFromArray ...................................................................... 168 8.1.9 getHostname........................................................................................................... 168 8.1.10 getIndpEnv ............................................................................................................. 168 8.1.11 getThisUrl .............................................................................................................. 168 8.1.12 linkThisScript......................................................................................................... 169

Inhalt

8.2

8.1.13 linkThisUrl .............................................................................................................169 8.1.14 locationHeaderUrl ..................................................................................................169 8.1.15 makeInstance..........................................................................................................170 8.1.16 makeInstanceService ..............................................................................................170 8.1.17 rmFromList.............................................................................................................170 8.1.18 sysLog ....................................................................................................................171 String-Funktionen ................................................................................................................171 8.2.1 calcParenthesis .......................................................................................................171 8.2.2 cmpFQDN ..............................................................................................................171 8.2.3 cmpIP .....................................................................................................................172 8.2.4 convUmlauts...........................................................................................................172 8.2.5 csvValues ...............................................................................................................172 8.2.6 deHSCentities.........................................................................................................173 8.2.7 expandList ..............................................................................................................173 8.2.8 fixed_lgd ................................................................................................................173 8.2.9 fixed_lgd_pre .........................................................................................................174 8.2.10 formatForTextarea..................................................................................................174 8.2.11 generateRandomBytes............................................................................................174 8.2.12 get_tag_attributes ...................................................................................................175 8.2.13 htmlspecialchars_decode ........................................................................................175 8.2.14 implodeArrayForUrl...............................................................................................175 8.2.15 implodeAttributes...................................................................................................176 8.2.16 inList ......................................................................................................................176 8.2.17 int_from_ver...........................................................................................................177 8.2.18 intInRange ..............................................................................................................177 8.2.19 intval_positive ........................................................................................................178 8.2.20 isFirstPartOfStr.......................................................................................................178 8.2.21 md5int ....................................................................................................................178 8.2.22 milliseconds............................................................................................................179 8.2.23 modifyHTMLColor ................................................................................................179 8.2.24 modifyHTMLColorAll ...........................................................................................179 8.2.25 normalizeIPv6 ........................................................................................................180 8.2.26 removeXSS.............................................................................................................180 8.2.27 revExplode .............................................................................................................180 8.2.28 rm_endcomma ........................................................................................................181 8.2.29 shortMD5 ...............................................................................................................181 8.2.30 split_tag_attributes .................................................................................................181 8.2.31 splitCalc..................................................................................................................182 8.2.32 strtolower................................................................................................................183 8.2.33 strtoupper................................................................................................................183 8.2.34 substUrlsInPlainText ..............................................................................................183 8.2.35 testInt......................................................................................................................184 8.2.36 trimExplode............................................................................................................184 8.2.37 uniqueList...............................................................................................................185 8.2.38 validEmail ..............................................................................................................185 8.2.39 validIP ....................................................................................................................185

IX

Inhalt

8.3

8.4

9 9.1 9.2

9.3

10 10.1

X

8.2.40 validIPv4 ................................................................................................................ 186 8.2.41 validIPv6 ................................................................................................................ 186 Array-Funktionen................................................................................................................. 186 8.3.1 addSlashesOnArray................................................................................................ 186 8.3.2 array_merge ........................................................................................................... 187 8.3.3 array_merge_recursive_overrule............................................................................ 187 8.3.4 array2json............................................................................................................... 188 8.3.5 array2xml ............................................................................................................... 188 8.3.6 arrayToLogString ................................................................................................... 189 8.3.7 explodeUrl2Array .................................................................................................. 190 8.3.8 inArray ................................................................................................................... 190 8.3.9 print_array.............................................................................................................. 190 8.3.10 removeArrayEntryByValue.................................................................................... 191 8.3.11 slashArray .............................................................................................................. 191 8.3.12 view_array.............................................................................................................. 192 8.3.13 xml2array ............................................................................................................... 192 8.3.14 xml2tree ................................................................................................................. 193 8.3.15 xmlGetHeaderAttribs ............................................................................................. 194 String-Funktionen in Frontend-Plug-ins .............................................................................. 195 8.4.1 calcAge .................................................................................................................. 195 8.4.2 checkEmail............................................................................................................. 195 8.4.3 codeString .............................................................................................................. 195 8.4.4 encryptEmail .......................................................................................................... 196 8.4.5 HTMLcaseshift ...................................................................................................... 196 8.4.6 keywords ................................................................................................................ 196 8.4.7 linebreaks ............................................................................................................... 197 8.4.8 processParams ........................................................................................................ 197 8.4.9 uniqueHash............................................................................................................. 198 8.4.10 URLqMark ............................................................................................................. 198 Bilder .................................................................................................................... 199 Einbinden............................................................................................................................. 199 Bearbeiten ............................................................................................................................ 200 9.2.1 Bild umrechnen (fürs Web optimieren).................................................................. 200 9.2.2 Abmessungen ändern ............................................................................................. 201 9.2.3 Zuschneiden ........................................................................................................... 201 9.2.4 Graustufen.............................................................................................................. 202 Erzeugen .............................................................................................................................. 203 9.3.1 Bild in bestehendes Bild einfügen.......................................................................... 203 9.3.2 Text erzeugen......................................................................................................... 204 AJAX im Frontend ............................................................................................... 205 eID ....................................................................................................................................... 205 10.1.1 Konfiguration ......................................................................................................... 205 10.1.2 Basisdatei ............................................................................................................... 206

Inhalt 10.2

Zusammenspiel AJAX & eID ..............................................................................................206 10.2.1 Aufbau der XML-Response....................................................................................206 10.2.2 AJAX-Request und Verarbeitung...........................................................................207

11 11.1

Frontend-Plug-ins ................................................................................................ 209 Cache ...................................................................................................................................209 11.1.1 cacheExpires...........................................................................................................209 11.1.2 clearPageCacheContent..........................................................................................209 11.1.3 clearPageCacheContent_pidList.............................................................................209 11.1.4 get_cache_timeout..................................................................................................210 11.1.5 set_cache_timeout_default .....................................................................................210 11.1.6 set_no_cache ..........................................................................................................210 Content-Elemente.................................................................................................................211 11.2.1 cImage ....................................................................................................................211 11.2.2 cleanFormName .....................................................................................................211 11.2.3 cObjGet ..................................................................................................................211 11.2.4 cObjGetSingle ........................................................................................................212 11.2.5 currentPageUrl........................................................................................................212 11.2.6 fileResource............................................................................................................213 11.2.7 getImgResource......................................................................................................213 11.2.8 getSlidePids............................................................................................................213 11.2.9 gifBuilderTextBox..................................................................................................214 11.2.10 stdWrap ..................................................................................................................214 Links ....................................................................................................................................215 11.3.1 baseUrlWrap...........................................................................................................215 11.3.2 getMailTo ...............................................................................................................215 11.3.3 getTypoLink ...........................................................................................................216 11.3.4 getTypoLink_URL .................................................................................................216 11.3.5 http_makelinks .......................................................................................................217 11.3.6 imageLinkWrap......................................................................................................217 11.3.7 mailto_makelinks ...................................................................................................218 11.3.8 pi_getPageLink.......................................................................................................218 11.3.9 pi_linkToPage ........................................................................................................218 11.3.10 pi_linkTP................................................................................................................219 11.3.11 pi_linkTP_keepPIvars ............................................................................................219 11.3.12 pi_linkTP_keepPIvars_url ......................................................................................219 11.3.13 pi_openAtagHrefInJSwindow ................................................................................220 11.3.14 prefixLocalAnchorsWithScript...............................................................................220 Listen ...................................................................................................................................220 11.4.1 pi_list_browseresults ..............................................................................................221 11.4.2 pi_list_linkSingle....................................................................................................222 11.4.3 pi_list_makelist ......................................................................................................222 11.4.4 pi_list_modeSelector ..............................................................................................223 11.4.5 pi_list_searchBox ...................................................................................................223

11.2

11.3

11.4

XI

Inhalt 11.5

11.6

11.7

11.8

XII

JavaScript............................................................................................................................. 224 11.5.1 additionalHeaderData............................................................................................. 224 11.5.2 additionalJavaScript ............................................................................................... 224 11.5.3 JSeventFuncCalls ................................................................................................... 225 11.5.4 minifyJavaScript .................................................................................................... 225 11.5.5 quoteJSvalue .......................................................................................................... 226 11.5.6 rawUrlEncodeJS..................................................................................................... 226 11.5.7 setJS ....................................................................................................................... 227 11.5.8 wrapJS.................................................................................................................... 227 CSS ...................................................................................................................................... 228 11.6.1 additionalCSS......................................................................................................... 228 11.6.2 additionalHeaderData............................................................................................. 228 11.6.3 pi_getClassName.................................................................................................... 229 11.6.4 pi_setClassStyle ..................................................................................................... 229 11.6.5 setCSS .................................................................................................................... 229 Mehrsprachigkeit ................................................................................................................. 230 11.7.1 getLLL ................................................................................................................... 230 11.7.2 pi_getLL................................................................................................................. 230 11.7.3 readLLfile............................................................................................................... 231 Umgebungsvariablen und allgemeine Funktionen ............................................................... 232 11.8.1 absRefPrefix........................................................................................................... 232 11.8.2 additionalHeaderData............................................................................................. 232 11.8.3 all ........................................................................................................................... 232 11.8.4 anchorPrefix ........................................................................................................... 233 11.8.5 applicationData ...................................................................................................... 233 11.8.6 ATagParams........................................................................................................... 233 11.8.7 baseUrl ................................................................................................................... 234 11.8.8 baseUrlWrap .......................................................................................................... 234 11.8.9 beUserLogin........................................................................................................... 234 11.8.10 clientInfo ................................................................................................................ 234 11.8.11 content.................................................................................................................... 235 11.8.12 defaultBodyTag...................................................................................................... 235 11.8.13 domainStartPage .................................................................................................... 235 11.8.14 extTarget ................................................................................................................ 236 11.8.15 fePreview ............................................................................................................... 236 11.8.16 id ........................................................................................................................... 236 11.8.17 imagesOnPage........................................................................................................ 237 11.8.18 intTarget ................................................................................................................. 237 11.8.19 lang......................................................................................................................... 237 11.8.20 lastImageInfo ......................................................................................................... 238 11.8.21 loginUser................................................................................................................ 238 11.8.22 no_cache................................................................................................................. 238 11.8.23 page ........................................................................................................................ 239 11.8.24 printError................................................................................................................ 239 11.8.25 rootLine.................................................................................................................. 240 11.8.26 siteScript ................................................................................................................ 240

Inhalt 11.8.27 tmpl->config, tmpl->setup......................................................................................241 11.8.28 type.........................................................................................................................241 11.8.29 TYPO3_CONF_VARS ..........................................................................................241 11.8.30 uniqueString ...........................................................................................................242 11.9 Konfiguration mit Flexforms ...............................................................................................242 11.9.1 Erstellen einer Plug-in_Flexform ...........................................................................242 11.9.2 Auslesen der Formulardaten...................................................................................243 11.10 Konfiguration mit TypoScript ..............................................................................................244 11.11 Konfiguration im Extension-Manager..................................................................................244 12 12.1 12.2

13 13.1

13.2

13.3 13.4 13.5

Frontend-User & Sessions .................................................................................. 245 Frontend-User ......................................................................................................................245 12.1.1 Daten des aktuell angemeldeten Users ...................................................................245 Sessions................................................................................................................................246 12.2.1 Daten speichern ......................................................................................................246 12.2.2 Daten auslesen........................................................................................................246 12.2.3 Warenkorb aufbauen ..............................................................................................246 Backend & Services............................................................................................. 249 Eigene Flexforms .................................................................................................................249 13.1.1 Mehrsprachigkeit....................................................................................................250 13.1.2 Tabs ........................................................................................................................251 13.1.3 Elemente.................................................................................................................252 13.1.4 Flexform-Daten im Frontend..................................................................................256 Backend-Module..................................................................................................................257 13.2.1 Dokumententypen für $this->doc ...........................................................................257 13.2.2 TypoScript einer Seite auslesen..............................................................................257 13.2.3 AJAX im Backend..................................................................................................257 Services................................................................................................................................259 cli .........................................................................................................................................259 Debugging............................................................................................................................260 13.5.1 debug ......................................................................................................................260 13.5.2 debug_ordvalue ......................................................................................................261 13.5.3 debug_trail..............................................................................................................261 13.5.4 debugRows .............................................................................................................261 13.5.5 devLog....................................................................................................................262

Teil III – Extbase und Fluid 14 14.1

Der neue Weg der Extension-Programmierung ................................................ 265 Grundlagen der objektorientierten Programmierung............................................................267 14.1.1 Klassen und Objekte...............................................................................................267 14.1.2 Vererbung von Klassen ..........................................................................................271

XIII

Inhalt

14.2

15 15.1 15.2 15.3 15.4

15.5

15.6

15.7

16 16.1

XIV

14.1.3 Kontrollierte Vererbung – abstrakte und finale Klassen......................................... 273 14.1.4 Sichtbarkeiten: public, private und protected ......................................................... 274 14.1.5 Interfaces................................................................................................................ 275 14.1.6 Type Hints.............................................................................................................. 277 14.1.7 Statische Methoden und Eigenschaften.................................................................. 277 14.1.8 Namespaces............................................................................................................ 278 Neue Konzepte..................................................................................................................... 279 14.2.1 Domain Driven Design........................................................................................... 280 14.2.2 Model-View-Controller.......................................................................................... 281 14.2.3 Modellierung.......................................................................................................... 282 14.2.4 Gemeinsamens Vokabular – Ubiquitous Language................................................ 282 14.2.5 Die Elemente des Modells...................................................................................... 285 Ein Rundgang durchs System............................................................................ 287 Installation von Extbase und Fluid....................................................................................... 287 Installation der Extension blog_example ............................................................................. 289 Datenstruktur im Blog-Example .......................................................................................... 291 Namenskonventionen........................................................................................................... 293 15.4.1 Verzeichnis- und Dateinamen ................................................................................ 294 15.4.2 Klassennamen ........................................................................................................ 294 15.4.3 Controller und Actions ........................................................................................... 295 Die Dateistruktur im Extension-Root-Verzeichnis .............................................................. 295 15.5.1 Die Datei ext_emconf.php...................................................................................... 296 15.5.2 Die Datei ext_tables.php ........................................................................................ 297 15.5.3 Die Datei ext_localconf.php................................................................................... 301 15.5.4 Die Dateien ext_tables.sql und ext_icon.gif ........................................................... 302 Die Verzeichnisstruktur ....................................................................................................... 302 15.6.1 Das Verzeichnis Classes......................................................................................... 303 15.6.2 Das Verzeichnis Configuration .............................................................................. 304 15.6.3 Das Verzeichnis Module ........................................................................................ 305 15.6.4 Das Verzeichnis Resources .................................................................................... 305 15.6.5 Weitere Verzeichnisse............................................................................................ 305 Der prinzipielle Ablauf ........................................................................................................ 305 15.7.1 Aufruf des Dispatchers (Schritt 1).......................................................................... 306 15.7.2 Aufruf des Controllers (Schritt 2)........................................................................... 309 15.7.3 Ansprechen des Respositorys (Schritt 3)................................................................ 309 15.7.4 Zurückliefern der Blog-Objekte (Schritt 4) ............................................................ 309 15.7.5 Das Objekt wird dem View zugeordnet (Schritt 5) ................................................ 311 15.7.6 Rückgabe der Template-Ausgabe an den Controller (Schritt 6)............................. 313 15.7.7 Rückgabe der Ausgabe an den Dispatcher (Schritt 7) ............................................ 314 15.7.8 Rückgabe der Ausgabe an TYPO3 (Schritt 8)........................................................ 314 Entwicklung eines eigenen Plug-ins.................................................................. 315 Aufbau einer minimal funktionstüchtigen Extension........................................................... 315 16.1.1 Extension-Key........................................................................................................ 315 16.1.2 Verzeichnisse anlegen ............................................................................................ 316

Inhalt

16.2 16.3 16.4

16.5 16.6 16.7 16.8

16.9 16.10 16.11 16.12

16.13

16.14 16.15 16.16 16.17 16.18

16.1.3 Die Datei ext_emconf.php......................................................................................316 16.1.4 Die Dateien ext_localconf.php und ext_tables.php ................................................317 16.1.5 Einrichten eines Standard-Controllers ....................................................................318 16.1.6 Installieren der Extension und das erste Erfolgserlebnis ........................................319 Hinzufügen eines Views.......................................................................................................320 Entitäten einführen ...............................................................................................................322 Daten persistieren (Datenspeicher).......................................................................................324 16.4.1 Datenbankstruktur – die Datei ext_tables.sql .........................................................325 16.4.2 Datenbankstruktur – das TCA ................................................................................329 Anlegen eines Repositorys ...................................................................................................333 Anlegen einer neuen Action.................................................................................................335 16.6.1 Anlegen der Action addAction() ............................................................................336 Daten per Formular eingeben und auswerten .......................................................................337 Objekte aus dem Repository entfernen.................................................................................338 16.8.1 Zufügen der Action delete in der Konfiguration.....................................................338 16.8.2 Anpassen der Template-Datei index.html...............................................................338 Update von Objekten............................................................................................................339 16.9.1 Edit- und Update-Action hinzufügen......................................................................339 Der Query-Manager .............................................................................................................341 Eingabevalidierung ..............................................................................................................343 Validatoren...........................................................................................................................343 16.12.1 Vordefinierte Validatoren.......................................................................................343 16.12.2 Eigene Validatoren .................................................................................................344 16.12.3 Ausgabe der Fehler.................................................................................................347 16.12.4 Optionale Argumente .............................................................................................347 Relationen zu anderen Tabellen ...........................................................................................348 16.13.1 Erweiterung des TCA .............................................................................................348 16.13.2 Die Domain-Klasse Tx_Simpleblog_Domain_Model_Post ...................................349 16.13.3 Registrieren der Actions in der Datei ext_localconf.php ........................................350 16.13.4 Erstellung des Post-Controllers ..............................................................................350 16.13.5 Neue Templates und Template-Änderungen ..........................................................353 16.13.6 Ändern der Blog-Identity........................................................................................354 16.13.7 Aufruf der Extension im Frontend..........................................................................356 Relationen zu anderen Tabellen m:n ....................................................................................356 Mehrsprachigkeit zufügen....................................................................................................361 Konfiguration mittels TypoScript ........................................................................................363 Backend-Module mit Extbase ..............................................................................................365 Der Extbase-Kickstarter .......................................................................................................366 16.18.1 Installation des Extbase-Kickstarters......................................................................367 16.18.2 Überblick über den Arbeitsbereich .........................................................................368 16.18.3 Eingabe der Extension-Konfiguration ....................................................................369 16.18.4 Modellierung ..........................................................................................................370 16.18.5 Anlegen des Post-Objekts.......................................................................................372 16.18.6 Anlegen des Tag-Objekts .......................................................................................372 16.18.7 Relationen festlegen ...............................................................................................373

XV

Inhalt 16.19 Weitere Extbase-Interna....................................................................................................... 374 16.19.1 StoragePid .............................................................................................................. 374 16.19.2 MVC-Request ........................................................................................................ 376 16.19.3 FlashMessages realisieren ...................................................................................... 377 17 17.1 17.2

17.3

17.4

17.5

17.6 17.7

Die Fluid-Template-Engine.................................................................................. 379 Vorbereitung ........................................................................................................................ 380 Basissyntax und einfache Ausgabe ...................................................................................... 382 17.2.1 Arrays..................................................................................................................... 382 17.2.2 Objekte................................................................................................................... 383 Fluid ViewHelper-Syntax .................................................................................................... 384 17.3.1 Namespace (Namensraum)..................................................................................... 384 17.3.2 Argumente.............................................................................................................. 385 ViewHelper-Übersicht ......................................................................................................... 387 17.4.1 alias ........................................................................................................................ 387 17.4.2 base ........................................................................................................................ 388 17.4.3 cObject ................................................................................................................... 388 17.4.4 count....................................................................................................................... 389 17.4.5 cycle ....................................................................................................................... 389 17.4.6 debug...................................................................................................................... 390 17.4.7 else ......................................................................................................................... 390 17.4.8 for........................................................................................................................... 390 17.4.9 form........................................................................................................................ 391 17.4.10 format..................................................................................................................... 402 17.4.11 groupedFor ............................................................................................................. 406 17.4.12 if ........................................................................................................................... 407 17.4.13 image...................................................................................................................... 408 17.4.14 layout...................................................................................................................... 409 17.4.15 link ......................................................................................................................... 409 17.4.16 render ..................................................................................................................... 414 17.4.17 renderFlashMessages ............................................................................................. 414 17.4.18 section .................................................................................................................... 415 17.4.19 then......................................................................................................................... 415 17.4.20 translate .................................................................................................................. 415 17.4.21 uri ........................................................................................................................... 416 Erstellen eines eigenen ViewHelpers................................................................................... 416 17.5.1 Der Dummytext-ViewHelper ................................................................................. 417 17.5.2 Zugriff auf die übergebenen Argumente ................................................................ 418 17.5.3 Zufügen von Argumenten ...................................................................................... 419 17.5.4 Tag-basierende ViewHelper................................................................................... 419 17.5.5 Der Variablen-Container ........................................................................................ 420 Verwendung von Fluid in klassischen Extensions ............................................................... 421 Layouts und Partials............................................................................................................. 423

Register............................................................................................................................ 427

XVI

Vorwort Das letzte Jahr war voll von Veränderungen und Neuerungen, die es nicht ganz leicht machten, dieses Buch zu vollenden. Ich danke daher unserem Lektor Fernando Schneider für seine Engelsgeduld mit uns. Besonderer Dank gebührt Börni, der sich in einer Nachtund-Nebel-Aktion ins Boot gewuchtet hat, um mit Patrick und mir dieses Projekt zu meistern. Seine Engagement und seine Ideen haben dem Buch den richtigen Impuls gegeben. Ich danke Pat für die vielen guten Ideen und die Rolle als treibende Kraft in so vielen Aspekten meiner beruflichen und auch privaten Laufbahn. Ich danke ebenso meinen Eltern und meiner Großmutter für ihren Rückhalt. Auch meinen Kolleginnen und Kollegen bei FTI, Chris, Bogomip, Matthias, Vivian, Silke, Nanni, Mirja, Emilios, Murat, Alf, Marita und allen anderen möchte ich meinen Dank aussprechen, haben sie mir doch im vergangenen Jahr ein neues berufliches, aber auch freundschaftliches Zuhause gegeben, in dem ich mich pudelwohl fühle. Meinen Teil widme ich meinem Großvater. Das hier hätte Dir gefallen.

Alexander Ebner

Dieses Buch hat eine lange und vielleicht auch etwas schwere Geburt hinter sich. Aber es hat sich durchaus gelohnt, so lange damit zu warten. Nur so hatten wir die Chance die Themen Extbase & Fluid hineinzunehmen, die im ursprünglichen Manuskript nicht vorgesehen waren, da diese hoch spannenden Technologien zu jenem Zeitpunkt noch nicht zur Verfügung standen. Allein dieser Umstand, so unsere Hoffnung, wird manchem Leser die lange Wartezeit im Nachhinein vielleicht ein wenig versüßen.

XVII

Vorwort Mein Dank geht natürlich auch an meinen Freund Alex, der dieses Projekt trotz widriger Umstände und meiner Gängeleien bestens gemeistert hat, und auch Börni, den wir komplett ins kalte Wasser schmissen, indem wir ihn fragten, ob er sich nicht zu uns gesellen will. Er hat seinen Erstling mit Bravour gemeistert. Es hat mir sehr viel Spaß gemacht, dieses Projekt mit Euch zu stemmen. Vor allem danke ich Fernando, unserem geschätzten Lektor. Er hat stets an uns und dieses Buch geglaubt und es so überhaupt noch möglich gemacht. Mein größter Dank allerdings gebührt der Liebe meines Lebens – meiner Frau Marina. Ohne Ihre Geduld und Ihren Glauben an mich und meine Arbeit hätte ich auch dieses Buch sicherlich nie zu Ende bringen können. Unzählige Stunden hat Sie (wieder einmal) auf mich verzichtet und ist vielleicht nun, da dieses Buch das Licht der Welt erblickt hat, etwas stolz. Und wenn sich auch am Ende nur eine kurze Erwähnung im Vorwort dazu findet – bei jedem Buchstabe, bei jedem Satz und bei jeder Seite waren meine Gedanken stets auch bei Dir.

Patrick Lobacher

Zunächst einmal möchte ich meinen beiden Autorenkollegen Patrick und Alex für das große Vertrauen danken, das sie in mich hatten – auf ihre ganz unkomplizierte und lockere Art und Weise haben sie mich mit ins Boot genommen. Alleine hätte ich mir das sicherlich nie zugetraut - vielen vielen Dank dafür! Es war für mich ein sehr spannendes, lehrreiches aber vor allem auch schönes erstes Buchprojekt. Mein größter Dank gilt aber natürlich meiner geliebten Frau Silvi. Sie hat mich von Anfang an zu hundert Prozent unterstützt und hat immer daran geglaubt, dass ich diese Aufgabe schaffen werde. Von unserer ohnehin schon knappen gemeinsamen Zeit hat sie mir an vielen Tagen und Wochenenden den Rücken vollkommen frei gehalten – ohne diese Unterstützung, mein Bär, hätte ich es sicher nie geschafft! Und schließlich widme ich dieses Buch unserer süßen Maus Marie. Auch sie musste viele viele Stunden auf ihren Papa verzichten und hat es sogar gelernt, die Tür zum Büro geschlossen zu lassen. Vielleicht findet ja auch sie eines Tages Gefallen an TYPO3 – ich arbeite daran ;-)

Bernhard Ulbrich

XVIII

I Teil I – Klassische Extensionprogrammierung

1 1 1.1

Grundlagen

Aufbau und Struktur von TYPO3 Um Extensions für TYPO3 entwickeln zu können, müssen Sie über den grundsätzlichen Aufbau von TYPO3 Bescheid wissen. Dazu gehört zum Beispiel, in welcher Reihenfolge TYPO3 die einzelnen Schritte, die nötig sind, um eine Webseite zu erstellen, abarbeitet. Der schematische Aufbau geht vom Webbrowser aus. Damit rufen Sie entweder das Backend oder das Frontend auf. Beide greifen wiederum auf die Extensions zu, die in dem System installiert sind. Selbst das CMS ist als Extension realisiert. Alle Extensions greifen über die Extension-API auf den TYPO3-Kern (Core) zu. Dieser beinhaltet nur die grundlegenden Funktionen. Der Kern, wie auch alle Extensions, sind in der Programmiersprache PHP geschrieben. Als Basis dienen ein Webserver (z.B. Apache, lighttpd, IIS) und eine Datenbank (z.B. MySQL, Oracle, PostreSQL). Es ist möglich, die TYPO3-Extension-API zu umgehen und beispielsweise Datenbankzugriffe nativ in PHP zu schreiben. Aber dieses „Zu-Fuß-Gehen“ ist nicht empfehlenswert. Über die API stehen Ihnen weitere Features wie Abstraktion, Validierungen etc. zur Verfügung. Um beim Beispiel Datenbankzugriff zu bleiben, über die API-Funktionen kann eine Extension auf verschiedenen Datenbanksystemen verwendet werden. Wenn Sie die mysql-Funktionen von PHP direkt verwenden, wird Ihre Extension auch nur mit MySQL zusammenarbeiten. Daher ist die API ein sehr wichtiger Schritt im Ablauf. Sehr interessant ist allerdings, was im Einzelnen denn nun genau passiert, wenn eine Webseite im Frontend aufgerufen wird. Jeder Aufruf im Frontend beginnt mit der Datei index.php im Rootverzeichnis (DocumentRoot). Ganz egal, ob Sie eine Seite tief im Seitenbaum aufrufen und wie viele Parameter Sie der URL anhängen, am Ende rufen Sie doch nur diese eine Datei auf. Das gilt auch beim Einsatz von Extensions wie realURL und CoolURI, die semantische URLs erzeugen. Durch die Direktiven der .htacces-Datei wird dennoch auf die index-php zugegriffen. In dieser ist der Pfad zur TypoScript-Library (PATH_tslib) hinterlegt, die damit inkludiert wird. Als Nächstes wird die Datei in-

3

1 Grundlagen dex_ts.php aus der Extension cms eingebunden, die den Rest der Ablaufsteuerung übernimmt. Ist diese nicht verfügbar, quittiert der Server die Anfrage mit einer Fehlermeldung und widmet sich anderen Dingen. In der index_ts.php kommt es zu folgendem Ablauf im Frontend-Rendering-Prozess: 1. Die Umgebungsparameter wie Betriebsystem, Webserver und Art der PHP-Unterstützung werden ermittelt. 2. Die Pfade zum typo3conf- und t3lib-Verzeichnis werden bestimmt. 3. Der Time-Tracker (PATH_t3lib.’class.t3lib_timetrack.php) startet. Der Time-Tracker loggt die Parse-Zeiten und Kommentare. Diese werden im Admin-Panel zur Verfügung gestellt. 4. Die obligatorischen Klassen _t3lib_div, die diverse allgemeine Funktionen zur Verfügung stellt, und t3lib_extmgm, die das Extension-Handling durchführt, werden geladen. 5. Nun beginnt die Konfigurationsphase. Es wird eine Standardkonfiguration aus PATH_t3lib.config_default.php geladen. Dann wird die eigentliche Konfigurationsdatei typo3conf/localconf.php aufgerufen. Als Letztes werden noch die Konfigurationen aus den Extensions geladen. Diese befinden sich in den ext_ localconf.php-Dateien in den jeweiligen Extension-Verzeichnissen. Der Datenbankzugriff aufgrund der Konfiguration wird überprüft. Ist auch die Extension cms korrekt geladen, kann es weiter gehen. 6. Wird als GET- oder POST-Parameter die eID übergeben, wird diese nun ausgewertet. Dazu ist ein Ausstiegspunkt definiert, der den Frontend-Rendering-Prozess abbricht. Der eID-Parameter kommt beispielsweise bei AJAX-Requests zum Tragen, wenn nicht die komplette Seite, sondern nur Teile des Inhalts benötigt werden. Der Parameter beinhaltet die eine Klasse, die an dieser Stelle aufgerufen wird und die restliche Ausgabe erledigt. Diese Klasse lädt die eID-Helper-Klasse nach, die eine Datenbankunterstützung zur Verfügung stellt und den möglichen Frontend-User definiert. 7. Wird der Vorgang nicht durch einen eID-Prozess abgebrochen, wird er fortgesetzt, indem folgende Frontend-Bibliotheken geladen werden:

ƒ

PATH_tslib.’class.tslib_fe.php’

Klasse für das TypoScript-basierte Frontend

ƒ

PATH_tslib.’class.tslib_page.php’

Klasse für die Seitenfunktionen

ƒ

PATH_tslib.’class.tslib_userauth.php’

Klasse für die Authentifizierung von FE-Usern

ƒ

PATH_tslib.’class.tslib_feuserauth.php’

Klasse für die Frontend-Session und das Login

ƒ

PATH_tslib.’class.tslib_fe.php’

Klasse für die Template-Verarbeitung

ƒ

PATH_tslib.’class.tslib_cs.php’

Klasse für die Zeichenkonvertierung von und zu UTF-8

4

1.1 Aufbau und Struktur von TYPO3 8. Initialisierung und Befüllung des $TSFE-Objektes (TypoScript-Frontend). Erste Werte, mit denen es befüllt wird, sind einige GBVars (GET-/POST-Variablen): id, type, no_cache, cHash, jumpurl, MP und RDCT. 9. Bei gesetztem RDCT-Parameter wird mittels des zugehörigen MD5-Hashwertes der entsprechende Link aus der Datenbank geholt und eine Weiterleitung dahin ausgelöst. 10. Die Kompression wird aktiviert, sofern die Gzip-Kompression in der Variablen $TYPO3_CONF_VARS[’FE’][’compressionLevel] gesetzt ist. 11. Initialisierung des Frontend-Users. Dazu wird die Session ausgelesen und überprüft,

ob der User sich neu einloggen muss. 12. Überprüfung des Cookies auf den Wert preBeUser. Ist dieser gesetzt, wird geprüft, ob ein Backend-User angemeldet ist, der dann initialisiert wird. Ist dies erfolgreich geschehen, werden weitere Klassen für BE-Funktionalitäten nachgeladen:

ƒ

PATH_tslib.’class.tslib_befunc.php’

Klasse für Standardfunktionen des Backends.

ƒ

PATH_tslib.’class.tslib_userauthgroup.php’

Klasse zur Authentifizierung und Initialisierung von BE-Usergroups

ƒ

PATH_tslib.’class.tslib_beauthuser.php’

Klasse zur Authentifizierung von BE-Usern

ƒ

PATH_tslib.’class.tslib_beauthuser.php’

Klasse zur Authentifizierung von BE-Usern (TSFE-spezifisch) 13. Wurde ein Workspace-Preview angefordert, wird dieses nun initialisiert. 14. Bestimmung der Page-ID (pid). Da jedes Element, jeder Datensatz einer pid zugeordnet ist, ist sie die zentrale Komponente im Rendering-Prozess. 15. Einlesen der $TCA (Table Configuration Array). Das TCA definiert die (veränderbaren) Datenbanktabellen und ihre Beziehung untereinander. Darüber hinaus definiert sie die Darstellung der einzelnen Formularelemente der Felder im Backend und im FEEditing. 16. Verfügbarkeit der Seite im Cache wird geprüft. 17. Überprüfung, ob das config-Array existiert. Gegebenenfalls wird es nachgeladen. 18. POST-Variablen werden in einen vorher definierten Zeichensatz konvertiert. 19. Setzen der Sprache im Frontend und einstellen der Locales (Datumsformat, Zeit, Währung etc.) 20. Ist JumpUrl aktiviert und die aktuelle Seite per TypoScript-Typolink als externe Seite definiert, wird nun die ZielURL ermittelt. Die Weiterleitung selbst wird erst in Schritt 27 erfolgen. Dadurch ist es möglich, Statistiken zu erhalten, wie oft beispielsweise ein Link angeklickt wurde. Die JumpUrl-Konfiguration erfolgt im Setup des Templates: config.jumpurl_enable = 1 (oder 0) config.jumpurl_mailto_disable = 0 (oder 1)

5

1 Grundlagen 21. Verarbeitung der Formulardaten. Dies schließt Formulare im Frontend sowie E-MailFormulare mit ein. 22. Die Seite wird generiert, aber noch nicht ausgegeben. Ist sie bereits im Cache vorhanden, wird die Version aus dem Cache verwendet, ansonsten wird sie neu gerendert, also zusammengebaut. 23. Einfügen weiterer Files. Beispielsweise Dateien, die über PHP_SCRIPT_INT definiert wurden (includeLibs). 24. Wurde keine JumpUrl definiert, erfolgt nun der Output der Seite. Alle Inhalte und Skripte werden im $TSFE-Objekt zusammengefügt und über echo ausgegeben. Wurde allerdings die Gzip-Komprimierung aktiviert, werden die Inhalte nur zusammengefügt, aber noch nicht ausgegeben. Das erfolgt dann in Schritt 31. 25. Die mögliche Frontend-Session, die in Schritt 11 ermittelt wurde, wird abgespeichert. 26. Die Statistik wird erstellt. Zum Beispiel wird die Parse-Zeit ermittelt. 27. Wurde eine JumpUrl definiert, wird auf diese nun weitergeleitet. 28. Da es möglich ist, Seiten auch statisch zu publizieren, wird dies hier durchgeführt, falls die Konfiguration dies verlangt. 29. Das Admin-Panel wird an den Quellcode angehängt und ausgegeben, sofern es aktiviert wurde und der angemeldete Backend-Benutzer die Rechte dazu hat. 30. Bei aktiviertem Deugging werden alle Debug-Messages ausgegeben. 31. Ausgabe der Daten, falls Gzip-Kompression aktiviert ist.

1.2

Aufbau einer Extension Eine Extension kann aus einem oder mehreren Abschnitten bestehen. Die FrontendAusgabe besteht (in der Regel) aus einem Plug-in. Davon können durchaus mehrere in einer Extension existieren. Sie liegen in eigenen Verzeichnissen mit der Bezeichnung p1#, wobei # für eine vorlaufende Nummer steht. Plug-ins können auf die TypoScriptKonfiguration zurückgreifen oder aber direkt konfiguriert werden, sofern ein Konfigurationsformular implementiert wird. Backend-Module dagegen integrieren sich in das Hauptmenü des Backends und geben keine Ausgabe an das Frontend weiter. Sie sind in Unterverzeichnissen mit der Bezeichnung mod# organisiert. Auch Services können in einer Extension existieren, um Funktionen bereitzustellen. Für AJAX-Requests und ähnliche Aufgaben können Skripte hinzugefügt werden, die über den eID-Parameter aufgerufen werden können und die meisten Funktionalitäten des TYPO3-Cores umgehen. Nur der Datenbankzugriff und das FEuser-Objekt sind hier verfügbar. Eine Dokumentation darf ebenfalls nicht fehlen.

6

1.3 Arten und Einteilung von Extensions

1.3

Arten und Einteilung von Extensions Es gibt drei Arten von Extensions:

„ Lokale Extensions „ Globale Extensions „ Systemextensions Die Reihenfolge spiegelt die Priorität wider. Ist eine Extension also beispielsweise lokal und global installiert, wird die lokale Version verwendet, da sie die höhere Priorität besitzt. Um Extensions etwas zu ordnen, sind sie in verschiedene Kategorien eingeteilt. Backend Extensions, die im Backend zum Tragen kommen, aber keinen eigenen Menüpunkt im Hauptmenü besitzen. Sie stellen in der Regel Erweiterungen bestehender Funktionalitäten oder Tools dar. Ein Beispiel ist rtehtmlarea. Backend-Modul Extensions mit einem eigenen Menüpunkt, also einem eigenen Modul. Als Beispiel wäre hier quixplorer zu nennen. Frontend Erweiterungen für das Frontend, die nicht unbedingt eine eigene Ausgabe erzeugen, diese aber durchaus beeinflussen und verändern können. Die bekannteste Extension dieser Art ist die css_styled_content. Frontend-Plug-in Eine Extension, die ein eigenes Plug-in mitbringt und eine Ausgabe im Frontend erzeugt. Eine der bekanntesten Extension dieser Kategorie ist tt_news. Services Services stellen Dienstleistungen und Funktionalitäten für andere Extensions bereit. Ein Service kann beispielsweise Daten über externe Schnittstellen weiterverarbeiten. Documentation Diese Extensions dienen zur Bereithaltung von Dokumentationen und Manuals. Diese werden in der Regel als OpenOffice-Dokumente bereitgestellt. Examples Tutorials und Beispiele für TYPO3 und Extensions.

7

1 Grundlagen Templates Fertige TYPO3-Templates. Diese stehen sowohl als HTML-/CSS-Templates wie auch für TemplaVoila und TemplateAutoparser bereit. Templates für timtab werden beispielsweise in dieser Kategorie im TER bereitgehalten. Miscellaneous Alles, was nicht in die eben genannten Kategorien fällt, wird hier eingeordnet. [] Diese Nichtkategorie sammelt Extensions, für die keine Kategorie angegeben wurde. Die Entscheidung, in welche Kategorie eine Extension fällt, ist nicht immer einfach. Hier sollten Sie den Hauptzweck der Extension hinterfragen und danach eine Bewertung vornehmen. Wenn eine Extension mit FE-Plug-ins, BE-Modulen und vielleicht noch Services daherkommt, ihre Hauptaufgabe aber darin besteht, ganz tolle Sachen auf der Webseite darzustellen, ist die treffendste Kategorie natürlich Frontend-Plug-ins. Miscellaneous sollte die Ausnahme darstellen. Die Zeit, sich etwas Gedanken über die eigene Extension zu machen, sollte man schon mitbringen

1.4

Der Extension-Key Bevor Sie auch nur eine Zeile Code schreiben, kommt die Wahl des richtigen ExtensionKeys. Und diese Wahl will gut überlegt sein. Denn „eineLustigeExtension_trallalalla“ sorgt für mehr Frust und ist die fünf Sekunden Lächeln nicht wert. Der Extension-Key muss einmalig, (möglichst) treffend und (idealerweise) kurz sein. Sonderzeichen sind nicht erlaubt. Der oft anzutreffende Underscore „_“ ist zwar nicht verboten, sollte aber nicht mehr verwendet werden, da dieser im Quellcode sowieso getilgt wird. Aus „elu_krass“ wird beispielsweise „class_elukrass_pi1“. Haben Sie sich für einen Extension-Key entschieden, sollten Sie den Key reservieren. Zum einen um auszuschließen, dass es bereits eine Extension mit dem Key gibt, und anderseits um Probleme zu vermeiden, wenn ein Zweiter den genialen Einfall hatte, den Sie gerade produziert haben. Sie können natürlich den Extension-Key später noch ändern – theoretisch. Glauben Sie uns, Sie möchten das nicht tun. Dazu wäre es nötig, den Key auch in allen Skripten der Extension zu ändern. Bei umfangreichen Extensions kann das eine ganze Weile dauern. Um einen Key zu registrieren, loggen Sie sich auf der Webseite http://typo3.org ein. Wenn Sie noch keinen Login besitzen, ist dies nun die perfekte Gelegenheit für eine Registrierung auf der Seite. Nach der Anmeldung navigieren Sie in den Menüpunkt Extensions. Hier ist die Unterseite Extension-Keys von Interesse. Auf dieser Seite können Sie im Reiter Register Keys einen neuen Key anlegen.

8

1.5 TYPO3 Coding Guidelines

Abbildung 1.1 Neuen Key registrieren

1.5

TYPO3 Coding Guidelines Man könnte prinzipiell loslegen und Extensions programmieren. Einmal quer durch die API pflügen, und am Ende steht eine Extension da, die vielleicht sogar funktioniert. Aus Gründen der Sicherheit, der Erweiterbarkeit und der Zukunftssicherheit sollte man sich aber doch an einige Regeln halten. Diese Regeln haben in den TYPO3 Coding Guidelines Gestalt angenommen1. Die CGLs beinhalten beispielsweise die Verzeichnisstruktur einer Extension. Hier ist auch nachzulesen, dass eine strikte Trennung zwischen dem TYPO3-Kern und allen übrigen Dateien einzuhalten ist. PHP-Dateien, die Klassen beinhalten, müssen mit class_ im Namen beginnen, Dateien mit PHP-Interfaces mit interface_. Jede Datei sollte nur eine Klasse oder nur ein Interface beinhalten. Die Benennung erfolgt nach diesem Beispiel: class__.php

Der Namespace ordnet Klassen und Funktionen logisch zu.

1

http://typo3.org/documentation/document-library/core-documentation/doc_core_cgl/4.3.0/view/

9

1 Grundlagen

1.5.1

Namespace

Eine Reihe von Namensräumen ist bereits vordefiniert. t3lib Dieser Namensraum definiert gemeinsame Dateien für das Frontend und das Backend. Diese stellen allgemeine Funktionalitäten bereit. Sie befinden sich im t3lib-Verzeichnis des Cores und sind in Unterverzeichnisse geordnet. Die Verzeichnishierarchie wird in die Benennung der Klassendateien mit aufgenommen. User-Dateien dürfen diesen Namensraum nicht nutzen. typo3 Dieser Namensraum wird von TYPO3-Backend-Dateien verwendet, die aus historischen Gründen auch andere Präfixe in ihrer Benennung nutzen können. Keine User-Dateien erlaubt. tslib Die TypoScript-Bibliotheken sind für das Frontend-Rendering verantwortlich und halten sich aus historischen Gründen nicht zwingend an die Namenskonvention. User-Dateien dürfen diesen Namensraum ebenfalls nicht nutzen. tx_ Extensions nutzen den Namensraum tx_, gefolgt vom Extension-Key, der um die Underscores bereinigt wurde. Beispiel tx_mykey_myclass, user_ Der Namensraum user_ gilt für PHP-Dateien ohne Klasse. Die Verwendung von user_ wird nicht empfohlen. Funktionen in klassenlosen PHP-Dateien werden von TYPO3 nicht ausgeführt, wenn sie den user_-Namespace nicht verwenden. Auch die Funktionen selbst müssen dieses Präfix im Namen enthalten. ux_ Dieser Namespace ist für XCLASS-Dateien in Extensions reserviert.

10

1.5 TYPO3 Coding Guidelines

1.5.2

PHP-Regeln

PHP-Tags Die PHP-Tags dürfen als Paar nur einmal in einer Datei auftreten. Das Tag muss geschlossen werden. Listing 1.1 Korrekte Verwendung

Nach dem schließenden Tag dürfen keine Leerzeichen oder Leerzeilen kommen. Dies würde die Kompression verhindern und AJAX-Requests stören. Nicht erlaubt ist dagegen die folgende Verwendung. Listing 1.2 Falsche Verwendung

###TITLE### ###SUBTITLE### ###CRDATE### ###TEASERIMG### ###TEASER### ###BODYTEXT### ###CATEGORY### ###AUTHOR###



Speichern Sie die Datei unter dem Namen tx_elublog_template.tpl im Verzeichnis pi1 ab. Nun muss das Plug-in diese Datei einlesen und die Werte substituieren. Folgende Eintragung in der Funktion main() definiert die Template-Vorlage:

109

4 Frontend-Plug-ins Listing 4.28 Template verwenden $this->templateFile = ($conf['templateFile']) ? $this->cObj-> fileResource($conf['templateFile']) : $this->cObj-> fileResource('EXT:elublog/pi1/tx_elublog_template.tpl');

Nun müssen den Markern die Inhalte zugewiesen werden. Dazu ändern wir die Funktion $this->pi_list_row(). Listing 4.29 pi_list_row() function pi_list_row($c) { $cObj = t3lib_div::makeInstance('tslib_cObj'); $markerArray['###BODYTEXT###'] = ($this->getFieldContent ('bodytextinlist') == 1) ? $cObj->stdWrap($this->getFieldContent ('bodytext'),$this->conf['listView.']['bodytext_stdWrap']) : ''; $markerArray['###TITLE###'] = $cObj->stdWrap($this-> getFieldContent('title'),$this->conf['listView.']['title_stdWrap.']); $markerArray['###CRDATE###'] = $cObj->stdWrap(htmlspecialchars($this-> getFieldContent('crdate')),$this->conf['listView.']['crdate_stdWrap.']); $markerArray['###SUBTITLE###'] = $cObj->stdWrap(htmlspecialchars ($this->getFieldContent('subtitle')),$this->conf['listView.'] ['subtitle_stdWrap.']); $markerArray['###TEASERIMG###'] = $cObj->stdWrap($this-> getFieldContent('teaserimg'),$this->conf['listView.'] ['teaserimg_stdWrap.']); $markerArray['###TEASERTEXT###'] = $cObj->stdWrap($this-> getFieldContent('teasertext'),$this->conf['listView.'] ['teasertext_stdWrap.']); $markerArray['###AUTHOR###'] = $cObj->stdWrap($this-> getFieldContent('author'),$this->conf['listView.']['author_stdWrap.']); $markerArray['###CATEGORY###'] = $cObj->stdWrap($this-> getFieldContent('category'),$this->conf['listView.'] ['category_stdWrap.']); $markerArray['###TAGS###'] = $cObj->stdWrap($this-> getFieldContent('tags'),$this->conf['listView.']['tags_stdWrap.']); $template['item'] = $this->cObj->getSubpart($this->templateFile, '###ITEM###'); return $this->cObj->substituteMarkerArrayCached$template['item'], $markerArray); }

Es wird ein Array namens $markerArray aufgebaut, dessen Keys den Markern in der Vorlage entsprechen. Diesem Array werden die Markerinhalte zugewiesen, nachdem mittels $this->cObj->stdWrap() die StdWrap-Eigenschaften zugewiesen wurden. Am Ende wird dem Subpart ###ITEM### das Array übergeben, und damit werden die Marker in diesem Subpart substituiert. Allerdings muss auch die Funktion $this->pi_make_list() der Klasse tslib_pibase überschrieben werden, da diese Funktion eine Tabelle aufbaut, die uns hier nichts nützt. Listing 4.30 Neue Funktion $this->pi_make_list() function pi_list_makelist($res,$tableParams='') { $this->internal['currentRow']=''; $template['total'] = $this->cObj->getSubpart($this->templateFile, '###LISTVIEW###'); $template['item'] = $this->cObj->getSubpart($this->templateFile, '###ITEM###'); $c=0; $fixedMarkerArray['###AT###'] = $this->pi_getLL('at'); $fixedMarkerArray['###FROM###'] = $this->pi_getLL('from'); $fixedMarkerArray['###IN###'] = $this->pi_getLL('in');

110

4.3 HTML-Templates für das Plug-in while($this->internal['currentRow'] = $GLOBALS['TYPO3_DB']-> sql_fetch_assoc($res)) { $markerArray = $this->pi_list_row($c); $markerArrayMerged = array_merge($markerArray,$fixedMarkerArray); $contentItem .= $this->cObj->substituteMarkerArrayCached ($template['item'],$markerArrayMerged); $c++; } $content = $this->cObj->substituteMarkerArrayCached($template['total'], array(), array('###ITEM###' => $contentItem)); return $content; }

Die Datenbankeinträge werden geholt. Bei jedem Durchlauf wird die Funktion $this>pi_list_row() aufgerufen und das Ergebnis der Variablen $contentItem hinzugefügt.

Diese wird letztendlich dem Subpart ###LISTVIEW### zugewiesen. Am Ende wird der Subpart substituiert. Im TypoScript fehlen noch die Eigenschaften des stdWrap der einzelnen Felder. Jedes Feld sollte mit einem -Container gewrapt werden. Listing 4.31 stdWrap im TypoScript plugin.tx_elublog_pi1.listView.title_stdWrap.wrap = | plugin.tx_elublog_pi1.listView.subtitle_stdWrap.wrap = | plugin.tx_elublog_pi1.listView.crdate_stdWrap.wrap = | plugin.tx_elublog_pi1.listView.teaserimg_stdWrap.wrap = | plugin.tx_elublog_pi1.listView.teasertext_stdWrap.wrap = | plugin.tx_elublog_pi1.listView.bodytext_stdWrap.wrap = | plugin.tx_elublog_pi1.listView.author_stdWrap.wrap = | plugin.tx_elublog_pi1.listView.category_stdWrap.wrap = | plugin.tx_elublog_pi1.listView.tags_stdWrap.wrap = |

Das Ergebnis sehen Sie in der folgenden Abbildung.

111

4 Frontend-Plug-ins

Abbildung 4.4 Die Ausgabe der Listenansicht per Template

Auch die Ausgabe im SingleView, also der Artikel allein, muss angepasst werden. Es werden auch die Marker für die Kommentare und das Kommentarformular gesetzt. Diese werden aus eigenen Funktionen kommen, werden aber im Moment nur als Dummy gebaut. Listing 4.32 Templating der Einzelansicht function singleView($content, $conf) { $this->conf = $conf; $this->pi_setPiVarDefaults(); $this->pi_loadLL(); if ($this->internal['currentRow']['title']) $GLOBALS['TSFE']-> indexedDocTitle=$this->internal['currentRow']['title']; $content=''; $cObj = t3lib_div::makeInstance('tslib_cObj'); $markerArray['###BODYTEXT###'] = $cObj->stdWrap($this->getFieldContent ('bodytext'),$this->conf['singleView.']['bodytext_stdWrap']); $markerArray['###TITLE###'] = $cObj->stdWrap($this->getFieldContent ('title'),$this->conf['singleView.']['title_stdWrap.']); $markerArray['###CRDATE###'] = $cObj->stdWrap(htmlspecialchars ($this->getFieldContent('crdate')),$this->conf['singleView.'] ['crdate_stdWrap.']); $markerArray['###SUBTITLE###'] = $cObj->stdWrap(htmlspecialchars ($this->getFieldContent('subtitle')),$this->conf['singleView.'] ['subtitle_stdWrap.']);

112

4.3 HTML-Templates für das Plug-in $markerArray['###TEASERIMG###'] = $cObj->stdWrap($this-> getFieldContent('teaserimg'),$this->conf['singleView.'] ['teaserimg_stdWrap.']); $markerArray['###TEASERTEXT###'] = $cObj->stdWrap($this-> getFieldContent('teasertext'),$this->conf['singleView.'] ['teasertext_stdWrap.']); $markerArray['###AUTHOR###'] = $cObj->stdWrap($this->getFieldContent ('author'),$this->conf['singleView.']['author_stdWrap.']); $markerArray['###CATEGORY###'] = $cObj->stdWrap($this-> getFieldContent('category'),$this->conf['singleView.'] ['category_stdWrap.']); $markerArray['###TAGS###'] = $cObj->stdWrap($this->getFieldContent ('tags'),$this->conf['singleView.']['tags_stdWrap.']); $markerArray['###COMMENTS###'] = $cObj->stdWrap($this->getComments(), $this->conf['singleView.']['comments_stdWrap.']); $markerArray['###COMMENTFORM###'] = ($this->getFieldContent ('lockcomments') == 1) ? $cObj->stdWrap($this->pi_getLL ('commentsNotAllowed'),$this->conf['singleView.']['commentForm_stdWrap.'] ) : $cObj->stdWrap($this->getCommentForm(),$this->conf['singleView.'] ['commentForm_stdWrap.']); $markerArray['###AT###'] = $this->pi_getLL('at'); $markerArray['###FROM###'] = $this->pi_getLL('from'); $markerArray['###IN###'] = $this->pi_getLL('in'); $markerArray['###BACKLINK###'] = $cObj->stdWrap($this-> pi_list_linkSingle($this->pi_getLL('back','Back'),0),$this->conf ['singleView.']['backLink_stdWrap.']); $template['item'] = $this->cObj->getSubpart($this->templateFile, '###SINGLEVIEW###'); $content .= $this->cObj->substituteMarkerArrayCached ($template['item'],$markerArray); $content .= ""; $this->pi_getEditPanel(); return $content; }

Die Funktion sieht $this->pi_list_row() sehr ähnlich. Allerdings fehlt die if-Abfrage beim bodytext, der stdWrap wird durch den TypoScript-Hive singleView konfiguriert, und das Einbinden der Kommentarfunktion ist vorbereitet. Allerdings müssen wir zwei Funktionen anlegen, da sonst ein Fehler gemeldet würde. Sie müssen erst einmal nur existieren, ausprogrammiert werden sie im nächsten Schritt. Listing 4.33 Funktions-Dummys anlegen function getComments() {} function getCommentForm() {}

Da wir die Ausgabe des Funktionsselektors nicht benötigen, entfernen Sie aus der Funktion $this->listView folgende Zeilen: Listing 4.34 Funktionsselektor $fullTable.=$this->pi_list_modeSelector($items);

Wir haben die Listenansicht und die Einzelansicht mit Templates belegt. Durch Änderungen an diesen mit CSS und TypoScript haben wir nun die Möglichkeit, die Ausgabe individuell anzupassen. Im Folgenden wurde das Template in eine (sehr einfache) Seite mit drei Spalten eingebaut und gestylt.

113

4 Frontend-Plug-ins

Abbildung 4.5 Der Blog mit angepasstem Styling

4.4

Die Kommentarfunktion mit AJAX und eID Bei jedem Blog ist die Kommentarfunktion wichtig. Es gehört zum guten Ton des Web 2.0, die Besucher zu integrieren und Interaktivitäten zu ermöglichen. Um das Ganze ein wenig aufzupeppen, sollen die Kommentare über AJAX versendet werden. Der Schüssel dazu liegt im eID-Mechanismus. Dadurch sind Datenbankmanipulationen möglich, ohne den kompletten Rendering-Prozess von TYPO3 zu durchlaufen.

114

4.4 Die Kommentarfunktion mit AJAX und eID

Abbildung 4.6 Der Ablauf beim Abschicken eines Kommentars

4.4.1

Ausgabe vorhandener Kommentare

Wir beginnen also mit der Ausgabe eventuell vorhandener Kommentare und des Kommentarformulars. Dazu kommt ein wenig JavaScript für den AJAX-Request und das Einfügen des neuen Kommentars in die Kommentarliste.

115

4 Frontend-Plug-ins Um die vorhandenen Kommentare auszugeben, verwenden wir die Funktion $this-> getComments(), die wir vorhin als Dummy angelegt haben. Listing 4.35 Funktion $this->getComments() function getComments() { $template['comment'] = $this->cObj->getSubpart($this->templateFile, '###COMMENT###'); $template['item'] = $this->cObj->getSubpart($this->templateFile, '###COMMENTITEM###'); $contentItem = ''; $c=1; $cObj = t3lib_div::makeInstance('tslib_cObj'); // Datenbankabfrage nach allen Kommentaren zu dem Blogeintrag $res = $this->pi_exec_query('tx_elublog_blogcomments', 0, "AND blogentry=".$this->internal['currentRow']['uid']); // Übersetzungsmarker, die sich nicht ändern, setzen. $fixedMarkerArray['###AT###'] = $this->pi_getLL('at'); // Datensätze durchlaufen und Marker zuweisen while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { $markerArray['###NUMBER###'] = $cObj->stdWrap($c,$this->conf ['singleView.']['comment.']['number_stdWrap.']); $markerArray['###AUTHOR###'] = ($row['website'] != '') ? $cObj-> stdWrap("conf['singleView.'] ['comment.']['author_stdWrap.']."") : $cObj->stdWrap($row['author'], $this->conf['singleView.']['comment.']['author_stdWrap.']); $markerArray['###COMMENTTEXT###'] = $cObj->stdWrap(strip_tags ($row['comment']),$this->conf['singleView.']['comment.'] ['comment_stdWrap.']); $markerArray['###DATE###'] = $cObj->stdWrap($row['crdate'],$this-> conf['singleView.']['comment.']['crdate_stdWrap.']); $markerArray['###GRAVATAR###'] = $cObj->stdWrap('',$this->conf['singleView.']['comment.'] ['gravatar_stdWrap.']); // $markerArray mit den fixen Markern in $fixedMarkerArray zusammenführen $markerArrayMerged = array_merge($markerArray,$fixedMarkerArray); // Marker substituieren $contentItem .= ''.$this-> cObj->substituteMarkerArrayCached($template['item'], $markerArrayMerged). ''; $c++; } // Anzahl der Kommentare ermitteln und zuweisen. $count = $c-1; $marker['###COMMENTCOUNT###'] = $cObj->stdWrap(''.$count.''.$this-> pi_getLL('comments'),$this->conf['singleView.']['comment.'] ['commentcount_stdWrap.']); $content = ($contentItem != '') ? $this->cObj-> substituteMarkerArrayCached($template['comment'], $marker, array('###COMMENTITEM###' => $contentItem)):$this->pi_getLL ("noComments"); return ''.$content.''; }

Es wird eine Abfrage gegen die Datenbank abgesetzt, die alle Kommentare aus der Tabelle tx_elublog_blogcomments sucht, die dem Beitrag zugeordnet sind. Die Ergebnisse werden durchlaufen und den Markern zugeordnet.

116

4.4 Die Kommentarfunktion mit AJAX und eID Listing 4.36 HTML-Template für die Kommentare

###COMMENTNUMBER### ###COMMENTAUTHOR### ###AT### ###COMMENTDATE### ###GRAVATAR### ###COMMENTTEXT###

Der Container mit ID tx_elublog_newcomment wird den neuen Kommentar anzeigen, den der Besucher hinterlässt. Eine Besonderheit ist der Marker ###GRAVATAR###. Ihm wird ein sogenannter Gravatar zugeordnet. Das ist ein Bild, auch als Avatar bekannt, das oft den Kommentarverfasser, aber auch beliebig andere Inhalte zeigt. Das Bild ist einer Mail-Adresse zugeordnet und wird unter http://www.gravatar.com verwaltet. Zur Zeit der Drucklegung ist dieser Dienst kostenlos (und wird es wohl auch bleiben). Die Gravatar-Bilder haben in der Regel eine Größe zwischen 40 und 120 Pixel. Über eine API-Schnittstelle wird der MD5-Hash der Mail-Adresse bei Gravatar.com abgefragt, ob dafür ein Bild existiert. Wenn ja, wird dieses in der angeforderten Größe zurückgesendet. Andernfalls wird ein Standardbild geliefert. Zwei Parameter müssen über TypoScript konfiguriert werden: Listing 4.37 Gravatar-Konfiguration // Größe des Gravatars in Pixel plugin.tx_elublog_pi1.singleView.comment.gravatar_size = 40 // URL des Standardbildes plugin.tx_elublog_pi1.singleView.comment.gravatar_default_url = http://example.com/fileadmin/elublog/gravatar.png

Diese Parameter werden an die API zusammen mit dem MD5-Hash der Mail-Adresse übermittelt. Das Ergebnis wird in ein -Tag eingebaut.

Abbildung 4.7 Die Ausgabe der Kommentare mit Gravataren

117

4 Frontend-Plug-ins

4.4.2

Das Kommentarformular hinzufügen

Auch für das Formular haben wir bereits eine Funktion vorbereitet. Diese heißt $this-> getCommentForm(). Das Formular selbst bauen wir als HTML-Vorlage auf. Darin werden

nur ein paar Labels zum Substituieren sein. Listing 4.38 $this->getCommentForm() function getCommentForm() { /* ###### Platzhalter für AJAX-Funktionalität ###### */ $markerArray['###LABEL_COMMENTFORM###'] = $this-> pi_getLL('label_commentform'); $markerArray['###LABEL_COMMENTFORM_AUTHOR###'] = $this-> pi_getLL('label_commentform_author'); $markerArray['###LABEL_COMMENTFORM_AUTHOREMAIL###'] = $this-> pi_getLL('label_commentform_authoremail'); $markerArray['###LABEL_COMMENTFORM_WEBSITE###'] = $this-> pi_getLL('label_commentform_website'); $markerArray['###LABEL_COMMENTFORM_COMMENT###'] = $this-> pi_getLL('label_commentform_comment'); $markerArray['###LABEL_COMMENTFORM_SUBMIT###'] = $this-> pi_getLL('label_commentform_submit'); $markerArray['###BID###'] = $this->internal['currentRow']['bid']; $markerArray['###BLOGENTRY###'] = $this->internal['currentRow']['uid']; $markerArray['###DATEFORMAT###'] = $this->conf['singleView.'] ['comment.']['dateFormat']; $template['commentform'] = $this->cObj->getSubpart($this-> templateFile,'###COMMENTFORMSET###'); $content = $this->cObj->substituteMarkerArrayCached ($template['commentform'],$markerArray); return $content; }

In den Platzhalter am Anfang der Funktion setzen wir gleich die AJAX-Funktionalität ein. Die Übersetzung der Labels kommt wieder aus locallang.xml. Listing 4.39 locallang.xml Senden

Das Formular selbst ist unspektakulär. Es enthält die erforderlichen Felder und Labels. Die einzige Besonderheit ist die Abwesenheit eines -Tags. Das Formular wird mit einem onclick-Event getriggert. Listing 4.40 HTML-Template-Vorlage

Nun fehlt allerdings noch die komplette AJAX-Funktionalität. Im Prinzip wollen wir die Eingaben des Formulars zum Server übertragen, wo sie in die Datenbank geschrieben werden. Quasi als Bestätigung, sollen die Daten zurück zum Skript geschickt und als neuer Kommentar dargestellt werden. Ab Version 4.3 wird TYPO3 mit dem ExtJS JavaScript-Framework ausgeliefert. ExtJS ist ein sehr mächtiges Framework, das wir für unsere Zwecke einsetzen möchten. Wir nutzen es für den AJAX-Request selbst, um dafür den zurückgelieferten Kommentar unter den bestehenden anzufügen. Der Vorteil, wie wir gleich sehen werden, ist der, dass wir dazu wieder das Template aus der Template-Vorlage verwenden können. Natürlich kann das Beispiel auch mit anderen Frameworks oder sogar nur mit eigenem JavaScript-Code umgesetzt werden. Fügen Sie Listing 4.41 und Listing 4.42 in den Platzhalter aus Listing 4.38 ein. Da wir die Template-Vorlage, die schon besteht, verwenden wollen, müssen wir diese laden und die Marker substituieren. Da die Inhalte aber erst auf dem Client nach dem AJAXRequest bereitstehen, substituieren wir sie wiederum mit eigenen Markern für ExtJS. Listing 4.41 Template-Vorlage für ExtJS erstellen $cObj = t3lib_div::makeInstance('tslib_cObj'); $gravatar_size = $this->conf['singleView.']['comment.'] ['gravatar_size']; $gravatar_default = $this->conf['singleView.']['comment.'] ['gravatar_default_url']; $template['comment'] = $this->cObj->getSubpart($this-> templateFile,'###COMMENT###'); $template['item'] = $this->cObj->getSubpart($this-> templateFile,'###COMMENTITEM###'); $fixedMarkerArray['###AT###'] = $this->pi_getLL('at'); $markerArray['###COMMENTNUMBER###'] = $cObj-> stdWrap('{commentnumber}',$this->conf['singleView.']['comment.']

119

4 Frontend-Plug-ins ['number_stdWrap.']); $markerArray['###COMMENTAUTHOR###'] = $cObj->stdWrap('{commentauthor}', $this->conf['singleView.']['comment.']['author_stdWrap.']); $markerArray['###COMMENTTEXT###'] = $cObj->stdWrap('{comment}',$this-> conf['singleView.']['comment.']['comment_stdWrap.']); $markerArray['###COMMENTDATE###'] = $cObj->stdWrap('{commentdate}', $this->conf['singleView.']['comment.']['crdate_stdWrap.']); $markerArray['###GRAVATAR###'] = $cObj->stdWrap('{commentemail}', $this->conf['singleView.']['comment.']['gravatar_stdWrap.']); $markerArrayMerged = array_merge($markerArray,$fixedMarkerArray); $html = str_replace("\n",'','' .$this->cObj->substituteMarkerArrayCached($template['item'], $markerArrayMerged).'');

Wir lesen die Einstellungen für die Gravatar-Darstellung ein. Dann geht es an das Substituieren. Am Beispiel ###COMMENT### sieht man, dass der Marker durch {comment} ersetzt wird, nachdem ein stdWrap darauf angewendet wurde. Auch ist ersichtlich, dass der Subpart ###COMMENT### nicht verwendet wird, sondern nur der tiefer liegende ###ITEM###. Listing 4.42 Das JavaScript $js = "";

Da JavaScript nur am Rande mit der Extension-Programmierung zu tun hat, gehen wir auf das Skript nur kurz ein. Die Funktion Ext.Ajax.request() löst einen AJAX-Request mit den übergebenen Parametern aus. Der Parameter params enthält ein Array mit den zu übergebenden Parameter an das Zielskript. Diese werden aus den Values der Input-Felder ausgelesen. Im Erfolgsfall wird das Template, das zuvor erzeugt und in die Variable $html geschrieben wurde, mit den Rückgabeparametern befüllt und in den Div-Container tx_elublog_ newcomment eingehängt. Im Fehlerfall wird die Statusmeldung mit einem Fehler ausgegeben. Der AJAX-Request wird an die URL index.php?eID=tx_elublog_eid gesendet. Der Parameter eID stellt eine Besonderheit dar. Ist er gesetzt, wird der Frontend-RenderingProzess abgebrochen und die Klasse tslib_eidtools eingebunden. eID-Skripte sind etwas schwieriger zu programmieren, da viele Klassen nicht zur Verfügung stehen, dazu gehören zum Beispiel auch die tslib_pibase und tslib_cObj. Da wir aber nur den Zugriff auf die Datenbank und den FE-User benötigen, stellt das kein Problem dar. Neu in TYPO3 4.3

Wie gesagt, stehen in eID-Skripten nicht alle Funktionen zur Verfügung. Mit „FOUR3“ kommen aber ein paar Änderungen: Mit tslib_eidtools::initLanguage() wird die Sprachunterstützung bereitgestellt. Mit tslib_eidtools::initTCA() wird das TCA geladen. Und mit tslib_eidtools::getTSFE() wird die Frontend-Klasse geladen. Somit können diese Funktionalitäten ebenfalls in eID-Skripten verwendet werden.

Listing 4.43 Die eID-Datei

Als Ergebnis liefert das Skript einen JSON-String zurück, der von JavaScript mittels eval() in ein Array zurückverwandelt und ausgegeben wird. Da eID-Skripte nicht den ganzen Rendering-Prozess durchlaufen, und nur ein ganz kleiner Teil des TYPO3-Frameworks geladen wird, sind sie bedeutend schneller und ressourcen-

122

4.5 Was sind Hooks? schonender, als wenn wir direkt ein Plug-in abgefragt hätten. Das prädestiniert sie für AJAX-Abfrageskripte, auch wenn nicht die ganze Funktionalität verfügbar ist. Es besteht natürlich immer noch die Möglichkeit, einzelne Klassen zu inkludieren.

Abbildung 4.8 Ein neuer Kommentar wurde eingegeben.

4.5

Was sind Hooks? Es kann nötig sein, eine bestehende Extension um Funktionalität zu erweitern oder bestehende Funktionen zu ändern. Damit andere Programmierer das möglichst einfach tun können, werden sogenannte Hooks eingebaut. Hooks sind im Grunde Einsprungmarken, über die eine Extension erweitert wird. Wenn eine Extension eine andere Extension – oder auch den Core – mit einer Hook-Funktion erweitert, wird diese in der Datei ext_localconf.php

123

4 Frontend-Plug-ins registriert. Das Plug-in, das erweitert wird, führt eine Schleife über die Registrierung aus und ruft über t3lib_div::callUserFunc() die entsprechende Funktion auf. Listing 4.44 Registrierung einer Hook-Funktion $TYPO3_CONF_VARS['EXTCONF']['hooked_extkey']['nameDesHooks'] [$_EXTKEY] = 'EXT:'.$_EXTKEY. '/class.tx_extkey_hooks.php:tx_extkey_hooks->hookFunc';

“hooked_extkey” ist hierbei durch den Extension-Key der zu hookenden Extension zu ersetzen. Damit Hooks überhaupt verwendet werden können, müssen sie erst einmal vorhanden sein. Hier beginnen die Überlegungen, wo Hooks überhaupt sinnvoll sind. Da wäre zum Beispiel:

„ Hooks, um Marker in der Einzel- und Listenansicht hinzuzufügen oder zu ändern. „ Ein Hook, bevor ein Kommentar in die Datenbank geschrieben wird. Eine Möglichkeit, die sofort ins Auge springt, ist die Überprüfung der Mail-Adresse. Wir wollen sichergehen, dass auch wirklich eine Mail-Adresse eingegeben wird. Mit einer Regex-Überprüfung ist das schnell erledigt. In diesem Fall müssen wir unsere eID-Funktion hooken. Der Hook kommt direkt nach dem Befüllen des Arrays $fields. Listing 4.45 Hook einbauen 'cruser_id' => $GLOBALS['TSFE']->fe_user->user['uid'], 'pid' => $this->extconf['storageId'] ); $checkError = 0; if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['elublog'] ['commentCheck'])){ foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['elublog'] ['commentCheck'] as $userFunc) { $params = array( 'fields' => $fields, 'pObj' => &$this ); $checkError = $checkError + t3lib_div::callUserFunction($userFunc,$params,$this); } } if ($checkError > 0) {die(json_encode(array('fail' => 1)));} if ($res = $GLOBALS['TYPO3_DB']->exec_INSERTquery ('tx_elublog_blogcomments',$fields)) {

Jede Hook-Funktion muss 0 zurückliefern, wenn alles in Ordnung ist. Jeder Fehler erhöht die Variable $checkError. Ist sie, nachdem alle Hook-Funktionen abgearbeitet sind, größer als 0, wird der Kommentar nicht in die Datenbank eingetragen. Um unseren Hook zu testen, legen wir eine neue Extension namens elublogmailcheck an. Dazu muss im Kickstarter nur der Punkt General info ausgefüllt werden. Speichern Sie die Extension ab, und legen Sie eine neue Datei namens ext_localconf.php im Verzeichnis der Extension an. Diese füllen Sie mit folgendem Inhalt:

124

4.6 Flexforms Listing 4.46 ext_localconf.php

Der Teil zwischen den PHP-Tags kommt in eine Zeile. Wie zu sehen ist, verweist die Hook-Registrierung auf die Datei class.tx_elublogmailcheck_hooks.php, der sich aus dem Dateinamen ergebenden Klasse und der Funktion hookFunc. Legen Sie also eine Datei namens class.tx_elublogmailcheck_hooks.php an. Listing 4.47 class.tx_elublogmailcheck_hooks.php

Die Funktion hookFunc() prüft die übermittelte Mail-Adresse mit einem RegexAusdruck. Wird damit eine Mail-Adresse erkannt, liefert die Funktion 0, ansonsten 1 zurück. Somit ist es möglich, verschiedenste Arten von Prüfungen zu realisieren.

4.6

Flexforms Es wäre toll, wenn sich einige Einstellungen leichter konfigurieren ließen als über TypoScript. Und vor allem für einzelne Plug-ins, wenn sich also Einstellungen im Plug-in selbst setzen ließen. Mit Flexforms ist das möglich. Folgende Einstellungen wollen wir hier konfigurierbar machen:

„ „ „ „ „ „

Die BlogID Seite für Einzelansicht (bei Listenansicht) Seite für Listenansicht (bei Einzelansicht) Anzahl der Beiträge pro Seite Länge der Voransicht Haupttext immer in Listenansicht anzeigen

Gerade der letzte Punkt ist wichtig, werden im Moment noch die Beiträge aller eingerichteten Blogs angezeigt. Eine Möglichkeit wäre, die Tabelle tt_content um die entsprechenden Felder zu erweitern. Allerdings darf man laut den TYPO3 Coding Guidelines ei-

125

4 Frontend-Plug-ins nige Tabellen nicht mehr auf Feldbasis erweitern. Wirft man einen Blick in die Feldstruktur von tt_content, sieht man auch den Grund: Die Tabelle ist so schon sehr groß, würde jede Extension weitere Felder hinzufügen, wächst die Tabelle extrem an. Die andere Möglichkeit bieten Flexforms. Dadurch werden alle Felder als XML-Struktur in dem Feld pi_flexform der tt_content-Tabelle gespeichert. Die Definition, wie die Felder angelegt werden – im Grunde handelt es sich um eine XML-Version der TCA –, wird als Flexform bezeichnet – für flexible Forms. Die XML-Struktur, die in der Datenbank abgespeichert wird und die Daten enthält, die durch die Flexform eingegeben wurden, nennt man DS bzw. Datenstruktur. Die DS kann von dem Plug-in wieder ausgelesen werden.

4.6.1

Erzeugen der Flexform

Um eine Flexform verwenden zu können, muss sie erst einmal aktiviert werden. Das geschieht in der Datei ext_tables.php. Fügen Sie folgende Zeilen am Ende der Datei, vor dem schließenden PHP-Tag, ein. Listing 4.48 Flexform aktivieren $TCA['tt_content']['types']['list']['subtypes']['subtypes_addlist'] [$_EXTKEY.'_pi1'] = 'pi_flexform'; t3lib_extMgm::addPiFlexFormValue($_EXTKEY.'_pi1', 'FILE:EXT:'.$_EXTKEY.'/flexform_ds_pi1.xml');

Um die Flexform zu erstellen, können Sie sich am TCA orientieren, da der Aufbau der Felder im Grunde gleich ist. Listing 4.49 Input-Feld im TCA 'author' => array ( 'exclude' => 0, 'label' => 'LLL:EXT:elublog/locallang_db.xml:tx_elublog_blogentrys.author', 'config' => array ( 'type' => 'input', 'size' => '30', ) ),

Listing 4.50 Input-Feld als Flexform

LLL:EXT:elublog/locallang.xml:label.author

input 30



Die Grundstruktur der Flexform sieht folgendermaßen aus:

126

4.6 Flexforms Listing 4.51 Grundstruktur

array

< … Elemete … >



Der Node ist ein Platzhalter für den Namen des Elements. Im Beispiel des Elements aus Listing 4.50 wäre das . Es ist möglich, die Elemente auf verschiedenen Tabs aufzuteilen: Listing 4.52 Struktur mit Tabs



Sheet 1

array

< … Elemente … >



Sheet 2

array

< … Elemente … >



Abbildung 4.9 Tabs im Flexform von tt_news

127

4 Frontend-Plug-ins Erstellen Sie für die Flexform die Datei flexform_ds_pi1.xml im Extension-Verzeichnis. Listing 4.53 flexform_ds_pi1.xml

array

LLL:EXT:elublog/locallang.xml:fflabel.blogid

select

LLL:EXT:elublog/locallang.xml:fflabel.allBlogs 0

tx_elublog_blogs



LLL:EXT:elublog/locallang.xml:fflabel.listPage

group db pages 1 0 1 1 1



LLL:EXT:elublog/locallang.xml:fflabel.singlePage

group db pages 1 0 1 1 1



LLL:EXT:elublog/locallang.xml:fflabel.entrysperpage

input 20 40 int 1



128

4.6 Flexforms

LLL:EXT:elublog/locallang.xml:fflabel.teaserCrop

input 20 40 trim 1



LLL:EXT:elublog/locallang.xml:fflabel.fullTextInList

check





Die Labels werden wieder aus einer Übersetzungsdatei gefüllt. Öffnen Sie die Datei locallang.xml, und fügen Sie folgende Zeilen in die entsprechenden Übersetzungen ein. Listing 4.54 Übersetzung default Crop Teasertext after ...

Listing 4.55 Übersetzung Deutsch Angezeigter Blog Alle Blogs Seite für 'zurück' in Einzelansicht (leer für aktuelle Seite) Seite für Einzelansicht (leer für aktuelle Seite) Beiträge pro Seite in Listenansicht (0 für alle Teasertext nach ... Zeichen abschneiden

In Abbildung 4.10 sehen Sie das Ergebnis.

129

4 Frontend-Plug-ins

Abbildung 4.10 Flexform-Plug-in-Konfiguration

Nun müssen die Werte umgesetzt werden. In der Listenansicht sind alle Einstellungen bis auf „Seite für ‚zurück’ in Einzelansicht“ von Bedeutung. Um auf den Wert eines FlexForm-Feldes zuzugreifen, wird die Funktion pi_getFFvalue() verwendet. Listing 4.56 Zugriff auf die Flexform $var = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'element','sDEF');

Das Wort element entspricht dabei dem Namen des Feldes. In der Funktion $this-> listView() wird bestimmt, welcher Blog abgefragt wird, wie viele Beiträge pro Seite an-

gezeigt werden und auf welche Seite für die Einzelansicht verlinkt wird. Listing 4.57 Parameter bestimmen $entrysperpage = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'entrysperpage','sDEF'); $entrysperpage ($entrysperpage > 0) ? $entrysperpage : $this-> conf['listView.']['entrysPerPage']; $entrysperpage ($entrysperpage > 0) ? $entrysperpage : 10; $bid = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'bid', 'sDEF'); $bidmm = ($bid > 0) ? 'AND tx_elublog_blogentrys.bid='.$bid : ''; $bid = ($bid > 0) ? 'AND bid='.$bid : ''; $singleViewPID = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'singlePage','sDEF'); $singleViewPID = ($singleViewPID) ? $singleViewPID : $this-> conf['singleViewPID']; $this->singleViewPID = ($singleViewPID) ? $singleViewPID : 0;

Wenn in der Plug-in-Konfiguration kein Wert für die Beiträge pro Seite eingegeben wurde, wird der Wert aus der TypoScript-Konfiguration übernommen. Ist auch dieser leer, wird der Wert 10 verwendet. Genauso verhält es sich bei der Seiten-ID der Einzelansicht.

130

4.6 Flexforms Die Blog-ID (bid) der Plug-in-Konfiguration liefert die ID des darzustellenden Blogs. Diese wird in eine Erweiterung der WHERE-Klausel eingebaut, die bei der SQL-Abfrage nach den Blogbeiträgen angehängt wird. Diese Klausel sorgt dafür, dass nur Beiträge des gewählten Blogs selektiert werden. Wird der Wert 0 (alle Blogs) geliefert, wird nichts angehängt und die SQL-Abfrage demnach auch nicht auf einen Blog eingeschränkt. Damit der Wert in $entrysperpage verwendet wird, müssen wir folgende Änderung in derselben Funktion durchführen: Listing 4.58 Entrys per Page setzen // Suchen Sie folgende Zeile: $this->internal['results_at_a_time']=t3lib_div::intInRange($lConf ['results_at_a_time'],0,1000,3);

Die „3“ am Ende der Zeile ersetzen Sie mit $entrysperpage. Um die Blog-ID an die SQL-Abfrage zu übergeben, modifizieren Sie folgende Zeilen: Listing 4.59 Blog-ID setzen $res = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query('count (tx_elublog_blogentrys.uid)','tx_elublog_blogentrys','tx_elublog_blogentr ys_category_mm','tx_elublog_blogcategorys',$bidmm.' AND tx_elublog_blogentrys_category_mm.uid_foreign='.$this->piVars ['showCat']); $res = $this->pi_exec_query('tx_elublog_blogentrys',1,$bid); $res = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query('tx_elublog_blogentrys .*','tx_elublog_blogentrys','tx_elublog_blogentrys_category_mm','tx_elubl og_blogcategorys',$bidmm.' AND tx_elublog_blogentrys_category_mm .uid_foreign='.$this->piVars['showCat']); $res = $this->pi_exec_query('tx_elublog_blogentrys',0,$bid);

Fügen Sie jeweils die fett geschriebenen Teile hinzu. In der Funktion $this->getFieldContent() ändern Sie die Titelverarbeitung: Listing 4.60 Titel mit SingleViewPID verlinken case "title": return $this->pi_list_linkSingle($this->internal['currentRow'] [$fN],$this->internal['currentRow']['uid'],1,'','',$this->singleViewPID); break;

Um den „Zurück“-Link in der Einzelansicht mit der Seite für die Listenansicht zu verknüpfen, fügen Sie in die Funktion $this->singleView() folgende Zeilen am Anfang ein: Listing 4.61 Konfiguration auslesen $listViewPID = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'listPage','sDEF'); $listViewPID = ($listViewPID) ? $listViewPID : $this->conf ['listViewPID']; $listViewPID = ($listViewPID) ? $listViewPID : 0;

131

4 Frontend-Plug-ins Ist weder im Plug-in noch im TypoScript eine Seite für die Listenansicht definiert, wird 0 zurückgegeben. Das entspricht der aktuellen Seite. Auch der Marker für den Link muss angepasst werden. Listing 4.62 „Zurück“-Marker anpassen $markerArray['###BACKLINK###'] = $cObj->stdWrap($this-> pi_linkToPage($this->pi_getLL('back','Back'),$listViewPID),$this-> conf['singleView.']['backLink_stdWrap.']);

Ob in der Listenansicht der Teasertext beschnitten wird, wird ebenso wie die Anzeige des kompletten Blogbeitrags in der Funktion $this->pi_list_row() abgefragt. Folgende Zeilen gehören an den Anfang der Funktion: Listing 4.63 Die Konfiguration einlesen $tcrop = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'teaserCrop','sDEF'); $fullTextInList = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'fullTextInList','sDEF');

Dadurch ist eine Änderung der Marker-Zuweisung nötig. Listing 4.64 Marker neu zuweisen $markerArray['###BODYTEXT###'] = (($this->getFieldContent ('bodytextinlist') == 1) || ($fullTextInList == 1)) ? $cObj->stdWrap ($this->getFieldContent('bodytext'),$this->conf['listView.'] ['bodytext_stdWrap']) : ''; … $markerArray['###TEASERTEXT###'] = $cObj->stdWrap($this->cObj>crop($this->getFieldContent('teasertext'),$tcrop),$this>conf['listView.']['teasertext_stdWrap.']);

Damit ist das Plug-in fertig für den Einsatz.

4.7

Das Plug-in pi2 Nun sollten Sie in der Lage sein, das Plug-in selbst zu erstellen. Ein paar Hinweise und Anregungen. Das Plug-in soll folgende Module bereitstellen können:

„ „ „ „

Eine Liste aller Kategorien Eine Liste aller Tags Die letzten Kommentare Die Blogroll

Verwenden Sie die Plug-in-Konfiguration, um festzulegen, welches Modul dargestellt werden soll und von welchem Blog. Werten Sie die Modulwahl mit switch/case aus, und erstellen Sie für jedes Modul eine eigene Funktion. Alle Module lassen sich mit den eben gelernten Standard-Datenbankabfragen erstellen.

132

5 5

Dokumentation und Abschluß

Jede Extension sollte ausreichend dokumentiert sein. Das betrifft nicht nur die ExtensionDokumentation, die genau erklären muss, wie die Extension verwendet wird, sondern auch den Code, der auch für andere Entwickler nachvollziehbar sein soll.

5.1

Code dokumentieren mit extdeveval Die Extension extdeveval bringt viele nützliche Funktionen für Extension-Entwickler mit. Dazu gehört auch eine Hilfe für die Code-Dokumentation nach dem phpDoc-Format. Nach der Installation finden Sie ein neues Modul bei den Admin-Werkzeugen:

Abbildung 5.1 extdeveval im Menü

Wählen Sie im Funktionsmenü des Moduls PHP script documentation help. Sie werden nun aufgefordert, eine Extension – in diesem Fall elublog – zu wählen. Dann erhalten Sie eine Liste aller Dateien. Nun können Sie alle relevanten Dateien, wie Plug-in-Klassendateien und Moduldateien, durchgehen. Die Änderungen, die ausgeführt werden, werden anhand verschiedener Farben dargestellt. Es wird ein Inhaltsverzeichnis aller Funktionen angelegt. Jede Funktion erhält einen Kommentarkopf mit einer Dummy-Beschreibung aller Parameter und des Rückgabewertes. Die korrekte Beschreibung muss nur noch hinzugefügt werden. Wenn Sie später Änderungen hinzufügen, führen Sie die Funktion einfach wieder aus, um den Funktionsindex zu aktualisieren. Durch die Zeilenangabe findet man gerade in großen Extensions leichter das Gesuchte. Die Beschreibung der Parameter und Rückgabewerte ist wichtig, um nicht erst die ganze Funktion zerpflücken zu müssen, um herauszufinden, was die Funktion erwartet und zurückgibt.

133

5 Dokumentation und Abschluß Listing 5.1 Funktionsübersicht der Datei mod2/index.php /** * [CLASS/FUNCTION INDEX of SCRIPT] * * * * 59: class tx_elublog_module2 extends t3lib_SCbase * 67: function init() * 84: function menuConfig() * 100: function main() * 125: function jumpToUrl(URL) * 168: function printContent() * 179: function moduleContent() * 196: function getNewPostButton() * 213: function getPostList() * 273: function getFunctions($table,$row) * * TOTAL FUNCTIONS: 9 * (This index is automatically created/updated by the extension "extdeveval") * */

Innerhalb der Funktion sollten Sie ebenfalls Kommentare hinterlassen, um bestimmte Abschnitte zu dokumentieren.

5.2

Extension-Dokumentation schreiben Auch wenn man gerade Männern nachsagt, nie Handbücher zu lesen, so ist eine ausführliche Dokumentation unerlässlich. Sie zeigt dem Leser nicht nur auf, wie eine Extension funktioniert, sondern auch, welche Möglichkeiten der Konfiguration zur Verfügung stehen. Nicht wenige Integratoren lehnen es ab, Extensions ohne Dokumentation zu installieren. Verwenden Sie also auf die Dokumentation dieselbe Sorgfalt wie auf die Programmierung.

Abbildung 5.2 Extensions ohne swx-Datei im /doc-Verzeichnis erzeugen einen Render-Fehler im TER.

134

5.2 Extension-Dokumentation schreiben

5.2.1

Die Vorlage

Die Dokumentation wird nicht nur in der Extension mitgeliefert, sondern auch online verfügbar gemacht. Dazu rendert das TER die Dokumentation. Nach dem Upload einer Extension steht sie nach kurzer Zeit automatisch unter http://typo3.org/documentation/document-library/extension-manuals und bei den einzelnen Extensions unter http://typo3.org/extensions/repository zur Verfügung. Aus diesem Grund muss die Dokumentation aus einer eigenen Vorlage heraus erstellt werden. Um diese Vorlage nutzen zu können, benötigen Sie OpenOffice1. Laden Sie die Vorlage von http://typo3.org/documentation/document-library/core-documentation/doc_template/current/ herunter, öffnen Sie sie in OpenOffice, und speichern Sie sie als Vorlage ab. Dokumentationen werden im /doc-Verzeichnis der Extension als OpenOffice 1.0-Dokument (.swx) abgelegt.

5.2.2

Der Aufbau der Dokumentation

Die Dokumentation hat einen vorgegebenen Aufbau, von dem nicht abgewichen werden sollte. Sie sollten nicht benötigte Abschnitte aber entfernen. Das Dokument gliedert sich in folgende Abschnitte:

„ Inhaltsangabe „ Introduction: Eine kurze Erklärung, was die Extension macht. Hier können auch Screenshots, beispielsweise von der Frontend-Ausgabe, hinterlegt werden.

„ Users Manual: Erklärt, wie die Extension vom Integrator und von den FrontendBenutzern verwendet wird.

„ Administration: Erklärt Installation, Systemvoraussetzungen und Wartung für den Administrator, einschließlich der Extension-Konfiguration über den Extension-Manager.

„ Configuration: In diesem Abschnitt wird die Konfiguration erklärt. Dazu gehört eine vollständige Auflistung aller TypoScript-Eigenschaften und der Plug-in-Konfiguration.

„ Tutorial: Eine kurze und einfache Einführung in die Extension. Am besten anhand eines Beispiels.

„ Known Problems: Probleme und Inkompatibilitäten, die dem Autor bekannt sind. „ To-Do list: Eine optionale Liste der Aufgaben und Features, die noch offen sind und in einer der späteren Versionen gelöst sein sollen.

„ Changelog: Dieser Abschnitt enthält die Änderungen an der Extension, sollte aber nicht mehr verwendet werden. Verwenden Sie für das Changelog stattdessen die Datei Change Log im Extension-Verzeichnis. Weisen Sie in der Dokumentation nur auf diese Datei hin. 1

http://www.openoffice.org

135

5 Dokumentation und Abschluß Nicht jede Extension benötigt alle diese Abschnitte. Wird ein Abschnitt nicht benötigt, löschen Sie ihn. Das sorgt wiederum für Übersicht in der Online-Dokumentation.

5.2.3

Vorlagenstile

Das TER rendert die Vorlage auf Basis der eingesetzten Stile. Dazu wurde ein Satz eigener Stile erstellt, der zu verwenden ist. Alle anderen Stile werden beim Rendern ignoriert. Tabelle 5.1 Stile in der Vorlagendatei Stil

Verwendung

Text body

Der Standardstil. Dieser kommt im normalen Text zum Tragen.

Table heading

Wird für Tabellenüberschriften verwendet

Table contents

Stil für den Tabelleninhalt

Preformatted text

Codebeispiele, TypoScript u.Ä. werden mit diesem Stil ausgezeichnet.

Heading 1

Überschriften für die Hauptabschnitte. Das TER rendert für jeden Hauptabschnitt eine neue HTML-Seite.

Heading 2

Für die Abschnitte innerhalb der Hauptabschnitte

Heading 3

Weitere Untergliederung

Heading 4

Letzte Ebene der Untergliederung

Source text

Eingefügte Codefragmente werden in diesem Stil dargestellt.

Die Verwendung von Bildern in der Dokumentation erfordert, dass die Bilder in das Dokument eingefügt werden und nicht nur darauf verwiesen wird. Dazu wählen Sie im Menü Bearbeiten > Inhalte einfügen. In dem folgenden Dialog wählen Sie Bitmap.

5.2.4

Die Dokumentation verfassen

Wenn Sie sich schließlich an das Schreiben der Dokumentation machen, behalten Sie immer im Hinterkopf, dass nicht alle Leser denselben Wissensstand wie Sie selbst haben. Gerade Anfänger sind schnell frustriert, wenn Wissen vorausgesetzt wird, das ein Anfänger noch nicht haben kann. Sie müssen keinen ausgewachsenen Roman schreiben, aber wenn Sie unsicher sind, ob Sie ein bestimmtes Detail beschreiben sollen oder nicht, sollten Sie es lieber tun. Auch Screenshots sollten nicht fehlen. Versuchen Sie, die Extension allgemeinverständlich zu beschreiben.

136

5.3 Upload ins TER

5.3

Upload ins TER Ist die Extension fertig und dokumentiert, wird es Zeit, sie in TER hochzuladen. Klicken Sie im Extension-Manager auf Ihre Extension, und wählen Sie im Extension-Menü Upload to TER. Im folgenden Dialog sollten Sie eine kurze Zusammenfassung der Änderungen formulieren. Hier wird auch die Versionsnummer gesetzt. Wenn Ihre Extension genau das tut, was Sie von ihr erwarten, scheuen Sie sich nicht, eine Hauptversion 1.0.0. zu setzen. Sollten Bugs auftauchen (was bei praktisch jeder größeren Software der Fall ist), können Sie die Versionsnummer immer noch als Bug-Fix-Version anheben.

Abbildung 5.3 Upload ins TER

Nach dem Upload wird die Extension im TER zur Verfügung gestellt und taucht im Extension-Manager auf, sofern die Extension-Liste upgedatet wurde. Bis die Dokumentation online erscheint, kann es aber ein wenig dauern. Gratulation, Ihre Extension ist fertig!

137

II Teil II – Funktionsreferenz zur klassischen Extensionprogrammierung

6 6

Datenbank

6.1 $GLOBALS['TYPO3_DB'] Insbesondere beim Zugriff auf Datenbanken ist darauf zu achten, dass ausschließlich die TYPO3-eigenen Datenbank-Handler verwendet werden. Die Klasse t3lib_DB bildet hierfür das sogenannte Datenbank-Abstraktions-Layer (DBAL). Sie ist sowohl im Backend als auch im Frontend in $GLOBALS['TYPO3_DB'] instanziiert.

6.1.1

exec_SELECTquery

$GLOBALS['TYPO3_DB']->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='');

Diese Methode führt eine Select-Abfrage aus und gibt ein Datenbankobjekt zurück. Das Objekt kann dann mittels sql_fetch_assoc oder sql_fetch_row in ein assoziatives Array umgewandelt werden (nicht mysql_fetch_assoc). Listing 6.1 Abfrage aller Frontend-User, die gerade angemeldet sind $selectRes = $GLOBALS['TYPO3_DB']->exec_SELECTquery('username', 'fe_users', 'disable = 0 AND is_online = 1');

6.1.2

exec_SELECT_queryArray

$GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryParts);

Dies ist eine Abwandlung von exec_SELECTquery, durch die gute Lesbarkeit vor allem für komplexere Abfragen sehr zu empfehlen.

141

6 Datenbank Listing 6.2 Abfrage aller Frontend-User, die gerade angemeldet sind $selectRes = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray(array( 'SELECT' => 'username', 'FROM' => 'fe_users', 'WHERE' => 'disable = 0 AND is_online = 1', 'GROUPBY' => '', 'ORDERBY' => '', 'LIMIT' => '' ));

6.1.3

exec_SELECTgetRows

$GLOBALS['TYPO3_DB']->exec_SELECTgetRows($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='', $uidIndexField='');

Führt eine Select-Abfrage aus und gibt ein assoziatives Array mit allen Ergebnissen zurück. Sehr interessant ist vor allem der letzte Parameter $uidIndexField. Falls die Abfrage ein eindeutiges Feld enthält wie z.B. uid, so werden die Keys des ausgegebenen Arrays mit diesem Feld gefüllt. Listing 6.3 Abfrage aller Frontend-User, die gerade angemeldet sind $selectRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, username', 'fe_users', 'disable = 0 AND is_online = 1', '', '', '','uid');

6.1.4

exec_SELECT_mm_query

$GLOBALS['TYPO3_DB']->exec_SELECT_mm_query($select, $local_table, $mm_table, $foreign_table, $whereClause='', $groupBy='', $orderBy='', $limit='');

Dient zur Abfrage von n-n-Beziehungen zwischen $local_table und $foreign_table. Verknüpft sind die beiden Tabellen über $mm_table. Zu beachten ist vor allem bei gleichnamigen Feldern in $local_table und $foreign_table wie etwa uid oder pid, dass hierfür entsprechende Aliasse in $select vergeben werden. Listing 6.4 Beispiel $selectRes = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query( 'product, name', 'tx_extkey_products', 'tx_extkey_products_category_mm', 'tx_extkey_categories'); while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($selectRes)) { $content.= $row['product'].':'.$row['name'].'
'; } /* Ergebnis in $content: Kaffee:Arabica Kaffee:Robusta */

142

6.1 $GLOBALS['TYPO3_DB']

6.1.5

exec_INSERTquery

$GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fields_values, $no_quote_fields=FALSE);

Fügt einen Datensatz in die Tabelle $table mit den im Array $field_values definierten Werten ein. Listing 6.5 Beispiel $GLOBALS['TYPO3_DB']->exec_INSERTquery('fe_users', array( 'username' => 'mickey', 'password' => 'start123', 'usergroup' => '4', 'name' => 'Mickey Maus', 'tstamp' => time() ));

6.1.6

exec_UPDATEquery

$GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields=FALSE);

Führt ein SQL Update auf $table aus. Die Bedingungen werden in $where definiert und die zu aktualisierenden Felder im Array $fields_values. Listing 6.6 Beispiel $GLOBALS['TYPO3_DB']->exec_UPDATEquery('fe_users', 'username = \'mickey\'', array('disable' => '1'));

6.1.7

exec_DELETEquery

$GLOBALS['TYPO3_DB']->exec_DELETEquery($table, $where);

Führt ein SQL Delete auf $table aus. Die Bedingungen werden in $where definiert. Listing 6.7 Beispiel $GLOBALS['TYPO3_DB']->exec_DELETEquery('fe_users', 'username = \'mickey\'');

6.1.8

sql_fetch_assoc

$GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);

Pendant zu mysql_fetch_assoc; um den Datenbank-Handler geschlossen im TYPO3-Core zu belassen, sollte auch unbedingt diese Methode genutzt werden.

143

6 Datenbank Listing 6.8 Beispiel: alphabetisch sortierte Liste aller User, die derzeit online sind $selectRes = $GLOBALS['TYPO3_DB']->exec_SELECTquery('username', 'fe_users', 'disable = 0 AND is_online = 1', '', 'username'); $usersOnline = array(); while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($selectRes)) { $usersOnline[] = $row['username']; }

6.1.9

sql_fetch_row

$GLOBALS['TYPO3_DB']->sql_fetch_row($res);

Siehe mysql_fetch_assoc, Pendant zu mysql_fetch_row.

6.1.10

searchQuery

$GLOBALS['TYPO3_DB']->searchQuery($searchWords,$fields,$table);

Erzeugt eine SQL-Abfragebedingung, um in mehreren Datenbankfeldern $fields nach den Suchbegriffen $searchWords zu suchen. Listing 6.9 Suche in den Feldern „username“ und „name“ nach „peter“ und „muster“ $whereClause = $GLOBALS['TYPO3_DB']->searchQuery(array('peter', 'muster'),array('name', 'username'), 'fe_users'); /* Ergebnis in $whereClause: (fe_users.name LIKE '%peter%' OR fe_users.username LIKE '%peter%') AND (fe_users.name LIKE '%muster%' OR fe_users.username LIKE '%muster%') */

6.1.11

listQuery

$GLOBALS['TYPO3_DB']->listQuery($field, $value, $table);

Erzeugt eine SQL-Abfragebedingung, um in kommaseparierten Listen nach einem bestimmten Wert $value zu suchen. Die kommaseaprierte Liste steht hierbei im Feld $field in der Tabelle $table. Listing 6.10 Suche im Listenfeld „Hersteller“ nach der Herstellernummer „25“ $whereClause = $GLOBALS['TYPO3_DB']->listQuery('hersteller', '25', 'tx_t3ref_produkte'); /* Ergebnis in $whereClause:

144

6.1 $GLOBALS['TYPO3_DB'] (hersteller LIKE '%,25,%' OR hersteller LIKE '25,%' OR hersteller LIKE '%,25' OR hersteller='25') */

6.1.12

splitGroupOrderLimit

$GLOBALS['TYPO3_DB']->splitGroupOrderLimit($str);

Zerlegt eine SQL-Abfrage in ein Array, optimal für exec_SELECT_queryArray. Listing 6.11 Beispiel $selectArr = $GLOBALS['TYPO3_DB']->splitGroupOrderLimit( "hersteller = '25' and lagerbestand > 0 group by menge limit 0,100"); /* Ergebnis in $selectArr: Array ( [WHERE] => hersteller = '25' and lagerbestand > 0 [GROUPBY] => menge [ORDERBY] => [LIMIT] => 0,100 ) */

6.1.13

quoteStr

$GLOBALS['TYPO3_DB']-> quoteStr($str, $table);

Ersatz der PHP-Funktion addslashes(). Listing 6.12 Beispiel mit Suchtext: Peter „PM“ Muster $quoteString = $GLOBALS['TYPO3_DB']-> quoteStr('Peter "PM" Muster', 'fe_users'); /* Ergebnis in $quoteString: Peter \"PM\" Muster */

6.1.14

fullQuoteStr

$GLOBALS['TYPO3_DB']-> fullQuoteStr($str, $table);

Wie quoteStr, jedoch mit slashes vor und hinter dem String.

145

6 Datenbank Listing 6.13 Beispiel mit Suchtext: Peter „PM“ Muster $quoteString = $GLOBALS['TYPO3_DB']-> fullQuoteStr('Peter "PM" Muster', 'fe_users'); /* Ergebnis in $quoteString: 'Peter \"PM\" Muster' */

6.1.15

fullQuoteArray

$GLOBALS['TYPO3_DB']->fullQuoteArray($arr, $table, $noQuote=FALSE);

Erweitert fullQuoteStr um die Funktionalität, ein Array abzuarbeiten. Wird Parameter $noQuote gefüllt (String oder Array), so können bestimmte Keys im Array ausgeschlossen werden, z.B. für Datenbankfelder. Listing 6.14 Beispiel mit zwei Suchtexten $quoteArray = $GLOBALS['TYPO3_DB']-> fullQuoteArray(array('Peter "PM" Muster', 'Eva "EM" Muster'), 'fe_users'); /* Ergebnis in $quoteArray: Array ( [0] => 'Peter \"PM\" Muster' [1] => 'Eva \"EM\" Muster' ) */

Listing 6.15 Beispiel mit einem Suchtext und $noQuote $quoteArray = $GLOBALS['TYPO3_DB']-> fullQuoteArray(array('select' => 'name, username', 'where' => 'Peter "PM" Muster'), 'fe_users', 'select'); /* Ergebnis in $quoteArray: Array ( [select] => name, username [where] => 'Peter \"PM\" Muster' ) */

6.1.16

escapeStrForLike

$GLOBALS['TYPO3_DB']->escapeStrForLike($str, $table);

Die Zeichen _ und % werden maskiert. Listing 6.16 Beispiel mit 100% $escapeString = $GLOBALS['TYPO3_DB']->escapeStrForLike('100%', 'tx_t3ref_bewertungen');

146

6.1 $GLOBALS['TYPO3_DB'] /* Ergebnis in $escapeString: 100\% */

6.1.17

cleanIntArray

$GLOBALS['TYPO3_DB']->cleanIntArray($arr);

Alle Werte in einem eindimensionalen Array werden in Integer-Werte umgewandelt. Listing 6.17 Beispiel $intArr = $GLOBALS['TYPO3_DB']->cleanIntArray(array(100,'69.99','5,5')); /* Ergebnis in $intArr: Array ( [0] => 100 [1] => 69 [2] => 5 ) */

6.1.18

cleanIntList

$GLOBALS['TYPO3_DB']->cleanIntList($list);

Alle Werte in einer kommaseparierten Liste werden in Integer-Werte umgewandelt. Listing 6.18 Beispiel $intList = $GLOBALS['TYPO3_DB']->cleanIntList('100,69.99,5a2'); /* Ergebnis in $intList: 100,69,5 */

6.1.19

debug_lastBuiltQuery

$GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;

Enthält die letzte erzeugte SQL-Abfrage. Hierfür muss eine der beiden folgenden Optionen gesetzt sein: $GLOBALS['TYPO3_DB']->store_lastBuiltQuery = true; oder $GLOBALS['TYPO3_DB']->debugOutput = true;

147

6 Datenbank Listing 6.19 Beispiel $debugQuery = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; /* Ergebnis in $debugQuery: SELECT uid,username FROM fe_users WHERE disable = 0 AND is_online = 1 */

6.2 tslib_pibase Standardmäßig erweitert jedes Frontend-Plug-in die Klasse tslib_pibase. Hier finden sich einige praktische Funktionen, mit denen es möglich ist, bestimmte Abfragen in wenigen Zeilen Quellcode auszuführen.

6.2.1

pi_exec_query

$this->pi_exec_query($table, $count=0, $addWhere='', $mm_cat='', $groupBy='', $orderBy='', $query='');

Führt eine SQL-Abfrage aus, primär gedacht, um Datensätze innerhalb einer Seite abzufragen. Ist $query nicht gesetzt, so wird immer nur die aktuelle pid abgefragt mit allen aktiven Elementen, je nachdem, welche Felder in der Tabelle vorhanden sind, werden auch deleted, hidden, starttime, endtime etc. mit berücksichtigt. Listing 6.20 Beispiel: Tabelle pages: $result = $this->pi_exec_query('pages', 0); while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) $items[] = array('uid' => $row['uid'], 'title' => $row['title']); /* Ergebnis in $items: Array ( [0] => Array ( [uid] => 41 [title] => subpage 1 ) [1] => Array ( [uid] => 42 [title] => subpage 2 ) ) Ausgeführte SQL-Abfrage: SELECT pages.* FROM pages WHERE pid IN (25) AND pages.deleted=0 AND pages.t3ver_statepi_exec_query('pages', 1); $item = $GLOBALS['TYPO3_DB']->sql_fetch_row($result); /* Ergebnis in $item: Array ( [0] => 2 ) Ausgeführte SQL-Abfrage: SELECT count(*) FROM pages WHERE pid IN (25) AND pages.deleted=0 AND pages.t3ver_statepi_exec_query('fe_users', 0, '', '', '' ,'', 'FROM fe_users'); while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) $items[] = array('username' => $row['username'], 'name' => $row['name']); /* Ergebnis in $item: Array [0] [1] [2] )

( => Array ( [username] => test [name] => ) => Array ( [username] => max [name] => ) => Array ( [username] => eva [name] => )

Ausgeführte SQL-Abfrage: SELECT fe_users.* FROM fe_users LIMIT 0,20 */

6.2.2

pi_getPidList

$this->pi_getPidList($pid_list, $recursive = 0)

Erzeugt eine kommaseparierte Liste von Seiten-IDs innerhalb der ebenfalls kommaseparierten Liste $pid_list. Mit einem Wert größer 0 für $recursive kann auch um diesen Wert rekursiv gesucht werden. Listing 6.23 Beispiel: Suche alle Seiten innerhalb der Page-IDs 12 und 24 $pages = $this->pi_getPidList('24,12',2); /* Ergebnis in $pages:

149

6 Datenbank 44,25,41,42,26,24,36,12 */

6.2.3

pi_getRecord

$this->pi_getRecord($table,$uid,$checkPage=0)

Es wird ein einzelner Datensatz aus Tabelle $table abgefragt, es gibt nur die Bedingung auf eine gezielte $uid. Mit $checkPage kann zusätzlich abgefragt werden, ob dieser Datensatz auf der jeweiligen Seite verfügbar ist. Listing 6.24 Beispiel $product = $this->pi_getRecord('tx_extkey_products', 1); /* Ergebnis in $products: Array ( [uid] => 1 [pid] => 6 [tstamp] => 1255874092 [crdate] => 1255874092 [cruser_id] => 1 [product] => Kaffee [category] => 2 )

6.2.4

pi_prependFieldsWithTable

$this->pi_prependFieldsWithTable($table,$fieldList)

Fügt allen Feldern in der kommaseparierten Liste $fieldList den Tabellennamen $table vorne an. Listing 6.25 Beispiel $fields = $this->pi_prependFieldsWithTable('tx_extkey_products', 'name, category, price'); /* Ergebnis in $fields: tx_extkey_products.name,tx_extkey_products.category, tx_extkey_products.price */

150

6.3 cObj

6.3 cObj

6.3.1

DBgetDelete

$this->cObj->DBgetDelete($table, $uid, $doExec=FALSE)

„Löscht“ einen Datensatz aus Tabelle $table mit $uid als Bedingung. Sofern die Tabelle das Feld „deleted“ enthält, wird der Datensatz nicht physikalisch gelöscht, sondern es wird lediglich das Feld „deleted“ auf „1“ gesetzt. Bleibt $doExec false, so gibt die Funktion die SQL-Abfrage zurück, die ausgeführt werden würde – dies dient allerdings nur zur Information. Die SQL-Abfrage sollte immer mit $doExec = true ausgeführt werden. Im Erfolgsfall wird „1“ zurückgegeben. Listing 6.26 Beispiel $delete = $this->cObj->DBgetDelete('fe_users', '1', false); /* Ergebnis in $delete: UPDATE fe_users SET deleted='1' WHERE uid=1 */ $deleted = $this->cObj->DBgetDelete('fe_users', '1', true); /* Ergebnis in $deleted: 1 */

6.3.2

DBgetInsert

$this->cObj->DBgetInsert($table, $pid, $dataArr, $fieldList, $doExec=FALSE)

Generiert einen SQL-Insert-Befehl, der alle Systemfelder wie „tstamp“, „crdate“, „cruser_id“, „fe_cruser_id“ und „fe_crgroup_id“ automatisch berücksichtigt, sofern sie in der Tabelle vorhanden sind. Es werden ausschließlich die Werte aus dem Array $dataArr in die Tabelle $table eingefügt, die in der kommaseparierten Liste $fieldList stehen. Listing 6.27 Beispiel $dataArr = array( 'username' => 'mickey', 'password' => 'start123', 'name' => 'Mickey Maus', 'usergroup' => '2',

151

6 Datenbank 'address' => 'Entenstr. 9' ); $insert = $this->cObj->DBgetInsert('fe_users', '6', $dataArr, 'username,password,name,usergroup', false); /* Ergebnis in $insert: INSERT INTO fe_users ( username, password, name, usergroup, tstamp, crdate, cruser_id, fe_cruser_id, pid ) VALUES ( 'mickey', 'start123', 'Mickey Maus', '2', '1255884274', '1255884274', '0', '0', '6' ) */ $inserted = $this->cObj->DBgetInsert('fe_users', '6', $dataArr, 'username,password,name,usergroup', true); /* Ergebnis in $inserted: 1 */

6.3.3

DBgetUpdate

$this->cObj->DBgetUpdate($table, $uid, $dataArr, $fieldList, $doExec=FALSE)

Generiert einen SQL-Update-Befehl, der das Feld „tstamp“ automatisch berücksichtigt, sofern es vorhanden ist. Zum Ausführen muss $doExec = true gesetzt werden. Listing 6.28 Beispiel $dataArr = array( 'address' => 'Entenstr. 9' ); $update = $this->cObj->DBgetUpdate('fe_users', '11', $dataArr, 'address', false) /* Ergebnis in $update: UPDATE fe_users SET address='Entenstr. 9', tstamp='1255884909' WHERE uid=11 */ $updated = $this->cObj->DBgetUpdate('fe_users', '11', $dataArr, 'address', true) /* Ergebnis in $updated: 1 */

152

6.3 cObj

6.3.4

enableFields

$this->cObj->enableFields($table,$show_hidden=0)

Erzeugt die für SQL-Abfragen benötigten Bedingungen, um nur die aktuell „aktiven“ Datensätze zu bekommen. Es werden nur Bedingungen erzeugt für Systemfelder, die in der abgefragten Tabelle vorhanden sind. Listing 6.29 Beispiele $whereCategories = $this->cObj->enableFields('tx_extkey_categories'); /* Ergebnis in $whereCategories: AND tx_extkey_categories.deleted=0 */ $whereTTContent = $this->cObj->enableFields('tt_content', 1); /* Ergebnis in $whereTTContent; AND tt_content.deleted=0 AND tt_content.t3ver_state [1] => [2] => [3] => [4] => [5] => )

flags scripts struktur styles templates user_upload

*/

7.1.5

getAllFilesAndFoldersInPath

t3lib_div::getAllFilesAndFoldersInPath(array $fileArr,$path,$extList='', $regDirs=0,$recursivityLevels=99,$excludePattern='')

Ermittelt innerhalb von $path alle Dateien und Verzeichnisse. Über das Array $fileArr können bereits zuvor ausgelesene Verzeichnisse dem Ergebnis vorangestellt, über die kommaseparierte Liste $extList bestimmte Dateierweiterungen gefiltert werden. $regDirs steuert, ob im Ergebnis-Array auch die Verzeichnisnamen separat aufgeführt werden sollen. Wie viele Ebenen rekursiv ausgelesen werden sollen, steuert $recursivityLevels. Mit Hilfe eines regulären Ausdrucks in $excludePattern können bestimmte Dateien oder Verzeichnisse ausgeschlossen werden. Listing 7.5 Beispiel $files = t3lib_div::getAllFilesAndFoldersInPath( array(), 'fileadmin/', '', 1 ); /* Auszug aus $files: Array ( [0] => fileadmin/ [65a5bd5b6c270c9628c3e90ca979996b] [1] => fileadmin/flags/ [472a2f8cda5b585bb8cb64b349e1f61c] [5cd2cbdc135d3c3a90f65b756877db77] [814da70ac0d558d15295303e3f7e249b] [6f7e898514e7baa8558a535608fbe699] [b6d02f3ee6b3ceb12644cb022bf7b444] [...] => ... )

=> fileadmin/logfile.log => => => => =>

fileadmin/flags/ar.gif fileadmin/flags/ar.png fileadmin/flags/ar_d.gif fileadmin/flags/bg.gif fileadmin/flags/bg.png

Der Array-Key ist der jeweilige md5-hash der Datei. */

157

7 Dateisystem

7.1.6

getFileAbsFileName

t3lib_div::getFileAbsFileName($filename, $onlyRelative=TRUE,$relToTYPO3_mainDir=FALSE)

Gibt den absoluten Dateipfad zur Datei in $filename an, wobei $filename auch ein Verzeichnis sein kann. $onlyRelative steuert, ob nur Dateien innerhalb des aktuellen Seitenpfades berücksichtigt werden. Standardmäßig bezieht sich der relative Dateipfad in $filename auf den aktuellen Seitenpfad, alternativ kann sich der Pfad mit $relToTYPO3_mainDir = true auf das TYPO3Core-Verzeichnis beziehen. Listing 7.6 Beispiel $file = t3lib_div::getFileAbsFileName('fileadmin/logfile.log'); /* Ergebnis in $file: /srv/www/domain.com/htdocs/fileadmin/logfile.log */

7.1.7

getFilesInDir

t3lib_div::getFilesInDir($path,$extensionList='',$prependPath=0, $order='',$excludePattern='')

Liest alle Dateien innerhalb des Verzeichnisses $path ein. $extensionList ist eine optionale kommaseparierte Liste, um nur bestimmte Dateierweiterungen einzulesen. Wird $prependPath = 1 gesetzt, so wird allen Dateinamen der Pfad vorangestellt. Mit $order = 1 wird die Dateiliste alphabetisch sortiert, $order = 'mtime' nach dem Aktualisierungsdatum. Listing 7.7 Beispiel $files = t3lib_div::getFilesInDir('fileadmin/flags/', 'png', 0, 1); /* Ergebnis in $files: Array ( [5cd2cbdc135d3c3a90f65b756877db77] [b6d02f3ee6b3ceb12644cb022bf7b444] [df58e1c2dd9a87f2b86b33b0e2cfe688] [f0aa7d05b27a5a85716c911f7e34556c] [...] => ... ) */

158

=> => => =>

ar.png bg.png bs.png ca.png

7.1 t3lib_div

7.1.8

getURL

t3lib_div::getURL($url, $includeHeader = 0, $requestHeaders = false, &$report = NULL)

Liest eine Datei oder URL aus und gibt den Dateiinhalt zurück. Optional wird der httpHeader mit ausgelesen ($includeHeader = 1). Auch der http-Header der Anfrage kann mittels $requestHeaders angepasst werden. Die Funktion nutzt cURL, setzt also ein installiertes cURL-Paket voraus. Listing 7.8 Beispiel $rss = t3lib_div::getURL('http://snippets.typo3.org/snippets.xml'); /* Ergebnis in $rss:



fileadmin/file1.csv [1] => fileadmin/ext/file2.csv ) */

7.1.14 resolveBackPath t3lib_div::resolveBackPath($pathStr)

Löst überflüssige relative Rücksprünge im Pfad $pathStr auf. Listing 7.14 Beispiel $path = t3lib_div::resolveBackPath( 'fileadmin/first/path/../../second/path/'); /* Ergebnis in $path: fileadmin/second/path/ */

7.1.15 rmdir t3lib_div::rmdir($path,$removeNonEmpty=false)

Erweitert die PHP-Funktion rmdir um die Möglichkeit, rekursiv Verzeichnisse löschen zu können inklusive aller enthaltenen Dateien. Im Erfolgsfall gibt die Funktion true zurück. $path muss absolut sein. Listing 7.15 Beispiel $deleted = t3lib_div::rmdir('/srv/www/domain.com/htdocs/fileadmin/ext/', true);

161

7 Dateisystem

7.1.16 split_fileref t3lib_div::split_fileref($fileref)

Bricht den Pfad zu einer Datei in Pfad, Datei, Dateiname und Erweiterung auf. Listing 7.16 Beispiel $filerefParts = t3lib_div::split_fileref('fileadmin/logfile.log'); /* Ergebnis in $filerefParts: Array ( [path] => fileadmin/ [file] => logfile.log [filebody] => logfile [fileext] => log ) */

7.1.17 tempnam t3lib_div::tempnam($filePrefix)

Ersetzt die PHP-Funktion tempnam(). Alle Dateinamen werden innerhalb des Verzeichnisses typo3temp erzeugt. Listing 7.17 Beispiel $tmpfile = t3lib_div::tempnam('ext'); /* Ergebnis in $tmpfile: /path/to/typo3/htdocs/typo3temp/ext076.tmp */

7.1.18 unlink_tempfile t3lib_div::unlink_tempfile($uploadedTempFileName)

Löscht die temporäre Datei $uploadedTempFileName im Verzeichnis „typo3temp/“ und wird üblicherweise nach der Funktion t3lib_div::upload_to_tempfile() aufgerufen. Es können nur Dateien innerhalb des Verzeichnis „typo3temp/“ gelöscht werden.

7.1.19 upload_copy_move t3lib_div::upload_copy_move($source,$destination)

162

7.1 t3lib_div Verschiebt eine hochgeladene Datei in den absoluten Pfad $destination. Wird als $source keine hochgeladene Datei angegeben, wird eine Kopie der Datei erstellt. Listing 7.18 Beispiel $source = $_FILES['uploadedfile']['tmp_name']; $destination = t3lib_div::getFileAbsFileName('uploads'). '/'.$this->extKey.'/'. str_replace(" ","_",$_FILES['uploadedfile']['name']); $uploaded = t3lib_div::upload_copy_move($source,$destination); /* Ergebnis in $uploaded: true */

7.1.20 upload_to_tempfile t3lib_div::upload_to_tempfile($uploadedFileName)

Verschiebt eine hochgeladene Datei ins Verzeichnis typo3temp mit temporärem Dateinamen. Der Rückgabewert enthält im Erfolgsfall den Dateinamen. Listing 7.19 Beispiel $source = $_FILES['uploadedfile']['tmp_name']; $filename = t3lib_div::upload_to_tempfile($source); /* Ergebnis in $filename: /srv/www/domain.tld/htdocs/typo3temp/upl1D5.tmp */

7.1.21 verifyFilenameAgainstDenyPattern t3lib_div::verifyFilenameAgainstDenyPattern($filename)

Überprüft den übergebenen Dateinamen gegen die Konfiguration in $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern']

und gibt false zurück, sobald der Dateiname übereinstimmt.

163

7 Dateisystem

7.1.22 writeFile t3lib_div::writeFile($file,$content)

Schreibt den String $content in die Datei $file. Existiert $file bereits, so wird die Datei überschrieben. Listing 7.20 Beispiel $written = t3lib_div::writeFile('fileadmin/data.txt','hello world'); /* Ergebnis in $written: true */

7.1.23 writeFileToTypo3tempDir t3lib_div::writeFileToTypo3tempDir($filepath,$content)

Schreibt $content in eine Datei innerhalb des Verzeichnisses typo3temp. $filepath muss den absoluten Pfad enthalten, es werden auch Verzeichnisse unterhalb von typo3temp unterstützt. Im Erfolgsfall gibt die Funktion false zurück. Listing 7.21 Beispiel $written = t3lib_div::writeFileToTypo3tempDir( PATH_site.'typo3temp/data.txt','hello world' ); /* Ergebnis in $written: false */

164

8 8

8.1

Strings, Arrays und Umgebungsvariablen

Allgemeine Funktionen

8.1.1

_GET

t3lib_div::_GET($var=NULL)

Gibt alle Werte des globalen GET-Arrays zurück. Da die Werte normalisiert und alle Escape-Zeichen entfernt werden, sollte jeder Zugriff auf GET-Variablen über diese Methode erfolgen. Wird $var gesetzt, erfolgt als Rückgabe nur der Wert von $var. Listing 8.1 Aufruf einer URL mit „&showArticle=54“ $articleID = t3lib_div::_GET('showArticel'); $getVars = t3lib_div::_GET(); /* Ergebnis in $articleID: 54 Ergebnis in $getVars: Array ( [id] => 40 [showArticle] => 54 ) */

8.1.2

_GETset

t3lib_div::_GETset($inputGet,$key='')

Schreibt Eingabe Werte in $_GET

165

8 Strings, Arrays und Umgebungsvariablen Wird $key nicht gesetzt, so wird das gesamte Array $_GET überschrieben! Mit definiertem $key wird nur dieser Bereich neu geschrieben. Listing 8.2 Beispiel t3lib_div::_GETset(array("name" => "mickey maus"));

8.1.3

_GP

t3lib_div::_GP($var,$strip=0)

Gibt den Wert von $var zurück, wobei $var sowohl eine GET- als auch eine POSTVariable sein kann. Sollten beide Variablen vorhanden sein, so wird die POST-Variable zurückgegeben. Mit $strip=1 werden nur Arrays normalisiert. Listing 8.3 Beispiel $name = t3lib_div::_GP('name'); /* Ergebnis in $name: mickey maus */

8.1.4

_POST

t3lib_div::_POST($var=NULL)

Gleiche Funktionalität wie _GET für _POST-Variablen Listing 8.4 Beispiel $articleID = t3lib_div::_POST('showArticel'); $getVars = t3lib_div::_POST(); /* Ergebnis in $articleID: 54 Ergebnis in $getVars: Array ( [id] => 40 [showArticle] => 54 ) */

8.1.5

callUserFunction

t3lib_div::callUserFunction( $funcName,&$params,&$ref,$checkPrefix='user_',$errorMode=0 )

166

8.1 Allgemeine Funktionen Führt eine benutzerdefinierte Funktion aus. Standardmäßig sollten diese Funktionen mit dem Präfix „user_“ beginnen. Die Parameter werden als Referenz übergeben. Listing 8.5 Beispiel $userVar = array (); t3lib_div::callUserFunction('user_Func',$userVar,$this); function user_Func($refVar) { $refVar = array(1 => 'hello world'); } /* Ergebnis in $userVar: Array ( [1] => hello world ) */

8.1.6

clientInfo

t3lib_div::clientInfo($useragent='')

Basisinformationen über den Browser und das Betriebssystem des Clients. Listing 8.6 Beispiel $client = t3lib_div::clientInfo(); /* Ergebnis in $client: Array ( [BROWSER] => net [VERSION] => 5 [SYSTEM] => win [FORMSTYLE] => 1 ) */

8.1.7

compat_version

t3lib_div::compat_version($verNumberStr)

Überprüft die Kompatibilität zur übergebenen TYPO3-Version Listing 8.7 Beispiel $check = t3lib_div::compat_version("4.2.0"); /* Ergebnis in $check: 1 */

167

8 Strings, Arrays und Umgebungsvariablen

8.1.8

compileSelectedGetVarsFromArray

t3lib_div::compileSelectedGetVarsFromArray( $varList,array $getArray,$GPvarAlt=1 )

Bildet einen Auszug aus einem eindimensionalen Array, gesteuert über eine kommaseparierte Liste von keys. Wird kein Array definiert, so werden die GET- und POST-Variablen verwendet (Reihenfolge siehe _GP). Listing 8.8 Beispiel $vars = t3lib_div::compileSelectedGetVarsFromArray('name,email',array()); /* Ergebnis ind $vars: Array ( [name] => mickey maus [email] => [email protected] ) */

8.1.9

getHostname

t3lib_div::getHostname()

Gibt den Fully Qualified Domain Name zurück. Listing 8.9 Beispiel t3lib_div::getHostname()

8.1.10 getIndpEnv t3lib_div::getIndpEnv($getEnvName)

Pendant zu $_SERVER, allerdings systemunabhängig. Listing 8.10 Gültige Werte für $getEnvName: SCRIPT_NAME, SCRIPT_FILENAME, REQUEST_URI, PATH_INFO, REMOTE_ADDR, REMOTE_HOST, HTTP_REFERER, HTTP_HOST, HTTP_USER_AGENT, HTTP_ACCEPT_LANGUAGE, QUERY_STRING, TYPO3_DOCUMENT_ROOT, TYPO3_HOST_ONLY, TYPO3_HOST_ONLY, TYPO3_REQUEST_HOST, TYPO3_REQUEST_URL, TYPO3_REQUEST_SCRIPT, TYPO3_REQUEST_DIR, TYPO3_SITE_URL, _ARRAY

8.1.11 getThisUrl t3lib_div::getThisUrl()

Gibt den Host und Pfad zur aktuellen Seite zurück.

168

8.1 Allgemeine Funktionen Listing 8.11 Beispiel $url = t3lib_div::getThisUrl(); /* Ergebnis in $url: domain.com/path/to/script/ */

8.1.12 linkThisScript t3lib_div::linkThisScript(array $getParams = array())

Erzeugt die vollständige URL zum aktuellen Skript, über das Array $getParams können die mit zu übergebenden GET-Parameter gesteuert werden. Ein leeres Array entfernt alle GET-Parameter. Listing 8.12 Beispiel $url = t3lib_div::linkThisScript(array()); /* Ergebnis in $url: /index.php?id=40 */

8.1.13 linkThisUrl t3lib_div::linkThisUrl($url,array $getParams=array())

Die in $url übergebene vollständige URL wird geparst und mit den in $getParams übergebenen Parametern ergänzt, bestehende werden überschrieben. Listing 8.13 Beispiel $url = t3lib_div::linkThisUrl('http://www.domain.com/produkte.html?productid=lee r',array('productid' => '354', 'category' => 'coffee')) /* Ergebnis in $url: http://www.domain.com/produkte.html?productid=354&category=coffee */

8.1.14 locationHeaderUrl t3lib_div::locationHeaderUrl($path)

Erzeugt aus relativen und absoluten Pfaden vollständige URLs.

169

8 Strings, Arrays und Umgebungsvariablen Listing 8.14 Beispiel $url = t3lib_div::locationHeaderUrl('/absolute/path/to/script.html'); /* Ergebnis in $url: http://www.domain.com/absolute/path/to/script.html */ $url = t3lib_div::locationHeaderUrl('relativepath/script.html'); /* Ergebnis in $url: http://www.domain.com/current/path/relativepath/script.html */

8.1.15 makeInstance t3lib_div::makeInstance($className)

Erzeugt eine neue Instanz der Klasse $className – sollte unbedingt statt der PHPMethode „new“ verwendet werden. Listing 8.15 Beispiel $obj =

t3lib_div::makeInstance('tx_extkey_functions');

8.1.16 makeInstanceService t3lib_div::makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=array())

Erzeugt eine neue Instanz eines Service und gibt die Service-Class als Objekt zurück Listing 8.16 Beispiel if (is_object($service = t3lib_div::makeInstanceService('myService'))) { $content = $serviceObj->main($value); }

8.1.17 rmFromList t3lib_div::rmFromList($element,$list)

Löscht den Inhalt von $element aus der kommaseparierten Liste $list.

170

8.2 String-Funktionen Listing 8.17 Beispiel $newList = t3lib_div::rmFromList('24', '12,26,24,1,4'); /* Ergebnis in $newList: 12,26,1,4 */

8.1.18 sysLog t3lib_div::sysLog($msg, $extKey, $severity=0) Schreibt den Inhalt von $msg in den syslog des Servers. Listing 8.18 Gültige Werte für $severity: 0 1 2 3 4

8.2

= = = = =

info, notice, warning, error, fatal error

String-Funktionen

8.2.1

calcParenthesis

t3lib_div::calcParenthesis($string)

Berechnet den übergebenen String unter der Berücksichtigung von Klammern. Listing 8.19 Beispiel $value = t3lib_div::calcParenthesis('(4+20)/4'); /* Ergebnis on $value: 6 */

8.2.2

cmpFQDN

t3lib_div::cmpFQDN($baseIP, $list)

171

8 Strings, Arrays und Umgebungsvariablen Vergleicht den Fully Qualified Domain Name der $baseIP mit einer in $list übergebenen kommaseparierten Liste. Diese Liste darf Wildcards enthalten. Bei Übereinstimmung gibt die Funktion true zurück, ansonsten false. Listing 8.20 Beispiele für $list: subdomain.*.com *.domain.com Falsch wäre: sub*.domain.com

8.2.3

cmpIP

t3lib_div::cmpIP($baseIP, $list)

Vergleicht die IP mit einer in $list übergebenen kommaseparierten Liste. Diese Liste darf Wildcards enthalten. Bei Übereinstimmung gibt die Funktion true zurück, ansonsten false. Es werden sowohl IPv4 als auch IPv6 unterstützt. Listing 8.21 Beispiel für $list 192.168.10.*,192.168.11.*

8.2.4

convUmlauts

t3lib_div::convUmlauts($str)

Wandelt die deutschen Umlaute um. Listing 8.22 Beispiel $string = t3lib_div::convUmlauts('Ä Ö Ü ä ö ü ß'); /* Ergebnis in $string: Ae Oe Ue ae oe ue ss */

8.2.5

csvValues

t3lib_div::csvValues(array $row,$delim=',',$quote='"')

Erzeugt aus einem eindimensionalen Array eine Zeile csv.

172

8.2 String-Funktionen Listing 8.23 Beispiel $csv = t3lib_div::csvValues(array('13','coffee','beans')); /* Ergebnis in $csv: "13","coffee","beans" */

8.2.6

deHSCentities

t3lib_div::deHSCentities($str)

Macht die Bearbeitung von HTML-Entities mit htmlspecialchars rückgängig. Listing 8.24 Beispiel $str = t3lib_div::deHSCentities("""); /* Ergebnis in $str: " */

8.2.7

expandList

t3lib_div::expandList($list)

Eine kommaseparierte Liste von Integer-Werten mit Bereichen „von-bis“ wird als erweiterte kommaseaprierte Liste zurückgegeben mit allen Einzelwerten innerhalb dieser Bereiche. Listing 8.25 Beispiel $list = t3lib_div::expandList("1,5,6-20,98"); /* Ergebnis in $list: 1,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,98 */

8.2.8

fixed_lgd

t3lib_div::fixed_lgd_cs($string,$origChars)

Kürzt den String $string am Ende mit $preStr ab, sofern dieser mehr Zeichen enthält als $origChars.

173

8 Strings, Arrays und Umgebungsvariablen Listing 8.26 Beispiel $str = t3lib_div::fixed_lgd_cs("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt", 30); /* Ergebnis in $str: Lorem ipsum dolor sit amet,... */

8.2.9

fixed_lgd_pre

t3lib_div::fixed_lgd_pre($string,$chars)

Kürzt den String $string am Ende mit „...“ ab, sofern dieser mehr Zeichen enthält als $origChars. Listing 8.27 Beispiel $str = t3lib_div::fixed_lgd_pre("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt.", 36); /* Ergebnis in $str: ...sed do eiusmod tempor incididunt. */

8.2.10 formatForTextarea t3lib_div::formatForTextarea($content)

Optimierte Variante von htmlspecialchars(), sollte für jede Textarea verwendet werden. Listing 8.28 Beispiel $content = t3lib_div::formatForTextarea($content);

8.2.11 generateRandomBytes t3lib_div::generateRandomBytes($count)

Erzeugt einen Zufalls-String mit der Anzahl von $count-Zeichen. Als Bereich werden die gesamten 8 Bit genutzt.

174

8.2 String-Funktionen Listing 8.29 Beispiel $random = t3lib_div::generateRandomBytes(20); /* Ergebnis in $random: uðU†+é /anmeldung.html [method] => get [target] => main )

8.2.13 htmlspecialchars_decode t3lib_div::htmlspecialchars_decode($value)

Umkehrfunktion zur PHP-Funktion htmlspecialchars(). Listing 8.31 Beispiel $value = t3lib_div::htmlspecialchars_decode('&'); /* Ergebnis in $value: & */

8.2.14 implodeArrayForUrl t3lib_div::implodeArrayForUrl($name,array $theArray, $str='',$skipBlank=0,$rawurlencodeParamName=0)

Erzeugt aus dem multidimensionalen Array $theArray einen String, der die Wertepaare des Arrays als GET-Parameter enthält.

175

8 Strings, Arrays und Umgebungsvariablen $name ist hierbei ein Präfix für alle Keys des Arrays. Wird $skipBlank gesetzt, so werden nur GET-Parameter geschrieben, die auch einen Wert übergeben. $rawurlencodeParamName steuert die PHP-Funktion rawurlencode() für die Keys. Listing 8.32 Beispiel $url = t3lib_div::implodeArrayForUrl('product', array( 'id' => '52345', 'category' => '', 'group' => 'coffee,beans' ), '', 1); /* Ergebnis in $url: &product[id]=52345&product[group]=coffee%2Cbeans */

8.2.15 implodeAttributes t3lib_div::implodeAttributes(array $arr, $xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE)

Erzeugt aus einem Array Attribute für bspw. ein html-Tag. $xhtmlSafe = true wird hier empfohlen, da diese Option gleichzeitig eventuell doppelt vorkommende Attribute ignoriert (hier wird jeweils das erste verwendet) und die Werte durch htmlspecialchars() bereinigt. Standardmäßig werden leere Attribute nicht berücksichtigt; falls allerdings $dontOmitBlankAttributes gesetzt ist, werden auch leere Attribute zurückgegeben. Listing 8.33 Beispiel $attr = t3lib_div::implodeAttributes(array( 'action' => '/anmeldung.html', 'Method' => 'get', 'target' => 'anmeldeformular' ), true); /* Ergebnis in $attr: action="/anmeldung.html" method="get" target="anmeldeformular" */

8.2.16 inList t3lib_div::inList($list, $item)

Sucht nach dem Element $item in der kommaseparierten Liste $list. Ist das Element vorhanden, gibt die Funktion true zurück, ansonsten false.

176

8.2 String-Funktionen Listing 8.34 Beispiel $status = (t3lib_div::inList('1,5,8,13,29,rehw,r7345j', 'reh')) ? 'gefunden' : 'nicht gefunden'; /* Ergebnis in $status: nicht gefunden */

8.2.17 int_from_ver t3lib_div::int_from_ver($verNumberStr)

Erzeugt einen Integer-Wert aus einer Versionsnummer im Format „x.x.x“, wobei jeder Teil der Versionsnummer bis 999 zählt. Listing 8.35 Beispiel $ver = t3lib_div::int_from_ver('5.0.13'); /* Ergebnis in $ver: 5000013 */

8.2.18 intInRange t3lib_div::intInRange($theInt,$min,$max=2000000000,$zeroValue=0)

Überprüft, ob $theInt innerhalb der Werte $min und $max liegt. Ist der Wert kleiner oder größer, so wird entweder $min oder $max ausgegeben. Falls der Wert kein Integer-Wert ist, wird er durch $zeroValue ersetzt. Listing 8.36 Beispiele $res[0] = t3lib_div::intInRange(5, 10); $res[1] = t3lib_div::intInRange(5, 1, 10); $res[2] = t3lib_div::intInRange('v', 1, 10); $res[3] = t3lib_div::intInRange(100, 1, 10); /* Ergebnis in $res: Array ( [0] => [1] => [2] => [3] => )

10 5 1 10

177

8 Strings, Arrays und Umgebungsvariablen

8.2.19 intval_positive t3lib_div::intval_positive($theInt)

Falls $theInt negativ ist, wird 0 zurückgegeben, positive Werte bleiben unverändert. Listing 8.37 Beispiele $res[0] = t3lib_div::intval_positive(-5); $res[1] = t3lib_div::intval_positive(10); $res[2] = t3lib_div::intval_positive('v'); /* Ergebnis in $res: Array ( [0] => 0 [1] => 10 [2] => 0 )

8.2.20 isFirstPartOfStr t3lib_div::isFirstPartOfStr($str, $partStr)

Überprüft, ob $partStr mit dem Anfang von $str übereinstimmt, und gibt true oder false zurück. Listing 8.38 Beispiel $str = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt"; $partStr =

"Lorem ipsum dolor sit amet";

$isFirstPart = t3lib_div::isFirstPartOfStr($str, $partStr); /* Ergebnis in $isFirstPart: true */

8.2.21 md5int t3lib_div::md5int($str)

Wandelt die ersten sieben Stellen des md5-Hash von $str in den Integer-Wert um, quasi der Integer-Hash von $str.

178

8.2 String-Funktionen Listing 8.39 Beispiel $intHash = t3lib_div::md5int('coffee'); /* Ergebnis in $intHash: 38711389 */

8.2.22 milliseconds t3lib_div::milliseconds()

microtime() in Millisekunden Listing 8.40 Beispiel $ms = t3lib_div::milliseconds(); /* Ergebnis in $ms: 1256537906427 */

8.2.23 modifyHTMLColor t3lib_div::modifyHTMLColor($color,$R,$G,$B)

Berechnet eine neue Farbe, indem den jeweiligen Farben die Offsets $R, $G und $B dazugerechnet werden. Der gültige Wertebereich liegt zwischen 0 und 255. Listing 8.41 Beispiel $newColor = t3lib_div::modifyHTMLColor('#000000', '155', '0', '255'); /* Ergebnis in $newColor: #9b00ff */

8.2.24 modifyHTMLColorAll t3lib_div::modifyHTMLColorAll($color,$all)

Der Farbe $color wird ein gleichmäßiges Offset dazugerechnet (Aufhellung). Die Werte des Offsets $all können zwischen 0 und 255 liegen.

179

8 Strings, Arrays und Umgebungsvariablen Listing 8.42 Beispiel $newColor = t3lib_div::modifyHTMLColorAll('#000000', '155'); /* Ergebnis in $newColor: #9b9b9b */

8.2.25 normalizeIPv6 t3lib_div::normalizeIPv6($address)

Normalisiert eine komprimierte Darstellung einer IPv6-Adresse $address. Listing 8.43 Beispiel $ipv6 = t3lib_div::normalizeIPv6('3ffe:400:280::1'); /* Ergebnis in $ipv6: 3ffe:0400:0280:0000:0000:0000:0000:0001 *&

8.2.26 removeXSS t3lib_div::removeXSS($string)

Entfernt eventuellen Cross-Site-Scripting-Code im Input-Wert $string. Listing 8.44 Beispiel $input = t3lib_div::removeXSS("

Mit dieser TCA-Definition wird nun Folgendes erreicht:

„ Blogs werden in der Tabelle tx_simpleblog_domain_model_blog gespeichert. „ Posts werden einerseits in der Tabelle tx_simpleblog_domain_model_post gespeichert und andererseits im Feld posts der Tabelle tx_simpleblog_domain_model_ blog referenziert. Hierbei werden dort die UIDs der Post-Beiträge als kommaseparierte Liste gespeichert. Dies wird über die foreign_*-Optionen des TCA erreicht.

„ Tags werden einerseits in der Tabelle

tx_simpleblog_domain_model_tag gespei-

chert und andererseits im Feld tags der Tabelle tx_simpleblog_domain_model_ posts referenziert. Hier wird allerdings – im Gegensatz zu den Posts im Blog – ledig-

lich die Anzahl der Tags in der Tabelle tx_simpleblog_domain_model_posts im

332

16.5 Anlegen eines Repositorys Feld tags abgespeichert. Die dazugehörigen Referenzen werden in die MM-Tabelle tx_simpleblog_post_tag_mm eingetragen. Das Handling für 1:N oder M:M-Relationen ist also prinzipiell komplett identisch – lediglich das TCA gibt vor, wie Extbase damit umgehen soll.

16.5 Anlegen eines Repositorys Vorhin haben wir ein Blog derart erzeugt, indem wir schlicht die Klasse instanziiert haben. Dies ist natürlich nicht praktikabel, da wir das so erzeugte Objekt schlecht speichern können und auch Extbase von dessen Existenz schlicht nichts weiß. Um nun ein Objekt vorschriftsmäßig zu erzeugen, werden wir gemäß der Paradigmen des Domain Driven Designs ein sogenanntes Repository verwenden. Dieses stellt die Schnittstelle von Domain und Infrastruktur dar und wird im Verzeichnis typo3conf/ext/simpleblog/Classes/Domain/Repository mit dem Namen EntityRepository.php erwartet. Hat man also ein Repository für die Entität Blog, so lautet die zugehörige Datei BlogRepository.php, und genau diese legen wir nun an. Listing 16.14 Die Datei BlogRepository.php

Nanu, werden Sie sich jetzt fragen – die Klasse ist ja komplett leer. Das hat tatsächlich seine Richtigkeit. Dadurch, dass die Klasse Tx_Extbase_Persistence_Repository abgeleitet wird, enthält das Repository bereits alle für den Anfang notwendigen Funktionalitäten. So kann beispielsweise ein Objekt (in unserem Fall ein Blog) zum Repository mittels add() hinzugefügt oder mittels remove() entfernt werden. Darüber hinaus stehen bereits per Default Methoden zum Finden alle Objekte (findAll()) oder über die magische Funktion findByEigenschaft() eine Funktion zur Verfügung, die nur diejenigen Objekte findet, deren Eigenschaft einen bestimmten Wert aufweist. So lässt sich beispielsweise nach dem Titel eines Objekts suchen, indem man findByTitle() verwendet.

Anpassen des Blog-Controllers Damit unser Repository auch benutzt wird, müssen wir es im Blog-Controller direkt ansprechen:

333

16 Entwicklung eines eigenen Plug-ins Listing 16.15 Änderung an der Datei BlogController.php

Wir haben den Controller nun um eine zusätzliche „magische“ Methode erweitert, und zwar initializeAction(). Diese Methode wird immer dann aufgerufen, wenn eine Action angefordert wird. Dabei wird kurz zuvor diese Methode angesprungen, und die dort enthaltenen Anweisungen werden noch kurz vor der eigentlichen Action ausgeführt. In diesem Fall wird das Blog-Repository initialisiert. Auf dieses kann später mittels $this>repository zugegriffen werden. Anschließend wird nun auch die Index-Action entsprechend angepasst, indem beispielhaft eine Schleife drei Blogs erzeugt. Der Titel der Blogs wird entsprechend gesetzt und mittels add() im Repository gespeichert. Schließlich werden über die dem Repository eigene Funktion findAll() alle Blogs gesucht und dem View zugeordnet. Zusätzlich müssen wir hier also auch den View entsprechend anpassen: Listing 16.16 Änderungen an der View-Datei index.html



  • {blog.title}


Blog hinzufügen



Hier wird ein neuer View-Handler verwendet, der ähnlich einer foreach-Schleife alle vorher angelegten und persistierten (gespeicherten) Blogs durchgeht und deren Titel in einer ungeordneten Liste zurückgibt. Das Ergebnis nach zweimaligem Neuladen der Seite kann sich durchaus sehen lassen:

334

16.6 Anlegen einer neuen Action

Abbildung 16.7 Ausgabe nach zweimaligem Reload der Seite

Allerdings werden nun bei jedem Reload drei neue Blogs erzeugt. Das ist unter Umständen nicht logisch, sodass es durchaus Sinn macht, das Hinzufügen in eine eigene Action auszulagern und die Index-Action lediglich zum Anschauen der bereits hinzugefügten Blogs zu nutzen. In der Anzeige haben wir ja bereits einen Link angelegt, der mit der Action add versehen wurde – diese werden wir nun mit Leben füllen. Denn bislang ist diese Action nicht definiert, daher springt der Dispatcher immer wieder in die Default-Action index.

16.6 Anlegen einer neuen Action Für eine neue Action brauchen wir zunächst einmal einen neuen Eintrag in der Datei ext_localconf.php. Dort verändern wir den Aufruf der Funktion configureDispatcher(): Listing 16.17 Änderung in der Datei ext_localconf.php Tx_Extbase_Utility_Plugin::configureDispatcher( $_EXTKEY, 'Pi1', array('Blog' => 'index,add'), array('Blog' => 'add') );

Da das Zufügen eines Blogs sicherlich nicht gechachet werden soll, wurde die Action zusätzlich noch einmal in der zweiten Zeile (hervorgehoben) zugefügt. Diese wird dann als USER_INT-Objekt im TypoScript-Code eingefügt. Aussagekräftige Fehlermeldungen Wenn Sie nun einmal probieren, die Website in diesem Stadium aufzurufen, bekommen Sie eine – wie wir finden – sehr anschauliche und aussagekräftige Fehlermeldung.

335

16 Entwicklung eines eigenen Plug-ins

Abbildung 16.8 Ausführliche Fehlermeldung mit Extbase

Dabei wird zunächst der Fehler angegeben – so fehlt dem Programm wohl eine Action mit dem Namen addAction(). Und etwas später sehen Sie dann auch, in welcher Klasse die Action fehlt, nämlich in Tx_Simpleblog_Controller_BlogController. So müssen wir, nach Lektüre dieser Meldung, die Action dort in der entsprechenden Datei einfach nur einfügen. Wichtig ist, dass Sie die Meldungen aufmerksamen studieren und interpretieren können – dies fällt aber aufgrund der Ausführlichkeit ziemlich leicht, sodass Ihnen Extbase eigentlich immer genau sagt, was es benötigt, um den Fehler zu beheben.

16.6.1

Anlegen der Action addAction()

Nun führen wir in der Controller-Klassendatei typo3conf/ext/simpleblog/Classes/ Controller/BlogController.php folgende Veränderung durch: Listing 16.18 Die geänderte Datei BlogController.php public function indexAction() { $this->view->assign('blogs', $this->blogRepository->findAll()); } public function addAction() { for ($i=0; $isetTitle('Das ist mein '.$i.'. Blog'); $this->blogRepository->add($myblog); } $this->redirect('index'); }

Wir haben nun in der Index-Action lediglich die Anzeige der Blogs untergebracht und in der neuen Add-Action das Anlegen der Blogs. Nach dem Anlegen schließlich wird mit dem API-Befehl redirect() wieder auf die Index-Action umgeleitet. Somit wird damit wieder die Liste der Blogs angezeigt. Das Template bleibt dabei unverändert.

336

16.7 Daten per Formular eingeben und auswerten

16.7 Daten per Formular eingeben und auswerten Störend ist natürlich jetzt noch, dass der Titel ausschließlich fest vorgegeben wird und nicht etwa vom User über ein Formular eingegeben werden kann. Da dies natürlich dringend notwendig ist, rüsten wir diese Funktionalität nun nach. Dafür müssen wir zunächst ein neues Template mit dem Namen add.html erstellen, das wir ebenfalls in das Verzeichnis typo3conf/ext/simpleblog/Resources/Private/ Templates/Blog legen. Listing 16.19 Das neue Template add.html Neues Blog anlegen

Blogtitel: (required)

Blog anlegen

Über einen neuen View-Handler können wir nun auch Formulare erzeugen lassen. Der Handler enthält auch gleich die notwendigen Parameter, damit der Dispatcher weiß, was er mit den Daten machen soll. So werden die Formulardaten durch den Controller Blog und die (für uns neue) Action create verarbeitet. Somit müssen wir zunächst diese Action in der Datei ext_localconf.php zufügen: Listing 16.20 Änderungen in der Date ext_localconf.php … Tx_Extbase_Utility_Plugin::configureDispatcher( $_EXTKEY, 'Pi1', array('Blog' => 'index,add,create'), array('Blog' => 'add,create') ); …

Im Blog-Controller nun müssen wir diese Action natürlich einfügen und die Add-Action entsprechend anpassen. Denn unser Plan ist, dass wir zunächst über den Link „Blog hinzufügen“ die Action add aufrufen, die das Formular enthält, in das wir den Namen des Blogs eingeben können. Beim Abschicken des Formulars wird die Action create aufgerufen, die das Blog dann schließlich anlegt. Sobald das Blog dann in das Repository gespeichert wurde, soll eine Weiterleitung auf die Action index erfolgen, die alle angelegten Blogs anzeigt. Nun müssen wir also noch die beiden Actions entsprechend einrichten:

337

16 Entwicklung eines eigenen Plug-ins Listing 16.21 Änderungen in der Datei BlogController.php public function addAction(Tx_Simpleblog_Domain_Model_Blog $newBlog = NULL) { $this->view->assign('newBlog', $newBlog); } public function createAction(Tx_Simpleblog_Domain_Model_Blog $newBlog) { $this->blogRepository->add($newBlog); $this->redirect('index'); }

Interessant sind in beiden Fällen die Parameter der jeweiligen Methoden. Bei der Methode addAction() wird ein leeres Blog übergeben, das allerdings vom Typ der Klasse Tx_Simpleblog_Domain_Model_Blog ist. Damit weiß Extbase automatisch, welche Eigenschaften verwaltet werden müssen. Die Create-Action schließlich bekommt das Objekt vom Formular zurück – allerdings mit gefülltem Titel – und fügt es zum Repository hinzu.

16.8 Objekte aus dem Repository entfernen Mit der Methode add() haben wir bislang Objekte dem Repository zufügen können, nun wollen wir diese natürlich auch wieder entfernen können. Dafür platzieren wir bei der Auflistung aller Blogs am Ende einen Link Delete, mit dem wir das spezielle Blog auch wieder entfernen können. Dies geschieht mit einer eigenen Action delete.

16.8.1

Zufügen der Action delete in der Konfiguration

In der Datei ext_localconf.php müssen wir nun also zunächst wieder unsere neue Action zufügen: Listing 16.22 Änderungen in der Datei ext_localconf.php Tx_Extbase_Utility_Plugin::configureDispatcher( $_EXTKEY, 'Pi1', array('Blog' => 'index,add,create,delete'), array('Blog' => 'add,create,delete') );

16.8.2

Anpassen der Template-Datei index.html

Weiter geht es mit der Template-Datei index.html, indem wir den Lösch-Link platzieren müssen: Listing 16.23 Änderungen in der Template-Datei index.html:
  • {blog.title} (Delete)


  • 338

    16.9 Update von Objekten Als Argument übergeben wir der Action delete schließlich das aktuelle Blog-Objekt (hintere blog-Bezeichnung) in der gleichnamigen Variablen blog (vordere blog-Bezeichnung). Extbase weiß automatisch, welches exakte Blog-Objekt gemeint ist, und extrahiert selbstständig die jeweilige ID heraus. 16.8.2.1

    Zufügen der Action delete im Blog-Controller

    Nun müssen wir noch den Blog-Controller anpassen, indem wir die Action delete zufügen, und schon haben wir (nach dem Löschen des Caches) unser Ziel erreicht: Listing 16.24 Zufügen der Action delete in der Datei BlogController.php public function deleteAction(Tx_Simpleblog_Domain_Model_Blog $blog) { $this->blogRepository->remove($blog); $this->redirect('index'); }

    Über die API-Methode remove() wird nun das übergebene Blog aus dem Repository gelöscht. Nach dem Löschen wird wie gewohnt zur Action index weitergeleitet. Weitere API-Methoden im Repository lauten replace() und update() – diese werden im Referenz-Teil des Buches näher erläutert.

    16.9 Update von Objekten Während wir nun sowohl Objekte angelegt wie auch gelöscht haben, wollen wir uns nun den dritten möglichen Fall der schreibenden Interaktion mit dem Repository ansehen – das Aktualisieren bzw. Update.

    16.9.1

    Edit- und Update-Action hinzufügen

    Um ein Update durchführen zu können, müssen wir einerseits ein Änderungsformular präsentieren und andererseits diese Änderung dann auch im Repository durchführen. Dafür benötigen wir zwei Actions – einmal eine Edit-Action, die das Formular präsentiert, und dann die Update-Action, die die Änderung in das Repository schreibt. Wir müssen also nun die beiden Actions edit und update erst einmal in der ext_localconf.php registrieren und andererseits diese Action im Blog-Controller ausformulieren. Listing 16.25 Änderung in der Datei ext_localconf.php Tx_Extbase_Utility_Plugin::configureDispatcher( $_EXTKEY, 'Pi1', array( 'Blog' => 'index,add,create,edit,update,delete', ), array(

    339

    16 Entwicklung eines eigenen Plug-ins 'Blog' => 'add,create,edit,update,delete', ) );

    Im Blog-Controller müssen wir zudem die beiden Actions ausformulieren: Listing 16.26 Änderungen in der Datei BlogController.php /** * Editiert einen bestehenden Blog * * @param Tx_Simpleblog_Domain_Model_Blog $blog Der ursprüngliche Blog * @return string Formular, um den Blog zu bearbeiten * @dontvalidate $blog */ public function editAction(Tx_Simpleblog_Domain_Model_Blog $blog) { $this->view->assign('blog', $blog); } /** * Aktualisiert einen bestehenden Blog * * @param Tx_Simpleblog_Domain_Model_Blog $blog Eine noch nicht gespeicherte, aber bereits modifierte Kopie des ursprünlichen Blogs * @return void */ public function updateAction(Tx_Simpleblog_Domain_Model_Blog $blog) { $this->blogRepository->update($blog); $this->redirect('index'); }

    Nun brauchen wir einerseits einen Link zum Editieren des Blogs in der Template-Datei der Index-Action: Listing 16.27 Änderung in der Datei index.html
  • {blog.title} ( Edit Blog / Delete) )


  • Und andererseits ein eigenes Template mit dem Dateinamen edit.html im Verzeichnis: typo3conf/ext/simpleblog/Resources/Private/Templates/Blog/: Listing 16.28 Die Datei edit.html Edit Blog "{blog.title}"

    Bitte geben Sie hier Ihre Änderungen ein:



    Blogtitel

    Edit

    340

    16.10 Der Query-Manager Wenn Sie nun das Beispiel im Frontend aufrufen, bekommen Sie hinter jedem Blog einen Link Edit Blog angezeigt, über den Sie ein Formular zum Ändern erhalten. Dort sind automatisch bereits die Eigenschaften in den Feldern vorbelegt – diese werden von Fluid an dieser Stelle automatisch aus dem übergebenen Blog-Objekt extrahiert. Nach der Änderung klicken Sie einfach auf den Button Edit, und schon wurde die Änderung auch im Blog-Repository persistiert.

    16.10 Der Query-Manager Wir haben im Repository bislang nur die Methode findAll() verwendet, um alle gespeicherten Blogs zu erhalten. Wer bereits einige Blogs angelegt hat, dem fällt zunächst auf, dass diese in genau der Reihenfolge wieder ausgelesen wurden, in der sie hinzugefügt wurden. Dies und eben die Auswahl der Blogs selbst wollen wir nun mithilfe des Query-Managers anpassen. Dafür stehen uns weitere „magische“ Methoden des Repositorys zur Verfügung,

    „

    findByUid($uid)

    Findet ein Objekt anhand einer übergebenen UID (die man natürlich kennen muss)

    „

    findBy[Eigenschaft]($search)

    Findet alle Objekte, deren Eigenschaft den Wert $search haben. Dabei wir die Eigenschaft selbst Teil des Funktionsnamens. So lautet die Suche nach dem Titel findByTitle($search), wenn die Eigenschaft $title heißt.

    „

    findOneBy[Eigenschaft]($search)

    Findet das erste auftretende Objekt, dessen Eigenschaft den Wert $search hat. Dabei wird die Eigenschaft selbst Teil des Funktionsnamens – exakt wie bei der vorherigen Funktion. Diese drei genannten Funktionen stehen in jedem Repository automatisch zur Verfügung. Was ist aber, wenn man eigene, individuelle Abfragen braucht? Dann kommt der QueryManager ins Spiel, der im sogenannten Persistence-Framework zu Hause ist – genauer in der Datei typo3conf/ext/extbase/Classes/Persistence/QueryFactory.php. Die Query-Factory schließlich implementiert das Interface Tx_Extbase_Persistence_ QueryInterface, und dieses enthält nun genau die Methoden, mit denen wir unsere Suchabfragen im Repository anpassen können. Um nun einen eigene Query abzusetzen, müssen wir das (bislang noch völlig leere) BlogRepository mit einer geeigneten Methode erweitern:

    341

    16 Entwicklung eines eigenen Plug-ins Listing 16.29 Eigene Query-Methode für die Datei BlogRepository.php public function findSpecial() { $query = $this->createQuery(); … $blogs = $query->execute(); return $blogs; }

    Wir erzeugen in der Funktion findSpecial() zunächst ein Query-Objekt. Mit diesem werden wir gleich interessante Dinge anstellen, daher haben wir diesen Teil erst einmal mit drei Punkten angedeutet. Anschließend wird der Query ausgeführt, wobei alle gefundenen Objekte in der Variablen $blogs gespeichert werden, die dann auch zurückgegeben wird. In der Index-Action des BlogControllers müssen wir jetzt natürlich auch den Query-Aufruf entsprechend anpassen: Listing 16.30 Änderung in der Index_Action der Datei BlogController.php public function indexAction() { //$this->view->assign('blogs', $this->blogRepository->findAll()); $this->view->assign('blogs', $this->blogRepository->findSpecial()); }

    Wir haben den ursprünglichen Aufruf auskommentiert und den Aufruf mit der neuen Methode zugefügt. Ruft man das Frontend nun auf, bekommt man genau dieselbe Anzeige wie vorher – das liegt primär daran, dass wir den Query noch nicht näher spezifiziert haben und somit das Default-Verhalten (finde alle Objekte) automatisch aktiviert wird. Nun fügen wir zur Query-Methode eine Möglichkeit hinzu, alle Blogs zu finden, in deren Titel der String „ist mein“ vorkommt (da wir vorhin Blogs mit dem Titel „Das ist mein x.Blog!“ angelegt hatten). Listing 16.31 Die überarbeitete Methode findSpecial() public function findSpecial() { $query = $this->createQuery(); $query->matching( $query->like('title', '%ist mein%') ); $blogs = $query->execute(); return $blogs; }

    Über die Funktion matching() wird grundsätzlich eine Suche eingeleitet. In diesem Fall bemühen wir nun die Funktion like(), die ähnlich wie bei SQL Wortbestandteile finden kann. Da wir tatsächlich im Hintergrund eine MySQL-Datenbank verwenden, wird somit ein Query der Art: SELECT * FROM tx_simpleblog_domain_model_blog WHERE title LIKE '%ist mein%' abgesetzt, und die Ergebnisse der Abfrage werden als Objekt verpackt zurückgesendet. Der Query-Manager bietet hier noch zahlreiche andere Methoden, die einerseits beliebig kombiniert und andererseits in beliebiger Reihenfolge aufgerufen werden können.

    342

    16.11 Eingabevalidierung So gibt es setLimit(), setOrderings und setOffset(), die auf das ganze Ergebnis angewendet werden können, und logicalAnd(), logicalOr(), logicalNot(), equals(), lessThan(), lessThanOrEqual(), greaterThan() und greaterThanOrEqual() für die Teilergebnisse. Damit lassen sich praktisch alle wichtigen Abfragen datenherkunftsneutral beschreiben. Wenn Sie damit nicht zum erwünschten Ergebnis kommen, gibt es zusätzlich die Möglichkeit, einen SQL-Query (sofern eine Datenbank als Speichermedium verwendet wurde) abzusetzen: $query->statement(SELECT * FROM tx_simpleblog_domain_model_blog WHERE title LIKE '%ist mein%');

    16.11 Eingabevalidierung Hin und wieder ist es hilfreich und auch nötig, eigene GET- oder POST-Variablen zu übermitteln und diese auszuwerten. Hier wurde mit Extbase ein leicht anderes Konzept eingeführt. So müssen alle Eingabevariablen zunächst sowohl registriert wie auch spezifiziert werden. Dies geschieht über die Annotation. Wollen wir beispielsweise in der Index-Action die GET-Variable $name auswerten, so geht das erst einmal nur im Namensraum der er Extension – sprich in unserem Fall würde die Variable tx_simpleblg_pi1[name] lauten. In der Extension selbst wird diese Variable aber auch immer mit $name angesprochen. Als Nächstes müssen wir die Variable als Input-Parameter an die Index-Action übergeben und eben in der zugehörigen Annotation die Variable registrieren und spezifizieren.

    16.12 Validatoren Extbase enthält ein umfangreiches Validation-Framework, das ebenfalls von FLOW3 rückportiert wurde. Damit ist es möglich, eigene Validatoren sowie auch vordefinierte Validatoren verwenden. So kann man damit einfache Fälle, wie die Überprüfung auf Datentypen, Längen und Ähnliches, schnell lösen. Sobald man dann eigene Überprüfungen benötigt, kann man einen eigenen speziellen Validator einsetzen.

    16.12.1 Vordefinierte Validatoren Um auf das Set an vordefinierten Validatoren zuzugreifen, müssen Sie diese über eine Annotation festlegen. Wir wollen beispielsweise sicherstellen, dass der Name eines Blogs sowohl ein Text sein soll wie auch die Mindestlänge von fünf Zeichen haben muss. Dafür fügen wir folgenden Kommentar in der Datei Blog.php vor der Definition der Eigenschaft $title ein.

    343

    16 Entwicklung eines eigenen Plug-ins Listing 16.32 Kommentar in der Datei Blog.php /** * Der Titel des Blogs * * @var string * @validate Text, StringLength(minimum = 5, maximum = 80) * @identity */ protected $title = '';

    Die wirklich entscheidende Zeile haben wir hervorgehoben. Beachten Sie bitte, dass eine Annotation im Gegensatz zu einem reinen PHP-Kommentar mit der Zeichenkette /** eingeleitet wird. Geben Sie etwa nur /* an, so wird der Kommentar von Extbase überhaupt nicht ausgewertet. Über die Annotation @validate wird die Validierung nun eingeschaltet. Nachfolgend werden die möglichen Validatoren angegeben. Wenn es (wie hier) mehrere Validatoren sind, so werden diese per Komma voneinander getrennt und dann später bei der Validierung sequenziell von links nach rechts durchlaufen. An erster Stelle steht hier Text, was bedeutet, dass die Eigenschaft $title daraufhin geprüft wird, dass diese nur Text (darf keine Tags beinhalten) enthalten darf. Der nächste Validator, der auf die Eigenschaft angewendet wird, ist der StringLength-Validator. Dieser prüft die Länge der Eigenschaft und löst einen Fehler aus, wenn diese kürzer als zwei Zeichen und länger als 80 Zeichen ist. Alle verfügbaren, vordefinierten Validatoren finden Sie übrigens im Verzeichnis typo3conf/ext/extbase/Classes/Validation/Validator/ und natürlich im Referenzteil des Buches. Wenn Sie nun den Cache löschen, die Seite erneut aufrufen, auf den Link Blog einfügen klicken und dann einen Blognamen mit nur einem Buchstaben angeben, wird das Blog nicht eingetragen, sondern das Eingabefeld wird erneut angezeigt – allerdings rot unterlegt.

    Abbildung 16.9 Der Name des Blogs muss fünf Buchstaben oder mehr haben.

    16.12.2 Eigene Validatoren Um einen eigenen Validator in Aktion zu sehen, wollen wir festlegen, dass ein neuer Blog nicht den Namen „Extbase“ haben darf.

    344

    16.12 Validatoren Wir müssen hierfür zunächst eine Datei BlogValidator.php im Verzeichnis typo3conf/ext/simpleblog/Classes/Domain/Validator/ anlegen. Listing 16.33 Die Datei BlogValidator.php

    module Sprachlabels für die Simpleblog Extension

    This blog contains the following posts: There are no posts in this blog! Add Blog

    Dieses Blog enthält die folgenden Posts: Keine Posts vorhanden! Blog hinzufügen



    Wir haben hier je drei Labels für zwei Sprachen definiert. Mit der Angabe wird die Default-Sprache definiert – d.h. wenn keine andere spezielle Sprache im TYPO3 konfiguriert wurde oder wenn eine eventuell konfigurierte Sprache in den Sprachfiles nicht wieder gefunden wird. Über wird nun speziell die deutsche Sprache definiert. Letztlich kann man beliebige Bezeichner (Keys) für die Labels verwenden – wir empfehlen, einen strukturellen Namen zu verwenden. So haben wir hier beispielsweise blog.containpost verwendet, um darauf hinzudeuten, dass der Bezeichner im Umfeld der Blogs verwendet wird. Um nun darauf zuzugreifen, wird ein eigener View-Helper translate im Template verwendet – hier das Template index.html des Blogs: Listing 16.60 Änderung in der Blog-Template-Datei index.html …







    Testen können Sie Ihr Setup sehr einfach, indem Sie in das Root-Template im Bereich Setup den folgenden TypoScript-Code platzieren:

    362

    16.16 Konfiguration mittels TypoScript Listing 16.61 TypoScript-Setup zur Änderung der Sprache config.language = de

    16.16 Konfiguration mittels TypoScript TypoScript ist eine mächtige und komplexe Konfigurationsmöglichkeit, die zwar in TYPO3 4.x zur Verfügung steht – unter anderem aber TYPO3 5.x nicht mehr bzw. nicht in dieser Form. Selbstverständlich ist zwar geplant, TypoScript auch in TYPO3 5.x zur Verfügung zu stellen – sicher ist dies zum jetzigen Zeitpunkt allerdings nicht. Und so kann es passieren, dass Ihre Extension – wenn sie denn auf TypoScript basiert – mehr Änderung als normal benötigt, wenn Sie diese für FLOW3 (respektive TYPO3 5.x) fit machen wollen. Prinzipiell werden die TypoScript-Konfiguration im Pfad plugin.tx_extensionname ausgewertet. Dabei wird hier der Extension-Name in Kleinbuchstaben und ohne Underscores „_“ notiert. Extbase wertet dabei den folgenden TypoScript-Baum aus: Listing 16.62 TypoScript-Konfiguration // Das grundlegende TS-Element plugin.tx_extensionname { // Einstellungen für ganze Extensions. Diese sind unter // $this->settings im Controller und als {settings} // im FLUID-Template zugänglich. Hier kommen Ihre individuellen // Einstellungen hinein. settings { … } // Hier werden die Einstellungen für die Persistenzschicht getroffen // beispielsweise die storagePid persistence { // Automatisches Cache-Löschen aktivieren (1 - default) oder // deaktivieren enableAutomaticCacheClearing = 1 // StoragePid (siehe Kapitel 16.19.1) storagePid = … // Einstellungen für das Speichern neuer Datensätze classes { // kompletter Klassenname des Domain-Models [KlassenName] { // ID der Seite, auf der die neuen Datensätze gespeichert // werden sollen newRecordStoragePid = … mapping { // Name der Kind-Klasse (kann entweder per TCA // …['config']['foreign_class'] oder hier definiert werden tableName = … // Angabe der zu mappenden Spalten columns = … } } }

    363

    16 Entwicklung eines eigenen Plug-ins } // Einstellungen für den View und die Templates view { … } // Überschreiben der Lokalisierungen (Sprachlabels) _LOCAL_LANG { … } }

    Um nun ein solches TypoScript zur Verfügung zu haben, werden wir das folgende in der Datei typo3conf/ext/simpleblog/Configuration/TypoScript/setup.txt speichern: Listing 16.63 TypoScript setup.txt für die Extension simpleblog plugin.tx_simpleblog { settings { key1 = value1 key2 = value2 key1.key3 = value3 controllers { Blog { maxItems = 5 } } } }

    Damit das TypoScript nun auch geladen werden kann, erweitern wir die Datei ext_tables.php um die folgende Ladeanweisung: Listing 16.64 Ladeanweisung für das statische TypoScript t3lib_extMgm::addStaticFile($_EXTKEY, 'Configuration/TypoScript', 'Blog Example');

    Durch diese Anweisung werden sowohl eine Datei setup.txt wie auch eine Datei constants.txt in dem angegebenen Verzeichnis spezifiziert, sofern diese vorhanden sind. Um diese nun in das System zu laden, müssen Sie im Modul Web > Template auf Ihr TypoScript-Template im Seitenbaum klicken und über Click here to edit whole template record den Datensatz bearbeiten. Im Tab Includes nun finden Sie im Abschnitt Include static (from extensions): auf der rechten Seite das statische Template. Mit einem Klick darauf und durch anschließendes Löschen des Caches ist dieses eingebunden.

    364

    16.17 Backend-Module mit Extbase

    Abbildung 16.11 Zufügen des statischen Templates (vor dem Klick)

    Um nun darauf zuzugreifen, gehen wir in den Blog-Controller zur Index-Action und geben dort das TypoScript-Settings-Array testweise komplett aus, um zu sehen, wie die Konfiguration dort genau ankommt: Listing 16.65 Ausgabe des Settings-Arrays in der Index-Action des Blog-Cotrollers … public function indexAction($name = 'Test') { debug($this->settings); $this->view->assign('blogs', $this->blogRepository->findAll()); …

    Abbildung 16.12 Ausgabe des Debug-Statements

    Nun könnten wir diese Konfigurationen direkt verwenden – beispielsweise: Listing 16.66 Zugriff auf die Settings-Konfiguration if ($this->settings['key2'] == 'value2') { … }

    16.17 Backend-Module mit Extbase Extbase eignet sich nicht nur hervorragend, um Frontend-Plug-ins zu entwickeln – es ist natürlich auch möglich, damit Backend-Module zu entwerfen. Dies ist insofern eine revolutionäre Neuerung – war es doch bislang nicht möglich, im Backend ein vernünftiges Templating zu verwenden –, sodass zahlreiche Backend-Module die komplette Ausgabe in den Programmcode geschrieben haben.

    365

    16 Entwicklung eines eigenen Plug-ins Hierfür sind grundsätzlich einige zusätzliche Schritte notwendig. Zunächst muss das Backend-Modul in der Datei ext_tables.php registriert werden: Listing 16.67 Registrierung des Backend-Moduls in der Datei ext_tables.php … if (TYPO3_MODE === 'BE') { /** * Hiermit wird ein Backend-Modul registriert */ Tx_Extbase_Utility_Extension::registerModule( $_EXTKEY, 'web', // Modul soll Submodul von 'web' sein 'tx_simpleblog_m1', // Der Key des Submoduls '', // Position innerhalb des Moduls 'web' array( // Angabe sämtlicher Controller-Action // Kombinationen, die im Backend // erlaubt sein sollen 'Blog' => 'index,add,create,edit,update,delete', 'Post' => 'index,add,create,delete,addTag,createTag', 'Tag' => 'add,create' ), array( // Angabe von Zugriffsrechten, Icons // und Label-Datei 'access' => 'user,group', 'icon' => 'EXT:blog_example/ext_icon.gif', 'labels' => 'LLL:EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_mod.xml', ) ); /** * Hinzufügen eines Labels für die Context-Sensitive-Hilfe (CSH) */ t3lib_extMgm::addLLrefForTCAdescr('_MOD_web_BlogExampleTxBlogexampleM1', 'EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_csh.xml'); }

    Im obigen Beispiel wird schlicht das gesamte bislang aufgebaute Simpleblog-Beispiel zusätzlich ins Backend verfrachtet, sodass man nun auch dort die Blogs, Posts und Tags anlegen kann. Selbstverständlich würde man dies bei einem reellen Beispiel trennen, sodass man eigene Backend-Actions eben nur im Backend registriert und damit aufrufbar macht.

    16.18 Der Extbase-Kickstarter Gerade wenn Sie die letzten 100 Seiten dieses dritten Teils des Buches abgearbeitet haben, werden Sie sich sicherlich des Öfteren gefragt haben, ob man davon nicht einige Schritte automatisieren kann – beispielsweise in einer Art Kickstarter. Ingmar Schlecht hat tatsächlich im Sommer 2009 im Rahmen des Google Summer of Code (GSoC) damit begonnen, einen derartigen Kickstarter zu schreiben. Dieser ist bei Drucklegung zwar schon prinzipiell verwendbar – steckt aber noch komplett in den Kinderschuhen und verfügt daher über relativ wenige Features. Dies ist auch ein Grund, warum wir dieses Kapitel so weit hinten platziert haben. Wenn Sie die grundsätzliche Funktionsweise von

    366

    16.18 Der Extbase-Kickstarter Extbase von Grund auf erlernen, werden Sie es später auch leicht haben, den Kickstarter zu verwenden, um Ihnen Arbeit abzunehmen. Andersherum funktioniert dies natürlich nicht. Da wir denken, dass gerade bei den neuen Paradigmen wie MVC ein grundsätzliches Verständnis der Materie vonnöten ist, kann es nicht schaden, einen derartigen Kickstarter erst zu verwenden, wenn die Zusammenhänge komplett klar sind. Da zwischen Manuskriptabgabe und Erscheinen eines Buches bisweilen mehrere Monate liegen, kann es gut sein, dass der Extbase-Kickstarter dann bereits final veröffentlicht wurde und auch über andere oder tiefer gehende Features verfügt wie die, die wir Ihnen im Folgenden zeigen wollen. Aber sehr wahrscheinlich werden Sie nach der Lektüre dieses Buches leicht damit umgehen können. Wenn Sie trotzdem auf Probleme damit stoßen sollten – schreiben Sie uns einfach eine E-Mail: [email protected] – wir helfen Ihnen dann gerne.

    16.18.1 Installation des Extbase-Kickstarters Da der Extbase-Kickstarter zur Drucklegung noch nicht Teil des TYPO3-4.3-Pakets ist, muss dieser zunächst manuell aus dem Forge-SVN installiert werden.Die Adresse dabei lautet https://svn.typo3.org/TYPO3v4/Extensions/extbase_kickstarter/trunk. Unter Linux und Mac OS X können Sie einfach auf der Kommandozeile mittels svn co Adresse die Sourcen aus dem SVN auschecken. Unter Windows können Sie das Programm Tortoise verwenden. Nun kopieren Sie die Sourcen in ein Verzeichnis mit dem Namen extbase_kickstarter (wenn nicht schon bereits geschehen) und diesen Ordner in das Verzeichnis typo3conf/ ext/. Nun taucht der Extbase-Kickstarter im Extension-Manager unter Install Extensions bereits auf, und Sie können diesen mittels Klick auf das Plussymbol am Anfang direkt installieren.

    Abbildung 16.13 Die Extension Extbase-Kickstarter im Extension-Manager

    Nun erscheint im linken Funktionenmenü im Abschnitt Admin (da der Extbase-Kickstarter natürlich nur für Administratoren zugänglich ist) der Eintrag Extbase Kickstarter als letztes Element.

    367

    16 Entwicklung eines eigenen Plug-ins

    Abbildung 16.14 Der Extbase-Kickstarter im Admin tools-Menü

    Klicken Sie nun auf den Extbase-Kickstarter.

    16.18.2 Überblick über den Arbeitsbereich Im nächsten Bild sehen Sie die vier grundsätzlichen Bereiche, die wir im Folgenden kurz anreißen wollen, bevor wir weiter in die Modellierung einsteigen: 1. Wenn Sie auf den Link unter 1 klicken, schiebt sich von links ein Bereich herein, in dem Sie die Extension-Konfiguration durchführen können. Dazu gehören der Name der Extension, der Extension-Key, der Status (alpha, beta, stable) und die Personen, die daran mitgewirkt haben. Wenn Sie die Extension nicht alleine entwickeln, sondern mit einem Team an Leuten, dann können Sie diese hier alle verewigen. 2. Auf der rechten Seite haben Sie ebenfalls einen Bereich, der Code Generator genannt wird. Zur Drucklegung war dieser Bereich allerdings noch leer und hatte keine Funktion. Gut denkbar, dass hier in Zukunft Möglichkeiten zur Steuerung der Codegenerierung zu finden sind. 3. Im unteren Bereich können Sie das erstellte Model abspeichern und wieder laden, ein neues anlegen sowie ein bestehendes löschen. Die momentane Codegenerierung findet beispielsweise erst statt, wenn Sie Ihr Modell abspeichern – darauf kommen wir in Kürze zu sprechen. 4. Hier schließlich startet die Modellierung. Das wird uns gleich im folgenden Unterkapitel beschäftigen.

    368

    16.18 Der Extbase-Kickstarter

    Abbildung 16.15 Der Extbase-Kickstarter in seiner ganzen Pracht

    16.18.3 Eingabe der Extension-Konfiguration Wir klicken nun also auf den Button für die Extension-Konfiguration (Nummer 1 in der vorhergehenden Abbildung). Nun können wir folgende Daten eingeben:

    „ Einen aussagekräftigen Namen „ Den Extension-Key (wir haben hier natürlich einen beliebigen anderen genommen, damit uns keine Extension überschrieben wird)

    „ Eine kurze Beschreibung „ Den Status (hier development) „ Einen Ansprechpartner – dieser kann über add angelegt werden ƒ Dort den Namen, die Rolle, E-Mail und Firma (sofern vorhanden) eintragen. – Teile dieser Informationen finden sich dann im Quellcode beispielsweise wieder. Nun sollte man zunächst erst einmal unten auf den Buttons Save klicken und warten, bis das Popup dies mit Saved! bestätigt hat.

    369

    16 Entwicklung eines eigenen Plug-ins

    Abbildung 16.16 Eingabe der Extension Konfiguration

    16.18.4 Modellierung Nun können wir mit der Modellierung starten. Zu Demonstrationszwecken und auch um einen Vergleich zu haben, wollen wir nun das Simpleblog-Beispiel 1:1 nachmodellieren, d h., wir benötigen ein Blog mit einem Titel, das kann Posts beinhalten (die ebenfalls einen Titel haben), und die Posts können mit einem Tag versehen werden (der einen Namen hat und zudem über eine M:M-Relation verknüpft ist). Gehen Sie nun mit der Maus auf die in Abbildung 16.15 unter Punkt 4 gezeigte grünblaue Fläche mit der Aufschrift New Model Object, klicken Sie mit der linken Maustaste darauf und ziehen nun nach unten auf die freie Fläche darunter, ohne die Maustaste währenddessen loszulassen. Wenn Sie nun mit der Maus stehen bleiben und die Maustaste loslassen, wurde ein neues Objekt erzeugt.

    Abbildung 16.17 Ein neues Objekt wurde erzeugt.

    370

    16.18 Der Extbase-Kickstarter 16.18.4.1 Anlegen des Blog-Objekts Über einen Klick auf click to edit können Sie nun einen Namen für das Objekt vergeben – wir nehmen hier zunächst als Namen Blog. Über Klick auf OK wird der Name übernommen. Nun füllen wir die restlichen Elemente aus:

    „ Bei Domain Object Settings wählen wir als Object Type Entity. Sie könnten hier auch Value Object auswählen, wenn Ihr Object einem solchen entsprechen würde. Zusätzlich wählen Sie Is aggregate root aus, da Sie wollen, dass Ihr Blog ein eigenes Repository erhält. Als Description könnten Sie nun auch eine kurze Beschreibung wie Das ist das Blog hinterlegen.

    „ Sie können nun bei Default Actions eine Reihe von Actions auswählen, die Sie gerne vorbereitet hätten. Bei Drucklegung des Buches war dieser Bereich allerdings noch ohne Funktion, sodass Sie die Controller samt Actions später manuell anlegen mussten.

    „ Nun folgen die Properties (Eigenschaften), die Sie mittels Add zufügen können. ƒ Als Property Name geben Sie den Namen der Eigenschaft an – bei uns Title. Der Property Type ist Text String. Als Description könnten Sie beispielsweise Titel des Blogs eingeben. Und da Sie sicherstellen sollten, dass das Attribut auch eingegeben wird, wählen Sie die Checkbox Is Required aus.

    „ Schließlich brauchen Sie noch eine Relation, die Sie über Add im Abschnitt Relations anlegen können.

    ƒ Hier tragen Sie als Name

    Posts ein und als Type 0..* (foreign Key). Diese Einstellung entspricht der kommaseparierten Liste im Feld Posts.

    Abbildung 16.18 Das Blog-Objekt

    371

    16 Entwicklung eines eigenen Plug-ins

    16.18.5 Anlegen des Post-Objekts Letztlich wiederholen wir hier exakt die Schritte des vorherigen Kapitels, mit einer Ausnahme:

    „ Die Relation zu Tags ist vom Typ 0..*

    (aasociation table).

    Abbildung 16.19 Das Post-Objekt

    16.18.6 Anlegen des Tag-Objekts Auch hier halten wir uns wieder an die vorherige Anleitung mit dem Unterschied, dass wir hier keine Relation benötigen und der Bezeichner nicht Title, sondern Name heißt und das Objekt kein Aggregate Root ist.

    Abbildung 16.20 Das Tag-Objekt

    372

    16.18 Der Extbase-Kickstarter

    16.18.7 Relationen festlegen Nun, da wir alle drei relevanten Objekte positioniert haben, können wir die Relationen zwischen diesen festlegen. Dies erfolgt sehr einfach und vor allem sehr elegant. Gehen Sie mit der Maus auf den runden Kreis rechts neben dem Label Related Object im Post-Objekt. Nun klicken Sie darauf und lassen den Finger auf der linken Maustaste. Wenn Sie nun die Maus bewegen, sehen Sie eine blaue Pipe (eine dicke Linie), die sich mit Ihrer Maus mitbewegt. Nun bewegen Sie die Maus auf das Post-Objekt in die linke obere Ecke gleich rechts vom TYPO3-Logo – dort ist ebenfalls ein runder Kreis. Genau über diesem lassen Sie nun die Maustaste los. Jetzt sollten die beiden Objekte verbunden sein, und die Verbindung – die sogenannte Pipe – bewegt sich auch mit, wenn Sie die Objekte verschieben. Zusätzlich sehen Sie den Text [wired] rechts neben dem Kreis im Abschnitt Relations. Dasselbe wiederholen Sie mit den Posts – auch hier greifen Sie den Kreis im Relationsbereich des Post-Objects und ziehen diesen auf den Kreis in der linken oberen Ecke des TagObjekts. ACHTUNG – die Reihenfolge ist entscheidend Die Reihenfolge – also von welchem Element zu welchem Element Sie die Pipe ziehen – ist entscheidend. Versuchen Sie es genau andersherum, wie eben beschrieben, bekommen Sie eine Fehlermeldung und können Ihre Extension nicht abspeichern.

    Abbildung 16.21 Fertig verknüpftes Domain-Model

    373

    16 Entwicklung eines eigenen Plug-ins Nun können Sie das Domain-Model über die Schaltfläche Save unten in der Mitte abspeichern.

    Abbildung 16.22 Durch den Extbase-Kickstarter angelegte Verzeichnisse und Dateien

    Wie Sie gut erkennen können, hat der Extbase-Kickstarter ganze Arbeit geleistet und nahezu alle wichtigen Verzeichnisse und Dateien angelegt. Lediglich die Controller fehlen bei Drucklegung noch – es ist aber damit zu rechnen, dass diese bei Erscheinen des Buches ebenfalls einwandfrei erzeugt werden. Aber selbst jetzt können Sie diese leicht durch das bisher erworbene Wissen anlegen. Eine Datei verdient eine besondere Hervorhebung – kickstarter.json. Diese ist dafür zuständig, dass eine derart erzeugte Extension mit dem Extbase-Kickstarter wieder gelesen und damit bearbeitet werden kann. Aber wie es bei Kickstarten so ist – dieser erzeugt den Code stets neu. Haben Sie also in den Dateien eine manuelle Änderung vorgenommen, so werden diese Änderungen beim erneuten Abspeichern allesamt wieder überschrieben. Legen Sie sich also in jedem Fall eine Sicherung davon an.

    16.19 Weitere Extbase-Interna In diesem Kapitel sollen die Interna – meist Kleinigkeiten – aufgelistet werden, für die ein eigenes Kapitel zu überdimensioniert wäre.

    16.19.1 StoragePid Jeder Datensatz liegt in TYPO3 auf einer einzelnen Seite. Diese Seite wiederum hat eine Seiten-ID, die als PID (Parent-ID) bezeichnet wird. Damit hat also auch jeder Datensatz eine ebensolche PID und ist damit einer speziellen Seite zuzuordnen.

    374

    16.19 Weitere Extbase-Interna Nun gibt es in Extbase einige Fälle, bei denen die Storage PID ermittelt werden muss:

    „ Schreiben „ Aktualisieren „ Lesen 16.19.1.1 Schreibzugriffe Sobald Extbase versucht, Datensätze (beispielsweise neue Blog- oder Post-Einträge) abzuspeichern, muss ermittelt werden, welche PID diese bekommen sollen – dabei geht Extbase wie folgt vor: 1. Existiert der folgende TS-Schlüssel, so wird dieser verwendet: plugin. tx_[pluginName]. persistence. classes. [KlassenName]. newRecordStoragePid

    Dabei ist anstelle von [pluginName] der komplette, kleingeschriebene Plug-in-Name ohne Unterstriche und anstelle von [KlassenName] der komplette Class-Name der Model-Datei einzugeben. 2. Existiert der folgende TS-Schlüssel, so wird dieser verwendet: config. tx_extbase. persistence. classes. [KlassenName]. newRecordStoragePid

    Dabei ist anstelle von [KlassenName] der komplette Class-Name der Model-Datei einzugeben. 3. Existieren beide TS-Schlüssel nicht, wird der Datensatz auf der Seite mit der Weltkugel mit per PID = 0 gespeichert. 16.19.1.2 Aktualisieren Beim Aktualisieren wird die PID natürlich per Default nicht verändert. Wird also ein Datensatz auf der PID=5 angelegt, so wird dieser nach dem Aktualisieren dort auch wieder abgespeichert. Allerdings kann man den Speicherort in der Domäne verändern, wenn das Model die Eigenschaft $pid und entsprechende Getter- und Setter-Methoden hat. Damit ist es also möglich, einen Datensatz von einer Seite auf eine andere Seite zu verschieben.

    375

    16 Entwicklung eines eigenen Plug-ins 16.19.1.3 Lesen Auch das Lesen von Datensätzen gehorcht eigenen Regeln – diese werde wie folgt durchlaufen: 1. Falls das Feld Startingpoint im Backend der Plug-in-Konfiguration gesetzt ist, werden alle Datensätze, die dieses Plug-in betreffen, von der dort hinterlegten Seite geladen. 2. Wenn dies nicht der Fall ist, wird nach dem folgenden TS-Key gesucht und der dort hinterlegte Wert als PID verwendet: plugin. tx_[pluginName]. persistence. storagePid

    Dabei ist anstelle von [pluginName] der komplette, kleingeschriebene Plug-in-Name ohne Unterstriche einzugeben. 3. Wenn dies auch nicht der Fall ist, wird nach dem folgenden TS-Key gesucht und der dort hinterlegte Wert als PID verwendet: config. tx_extbase. persistence. storagePid

    4. Ist dies schließlich auch nicht der Fall, so wird für die PID der Wert 0 verwendet.

    16.19.2 MVC-Request Nach einem MVC-Request landet man in einem Controller in einer bestimmten Action. Nun wäre es aber sehr brauchbar, wenn man auf genau diese Daten gesammelt zugreifen könnte. Dies ist über das $this->request-Objekt möglich. Die Eigenschaften dieses Objekts sind alle protected – insofern ist nur ein Zugriff mittels Getter möglich. Für alle Eigenschaften existieren aber natürlich entsprechende Getter – dafür müssen Sie einfach get vor die Eigenschaft stellen und den ersten Buchstaben der Eigenschaft großschreiben. Beispielsweise erreichen Sie die Eigenschaft pluginName über den Getter $this->request->getPluginName(). Tabelle 16.1 Eigenschaften des Request-Objekts

    376

    Eigenschaft

    Beispielwert

    Beschreibung

    format

    html

    Das Ausgabeformat

    method

    GET

    Die Übertragungsmethode (GET oder POST)

    requestURI

    http://….index.php?id=…

    Die komplette URL des Requests

    baseURI

    http://127.0.0.1:8080/

    Die URL des Hosts

    16.19 Weitere Extbase-Interna Eigenschaft

    Beispielwert

    Beschreibung

    controllerObjectNamePattern

    Tx_@extension_Controller_ @controllerController

    Die Vorlage zum Ermitteln des Controllers

    pluginName

    Pi1

    Der Name des Plug-ins

    controllerExtensionName

    Simpleblog

    Der Name der Extension

    controllerName

    Blog

    Der Name des Controllers

    controllerActionName

    index

    Der Name der Action

    arguments

    Array ( [blog] = 65 [action] = index [controller] = Blog

    Alle GET-/POST-Parameter als Array

    ) dispateched

    1

    errors

    Angabe, ob der Request durch den Dispatcher geschickt wurde Alle Fehlermeldungen als Array

    16.19.3 FlashMessages realisieren Häufig möchte man den User über das informieren, was die Software im Hintergrund macht. Beispielsweise sind Statusmeldungen ein probates Mittel, um dem User mitzuteilen, was die letzte Action gerade gemacht hat. So präsentiert man z.B. ein Formular zum Anlegen eines Blog-Posts, und nach dem Abschicken wechselt man (im Erfolgsfall) per redirect() zur Index-Action, die die Liste aller Blogs anzeigt. Wenn nun auf dieser Seite ganz oben ein Hinweis in der Art „Ihr Post wurde erfolgreich angelegt“ angezeigt werden würde, wäre dies ein Schritt hin zu einer perfekten Benutzerführung. Der User wäre somit stets informiert, was die letzte Aktion gerade bewirkt hat. Sogenannte FlashMessages lösen diese Aufgabenstellung perfekt. Dazu gehört, dass man einen FlashMessage-Speicher zu jeder Zeit mit Nachrichten befüllen und diese dann gebündelt an einer anderen Stelle wieder ausgeben kann. Für das Handling von FlashMessages stehen Ihnen folgende Methoden zur Verfügung:

    „

    $this->flashMessages->add($message)

    Zufügen einer Nachricht zum FlashMessage-Speicher

    „

    $this->flashMessages->getAll()

    Ermittelt alle Nachrichten aus dem FlashMessage-Speicher und gibt diese zurück

    „

    $this->flashMessages->getAllAndFlush()

    Ermittelt alle Nachrichten aus dem FlashMessage-Speicher, gibt diese zurück und löscht den Speicher anschließend

    „

    $this->flashMessages->flush()

    Löscht den FlashMessage-Speicher

    377

    16 Entwicklung eines eigenen Plug-ins

    „

    $this->flashMessages->persist()

    Normalerweise werden alle FlashMessages spätestens nach Ablauf des Skriptes wieder aus dem Speicher gelöscht. Diese Methode sorgt dafür, dass die FlashMessages in der Session des Users gespeichert werden und somit zu einem späteren Zeitpunkt immer noch vorhanden sind.

    „

    $this->flashMessages->loadFlashMessagesFromSession()

    Hiermit werden die mittels persist() gespeicherten FlashMessages wieder aus der Session des Users ins Objekt geholt und stehen wieder über die obigen Befehle zur Verfügung.

    378

    17 17 Die Fluid-Template-Engine In einem MVC gesteuerten System kommt natürlich der View auch eine ganz besondere Aufmerksamkeit zuteil. Gerade durch die Trennung von Businesslogik und Darstellung, muss die View dieses auch ideal widerspiegeln können. Per Default besitzt die View lediglich eine sehr einfache Methode render() – die allerdings über keinerlei zusätzliche Funktionalitäten verfügt. Daher war schnell klar, dass man an dieser Stelle eine eigene Template-Engine benötigt, die sich perfekt in das MVC-Framework einpasst und zudem leistungsfähig genug ist, um auch komplizierteste Anwendungsfälle abdecken zu können. Zudem – und auch dies war eine Hauptforderung – sollte es möglich sein, dass Templates auch von einem Nichtprogrammierer (wie einem Designer) erstellt bzw. geändert werden können. Weiterhin sollte keine allzu exotische Sprache für das Template ersonnen werden, da diese ansonsten mit keiner der üblicherweise eingesetzten IDEs in irgendeiner Weise kompatibel gewesen wäre. Sebastian Kurfürst, der Erfinder von Fluid, hat im Herbst 2008, direkt nach den Berliner Transition Days, bestehende Systeme wie das klassische herkömmliche TYPO3 Templating (basierend auf Marker und Subparts), PHPTAL oder Smarty untersucht und verglichen und daraufhin schnell festgestellt, dass diese Systeme aus dem einen oder anderen Gründen nicht den Anforderungen entsprechen. Entweder musste man zur Verwendung über Programmierkenntnisse verfügen (wie dies beim klassischen System der Fall ist), oder man war zu eingeschränkt in den Möglichkeiten. Nachteile der klassischen Template-Engine

    „ „ „ „

    Basiert lediglich auf Marker und Subparts – dadurch überhaupt nicht flexibel genug Es gibt keinerlei Ablaufsteuerung (Control Flow) Keine Erweiterbarkeit Arbeitet ausschließlich mit Arrays – aber nicht mit Objekten

    379

    17 Die Fluid-Template-Engine Nachteile von Smarty

    „ Basiert immer noch auf PHP4 „ Hat eine eigene proprietäre Syntax, die auf geschweiften Klammern beruht – damit ist keine Auto-Completion in einer IDE möglich

    „ Die eingebauten Funktionen verfügen nicht über Namespaces – dadurch sind Kollisionen mit eigenen Funktionen möglich. Nachteile von PHPTAL

    „ Das Markup basiert zwar auch wohlgeformtem XML – es ist aber nicht valide. „ Die Semantik ist sehr intuitiv und gewöhnungsbedürftig. „ PHP wird innerhalb des Templates verwendet – dadurch Trennung von Designer und Programmierer nicht haltbar

    „ Keine Möglichkeit der Auto-Completion in IDEs „ Nur schwer mittels eigener Funktionen zu erweitern Die Lösung Und so war (wieder einmal) die Idee geboren, eine speziell angepasste und alle Vorteile vereinende Template-Engine selbst zu schreiben. Diese wurde vorläufig mit dem Arbeitstitel BEER3 bezeichnet – dann aber durch eine Community-Befragung in Fluid geändert. Fluid ist angetreten, um folgende Vorteile zu realisieren:

    „ Die Templating-Engine soll einfach und elegant zu verwenden sein. „ Es soll ein einfaches und klares Interface existieren, mit dem man die Engine leicht erweitern kann.

    „ Es sollen unterschiedliche Ausgabeformate – nicht nur HTML – damit unterstützt werden.

    „ Die Engine soll den Template-Editor in vielerlei Hinsicht unterstützen – beispielsweise soll Auto-Completion in gängige IDEs möglich sein.

    „ Programmiercode und Ausgabe müssen komplett getrennt sein.

    17.1

    Vorbereitung Damit es möglich ist, die nachfolgenden Beispiele allesamt gleich auszuprobieren, müssen wir zunächst eine kleine Testumgebung aufsetzen. Dafür installieren wir einerseits natürlich die Extensions extbase und fluid (die aber, wenn Sie das vorherige Kapitel verfolgt haben, bereits installiert sein sollten) und die Extension efempty (Extbase Fluid Empty) aus dem Extension Respository, die eine „leere“ Extension

    380

    17.1 Vorbereitung mit einem rudimentären Model Start (mit einer Eigenschaft title), einem Controller (StartController), einer Action (index) und einem dazugehörigen View zur Verfügung stellt. Nun platzieren Sie die Extension auf einer Seite, indem Sie das Content-Element-Plug-in auswählen und als Plug-in-Typ Empty Extbase/Fluid Container angeben.

    Abbildung 17.1 Einfügen der Extension efempty als Content-Element

    Wenn Sie nun die Extension im Frontend aufrufen, sollten Sie die folgende Ausgabe bekommen:

    Abbildung 17.2 Ausgabe der Extension efempty im Frontend

    Für die weiteren Ausführungen benötigen wir im Grunde zwei Dateien, einmal die Controller-Datei StartController.php im Verzeichnis typo3conf/ext/efempty/Classes/ Controller/ und dann die View-Datei index.html im Verzeichnis typo3conf/ext/ efempty/Resources/Private/Templates/Start/.

    381

    17 Die Fluid-Template-Engine

    17.2

    Basissyntax und einfache Ausgabe In der Controller-Datei – genauer in der Index-Action – wird nun über die Methode assign() ein Wert an die View übergeben: Listing 17.1 Einfache Zuweisung mittels assign() im Start-Controller public function indexAction() { … $this->view->assign('helloworld','Hello world!'); … }

    Die entscheidende Stelle haben wir hier hervorgehoben. Die Methode assign() hat dabei zwei Parameter – über den ersten wird der Name festgelegt, unter dem der Wert der Zuweisung später im Template erreichbar sein soll, und über den ersten wird der Wert selbst spezifiziert. In unserem Beispiel wird also der Wert Hello world! an den Bezeichner helloworld übergeben. Nun betrachten wir uns die View-Datei, um zu sehen, wie das nun verarbeitet und ausgegeben wird: Listing 17.2 Ausgabe des Bezeichners helloworld in der View-Datei index.html This is the efempty extension! It works :-) {helloworld}

    Der Bezeichner wird also lediglich in geschweifte Klammern geschrieben, und schon sorgt Fluid dafür, dass einerseits nachgesehen wird, welcher Wert für diesen Bezeichner vorhanden ist, und andererseits wird dafür gesorgt, dass der Wert ausgegeben wird.

    17.2.1

    Arrays

    Um nun beispielsweise Arrays auszugeben, verwendet Fluid die sogenannte Punktsyntax (oder auch Objektzugriffssyntax). So kann über den Bezeichner mit dem Punkt auf den numerischen Index von Array zugegriffen werden. In den folgenden Codebeispielen notieren wir der Übersichtlichkeit halber oben die entscheidenden Codezeilen in der Action und darunter den Code im Template. Listing 17.3 Ausgabe eines numerischen Arrays // Code in der IndexAction des Start-Controllers $helloarray = array('Hello','World!'); $this->view->assign('helloworld',$helloarray); // Code im Index-Template {helloworld.0} {helloworld.1}

    382

    17.2 Basissyntax und einfache Ausgabe Man kann also über die Punktschreibweise ganz einfach auf den numerischen Index zugreifen. Natürlich funktioniert dies ähnlich für einen assoziativen Index: Listing 17.4 Ausgabe eines assoziativen Arrays // Code in der IndexAction des Start-Controllers $helloarray = array('hello' => 'Hello', 'world' => 'World!'); $this->view->assign('helloworld',$helloarray); // Code im Index-Template {helloworld.hello} {helloworld.world}

    Hier wird einfach der assoziative Index angegeben, um darauf zuzugreifen. Mehrdimensionale Arrays erreichen Sie dementsprechend ganz ähnlich. Sie müssen nur einfach anstelle der Klammern in PHP Punkte notieren. $helloarray['foo'][1]['bar'] wird somit zu {helloworld.foo.1.bar}.

    17.2.2

    Objekte

    Da Fluid grundsätzlich objektorientiert arbeitet, ist auch in diesem Bereich gute Unterstützung zu erwarten. Und tatsächlich sind das Ansprechen eines Objekts bzw. dessen Eigenschaften im Grunde exakt identisch zur Ansprache als Array. Listing 17.5 Anspache von Arrays // Code in der IndexAction des Start-Controllers $start = new Tx_Efempty_Domain_Model_Start; $start->setTitle("Hello World!"); $this->view->assign('helloworld', $start); // Code im Index-Template {helloworld.title}

    Die Extension efempty enthält ja genau ein Model mit dem Namen Start, das als Klasse Tx_Efempty_Domain_Model_Start manifestiert wurde. Dort gibt es eine Eigenschaft title, und die wiederum wird mit dem Setter setTitle gesetzt. Nun wird also das ganze Objekt samt gesetztem Titel an die View übergeben, und dort wird der Titel mittels {helloworld.title} ausgegeben. Das klingt zunächst logisch und nachvollziehbar – ist aber nur die halbe Wahrheit. Im Model wird die Eigenschaft title nämlich als protected gekennzeichnet – d h., ein direkter Zugriff darauf ist somit natürlich nicht möglich. Daher wird auch in obigem Fall die Eigenschaft nicht direkt ausgelesen, sondern es wird stattdessen der im Model vorhandene Getter getTitle() verwendet. Und auch hier ist es einfach, auf Objekte zuzugreifen, die lediglich Unterobjekte sind. Trennen Sie diese einfach wieder mit einem Punkt. So könnte man mittels {blog.post.comment.0} auf den ersten Kommentar eines Posts zugreifen, der wiederum in einem Blog liegt.

    383

    17 Die Fluid-Template-Engine Das ist schon alles, was Sie über die Basissyntax von Fluid wissen müssen – das ist der sogenannte Core (auch Kern genannt). Alle weiteren Funktionalitäten werden von sogenannten ViewHelpern übernommen, die wir uns im Folgenden näher ansehen.

    17.3

    Fluid ViewHelper-Syntax Sämtliche Ausgabelogik nun wird von speziellen Tags, den sogenannten ViewHelpern, übernommen. Diese sorgen für Kontrollstrukturen, Schleifen, Links und ähnliche benötigte Funktionalitäten. Die Grundsyntax dabei ist wie folgt (beachten Sie, dass das kleine f und der Doppelpunkt sozusagen den Namensraum von Fluid angeben): …

    Dabei entspricht jeder Tag (also jeder ViewHelper) einer eigenen ViewHelper-Klasse, die sich bei den mit Fluid mitgelieferten ViewHelpern im Verzeichnis typo3conf/ext/ fluid/Classes/ViewHelpers befindet. So gibt es beispielsweise den If-ViewHelper, der mit … spezifiziert wird. Die zugehörige Datei befindet sich also exakt hier: typo3conf/ext/fluid/Classes/ViewHelpers/IfViewHelper.php

    Für einige ViewHelper sind komplexere Aufgaben vorgesehen (beispielsweise bei der Link-Erzeugung oder den Formularen). Hier wird ein eigenes, gleich benanntes Unterverzeichnis im ViewHelpers-Verzeichnis angelegt, das die zugehörigen ViewHelper enthält. Angesprochen werden diese dann über die Punktsyntax. So wird beispielsweise der Action-Link … über die folgende Datei verwaltet: typo3conf/ext/fluid/Classes/ViewHelpers/Link/ActionViewHelper.php

    17.3.1

    Namespace (Namensraum)

    Wir haben nun immer ein kleines f: als Namensraum verwendet. Dies ist aber nicht ohne Grund so, sondern wurde implizit definiert. In jedem Fluid-Template kann man nämlich den verwendeten Namensraum mit folgendem Bezeichner definieren: Listing 17.6 Default-Deklaration des Fluid-Namensraumes {namespace f=Tx_Fluid_ViewHelpers}

    Hier wird festgelegt, dass das kleine f letztlich immer als Abkürzung für Tx_Fluid_ViewHelper steht. Damit wird dann auch klar, warum bei f:for beispielsweise die Klasse Tx_Fluid_ViewHelper_For angesprochen wird.

    384

    17.3 Fluid ViewHelper-Syntax Sie können selbstverständlich auch Ihren eignen Namensraum festlegen – beispielsweise wenn Sie eigene ViewHelper schreiben. Dies wird uns in einem späteren Kapitel natürlich auch noch begegnen. Lassen Sie die Namespace-Deklaration weg, wird automatisch die obige DefaultDeklaration verwendet.

    17.3.2

    Argumente

    Bei einigen ViewHelpern kann man Argumente übergeben, beispielsweise bei Links oder Formularen. Die Notation für solche Argumente folgt dabei der JSON-Syntax, die unter Umständen aus JavaScript bekannt ist. Dabei gilt folgende Festlegung:

    „ Ein Objekt beginnt mit { und endet mit }. Es kann eine durch Kommata geteilte, ungeordnete Liste von Eigenschaften enthalten.

    „ Eine Eigenschaft besteht aus einem Schlüssel und einem Wert, getrennt durch einen Doppelpunkt.

    „ Ein Schlüssel ist eine Zeichenkette. „ Ein Wert ist ein Objekt, ein Array, eine Zeichenkette, eine Zahl oder einer der Ausdrücke TRUE oder FALSE Listing 17.7 Einige Beispiele für Argumente // über den Parameter arguments wird ein Argument übergeben ... read more // zwei oder mehr Argumente sind natürlich ebenfalls möglich Edit

    17.3.2.1

    Boolesche Ausdrücke

    Beispielsweise kann dem If-ViewHelper ein Argument übergeben, das vom Typ Boolean sein soll und darauf geprüft wird. Die boolesche Bedingung wird dabei wie folgt behandelt:

    „ Ist der übergebene Wert vom Typ Boolean (TRUE oder FALSE), wird dieser verwendet. „ Ist der übergebene Wert vom Typ String (eine Zeichenkette) und ist der String false oder leer, so wird der boolesche Wert FALSE verwendet.

    „ Ist der übergebene Wert vom Typ Numeric (eine natürliche Zahl) und ist diese Zahl größer als null, so wird TRUE verwendet.

    „ Ist der übergebene Wert vom Typ Array oder ist es ein Countable Object (zählbares Objekt), dann wird TRUE verwendet, wenn es mindestens ein Element oder Objekt enthält.

    385

    17 Die Fluid-Template-Engine

    „ Ist der übergebene Wert vom Typ Object (ist es also ein Objekt), so wird TRUE zurückgegeben.

    „ In allen anderen Fällen wird FALSE zurückgegeben. 17.3.2.2

    Komplexe boolesche Ausdrücke

    Oftmals kann es nötig sein, auch komplexere boolesche Ausdrücke zu evaluieren. Stellen wir uns einfach einmal vor, dass wir eine Art Menü haben, das alle verfügbaren Seiten anzeigt. Nun soll aber die aktuell ausgewählte Seite im Menü etwas hervorgehoben werden. Dies könnte man vielleicht wie folgt notieren: Listing 17.8 Beispiel für einen komplexen booleschen Ausdruck



    Hier wird mit dem Operator == geprüft, ob die Seite in der Schleife identisch mit der aktuellen Seite ist, und wenn dem so ist, wird die Ausgabe mit einem -Tag hervorgehoben. Ein komplexer boolescher Ausdruck kann dabei das folgende Format haben: Ausdruck1 Operator Ausdruck 2

    Als Operatoren sind dabei folgende zugelassen:

    „ „ „ „ „ „

    == (identisch) > (größer) >= (größer oder gleich) < (kleiner)