Schlagwort: Typo3

Typo3 Snippets

08. Dezember 2009

Typo3 Snippets
Kleine Sammlung von Typo3 Snippets und nützlichen Quellen zum gleichen Thema für alle Kursteilnehmer des Typo3 Einsteiger Seminars. Die hier aufgelisteten Snippets sind nur eine lose Auflistung von Fragmenten aus den verschiedensten Aufgabenbeeichen, die vielleicht hilfreich sein könnten. Von der Optimierung der generierten HTML Syntax, über Navigationsvarianten bis zu speziellen Wrap Anweisungen.

Was ist TypoScript eigentlich? Grundsätzlich: TypoScript ist keine Programmiersprache. Es ist eher eine Konfigurationssprache – vergleichbar mit der Windows Registry. Und wie die Windows Registry ist TypoScript mächtiger als man auf den ersten Blick vermutet. Die offizielle Dokumentation findet man unter TSref bei Typo3. Und noch eine Analogie zur Windows Registry – je mehr Module und Plugins man lädt, desto größer wird der TypoScript Anteil des Projektes. Viele Plugins (bspw. Better Contact) lassen sich komplett über TypoScript konfigurieren.
Die Scriptsyntax lässt sich übrigens auch in seperate Dateien auslagern. Und mit SweeTs gibt es für den Editor PSPad sogar entsprechendes Syntax-Highlighting. Ähnliches liefert DEV3 – TYPO3 Enterprise Development für Eclipse. Eingebunden werden solche externen Dateien dann über:

<INCLUDE_TYPOSCRIPT: source="FILE: fileadmin/scriptdatei.ts">

Folgendes ist dabei zu beachten:

  • Im Template muss das Include in einer eigenen Zeile stehen, sonst wird es nicht erkannt.
  • Das Include wird vor dem Parsen des TS ausgeführt und funktioniert nicht mit Conditions und Verschachtelungen.
  • Die Dateigröße darf nicht größer als 100KB sein.
  • Die URL der Datei muss ausgehend von PATH_side sein, darf also kein “..” enthalten.
  • Die Dateien werden “gecacht”, d.h. Änderungen greifen nicht sofort, sondern erst nach dem Leeren des Caches, in der Installationsanleitung bei SweeTS findet man aber einen Lösungsvorschlag, um dieses Problem zu umgehen.

Genug der Vorrede, hier nun ein paar Snippets aus der Welt von TypoScript. Viel mehr davon findet man bspw. unter TYPO3 Wizard: Snippets oder bei TYPO3 snippets.

Absatzidentifizierung verbessern (die leeren A-Tags)

Verändert man die Vorgaben für CSS Styled Contents nicht, so liefert Typo3 jeden Absatz mit einem leeren A-Tag aus. Dieser Tag enthält die eindeutige Referenz für den Absatz (‘c’+UID des Datenbank Eintrages). Grundsätzlich ist dieser Ansatz ja nicht schlecht – immerhin kann man so theoretisch auf jeden einzelnen Textbaustein der gesamten Seite verweisen, aber der Code lässt sich optimieren. Zum einen sind Tags ohne Inhalt (wenn es sich nicht um selbst-schließende Tags handelt) in XHTML strict gar nicht zugelassen, zum zweiten kann es hilfreich sein jeden Absatz mit einer eigenen ID zu versehen (in Hinblick auf CSS und Javascript Spielereien). Eine ID lässt sich ebenso als Sprungmarke nutzen wie ein A-TAG mit einem NAME-Attribut. Der TypoScript code dafür:

tt_content.stdWrap.dataWrap = <div id="c{field:uid}">|</div>

Jede Seite identifizieren (Optimierung body tag)

Wohl noch häufiger als die notwendige Identifizierung jedes einzelnen Textbausteins ist die Unterscheidung verschiedener Seiten, auch wenn Sie auf ein- und demselbem Template basieren, auch hier kann man sich die UID zu Nutze machen:

page.bodyTagCObject = HTML
page.bodyTagCObject.value.field = uid
page.bodyTagCObject.value.wrap = !!<=|>

Inhalte nur umschließen, wenn nicht leer

Ein Wrap ist manchmal nur dann nötig, bzw. sollte auch nur dann stattfinden, wenn auch Inhalte vorhanden sind, die Syntax hierfür ist:

tt_content.stdWrap {
 wrap =  <div>|</div>
 required = 1
}

Inhalte aus einer anderen Spalte ausgeben

Nützlich für den Fall, dass auf bestimmten Seiten die Inhalte an anderer Stelle dargestellt werden sollen, als es “normaler” Weise in dem Template vorgesehen ist, ohne dafür ein neues Template zu erstellen. Um diese Angabe auf einen bestimmten Seitenbereich einzugrenzen kann man PIDinRootline nutzen:

#Alle Seiten unterhalb von der Seite mit der uid 1
[PIDinRootline = 1]
#Wobei die ColPos die Spalte definiert
#(Normal=0, Links=1, Rechts=2, Rand=3)
temp.boxes = CONTENT
temp.boxes {
  table = tt_content
  select {
    pidInList.field = uid
    orderBy = sorting
    where = colPos = 2
  }
}
[END]

Eine Basis Konfiguration

Ein Satz an allgemeinen Konfigurationsregeln für die Ausgabe von XHTML Seiten.

config {
   doctype = xhtml_strict
   xhtml_cleaning = all
   xmlprologue = none
   metaCharset = utf-8
   additionalHeaders = Content-Type:text/html;charset=utf-8
   htmlTag_langKey = de
   locale_all = de_DE
   language = de
   removeDefaultJS = 1
   simulateStaticDocuments = 0
   baseURL = http://www.beispiel.de
   #für den Fall das RealUrl verwendet wird
   #tx_realurl_enable = 1
}

Aufräumen bei der HTML Ausgabe

CSS Styled Contents und der RTE Parser produzieren mir persönlich zuviele Klassen und Verschachtelungen, da kann man ruhig ein wenig anpassen:

#entfernen von class="bodytext" aus den <p>-Tags
lib.parseFunc_RTE.nonTypoTagStdWrap.encapsLines.addAttributes.P >
 
#Entfernen der style Attribute (Hoehe und breite) für Bild-Wrapper
tt_content.image.20.rendering.dl.imageRowStdWrap.dataWrap =
  <div class="csc-textpic-imagerow">|</div>
tt_content.image.20.rendering.dl.oneImageStdWrap.dataWrap =
  <dl class="csc-textpic-image###CLASSES###">|</dl>
 
## Liste der Tags, die nicht mit p-Tags umschlossen werden
lib.parseFunc_RTE.nonTypoTagStdWrap.encapsLines.encapsTagList =
 div,p,pre,h1,h2,h3,h4,h5,h6,address

Navigation

Nachfolgend noch ein Beispiel für ein Navigationsmenü, hier aufgeteilt auf zwei Ebenen (oberste Navigationsebene, enthält neben dem Titel noch einen Untertitel) und einem zweiten Menü für die untergeordneten Seiten:

#Hauptnavigtion (1. Ebene)
lib.mainMenu = HMENU
lib.mainMenu.1 = TMENU
lib.mainMenu.entryLevel = 0
lib.mainMenu.1.expAll = 0
lib.mainMenu.1.noBlur = 1
lib.mainMenu.1.NO = 1
lib.mainMenu.1.NO {
  stdWrap.cObject = COA
  stdWrap.cObject {
    10 = TEXT
    10.field = title
    10.stdWrap.htmlSpecialChars = 1
    10.wrap = <strong>|</strong>&#32;
    20 = TEXT
    20.field = subtitle
    20.stdWrap.htmlSpecialChars = 1
    20.wrap = <em>|</em>
  }
  linkWrap = <li id="mm_{elementUid}">|</li>
  subst_elementUid = 1
}
 
lib.mainMenu.1.ACT = 1
lib.mainMenu.1.ACT < .lib.mainMenu.1.NO
lib.mainMenu.1.ACT.linkWrap = <li id="mm_{elementUid}" class="active">|</li>
 
#Untergeordnetes Menü 2-3 Ebene
lib.subMenu = HMENU
lib.subMenu {
  entryLevel = 1
  1 = TMENU
  1 {
    NO = 1
    NO {
     linkWrap = <li>|</li>
    }
    ACT = 1
    ACT {
      stdWrap.cObject = COA
      stdWrap.cObject {
        10 = TEXT
        10.field = title
        10.stdWrap.htmlSpecialChars = 1
        10.wrap = <strong>|</strong>
     }
     linkWrap = <li>|</li>
    }
  }
  2 = TMENU
  2 {
    wrap = <ul>|</ul>
    NO = 1
    NO {
      linkWrap = <li>|</li>
    }
    ACT = 1
    ACT {
      stdWrap.cObject = COA
      stdWrap.cObject {
        10 = TEXT
        10.field = title
        10.stdWrap.htmlSpecialChars = 1
        10.wrap = <strong>|</strong>
      }
     linkWrap = <li>|</li>
    }
  }
}

Zugegeben, auf den ersten Blick ist TypoScript sicher ein wenig gewöhnungsbedürftig, aber wer ernsthafter mit Typo3 arbeiten will, der kommt nicht umhin, sich tiefer in die Materie einzuarbeiten. Neben der bereits erwähnten TSRef findet man bei Typo3 u.a. auch noch den Artikel TypoScript Syntax and In-depth Study, ein umfassendes Tutorial zur Syntax und Funktionsweise vom TypoScript und eine mehrteilige deutschsprachige Einführung findet man beim Webworking Blog. In diesem Sinne, erfolgreiches Skripten.

Einmal T3Blog bitte – ein Selbstversuch mit der Typo3 Extension von Snowflake.ch

22. Oktober 2009

Snowflake.chDie Erweiterung T3Blog ist eine relativ neue Typo3 Erweiterung der schweizer Firma Snowflake. Soviel vorab, T3Blog ist für Typo3 Verhältnisse zweifelsfrei schon eine recht komfortable Bloganwendung, das liegt vor allem an der für Typo3 Verhältnisse recht aufgeräumten Administrationsoberfläche des Plugins. Allerdings hat auch die getestete Version 0.6.2 noch einige Kinderkrankheiten. Ein Selbstversuch.

Die Installation

Die Ausgangssituation: Typo3 Version 4.2.10, TemplaVoila basierte Seite, T3Blog in der Version 0.6.2. Nachdem ich die erforderlichen Erweiterungen typoscripttools und sfpantispam installiert hatte, lud ich die Erweiterung t3blog, auf die Erweiterung DAM (dam_1.1.1.t3x) hatte ich zunächst verzichtet. Das Manual sagt:

The extension is configured to work smoothly with the DAM. You can use it without DAM – but then you will have to reprogramm quite a few lines of TypoScript and PHP-Code.

Die Folge – nicht nur die Installation scheiterte, Typo3 ließ sich gar nicht mehr starten, stattdessen ein Fatal Error, dass eine DAM Methode nicht zu finden war, nicht gerade dass, was ich erwartet hatte. Konsequenz, den Ordner T3Blog löschen (was unter Windows XP auch noch einen Neustart erfordert, anderenfalls kann die .ttf Datei nicht entfernt werden), die temp Version unter typo3conf entfernen, DAM und t3blog erneut installieren. Ohne DAM Erweiterung sollte man es also gar nicht erst versuchen.

Die dann folgende Einbindung schien zunächst kein Problem. Dem User Manual folgend habe ich zuerst eine neue Seite angelegt (Typ Standard), und unter “Enthält Plugin” das Plugin “T3BLOG plugin” eingefügt.
Im zweiten Schritt habe ich auf dieser Seite ein neues Template angelegt und dort die erforderlichen Extensions eingebunden: “T3BLOG main configuration (t3blog)” und “T3BLOG functionalities on your website”. Auf die Erweiterungen “T3BLOG template – snowflake theme 1″ und “T3BLOG CSS – snowflake theme 1″ habe ich verzichtet, meine Idee war es, die Funktionalitäten ausschließlich über die vorhandenen Widgets zu realisieren. Um diese Idee zu testen, habe ich eine Kategorie unter T3BLOG > Kategorien angelegt und einen ersten Beitrag geschrieben. Auf der Blogseite das Plugin “blogList” als Content Element einfügt, und mir das Resultat angesehen.

Alle Einträge kommen doppelt

Der Blick auf die Vorschau – die nächste Irritation, die Einträge kamen doppelt, einmal – wie gewünscht – über das Plugin, aber auch als normales Content Element in meinem Kopfbereich, ich ging auf die Suche. Mit diesem Problem war ich wohl nicht allein. Das Konzept, dass sich ein Blogeintrag aus normalen Content Elementen zusammensetzt ist im Prinzip gar nicht schlecht und lässt beim Schreiben viele Freiheiten, gleichzeitig führt es aber zu der Dopplung aller Inhalte, insbesonders wenn TemplaVoila im Spiel ist. Die Lösungsansätze sind aber nicht gerade befriedigend. Der Vorschlag den entsprechenden Bereich einfach mittels CSS auszublenden ist sicher keine echte Lösung und der Vorschlag das ganze via TypoScript hart zu verlinken ist auch nicht der Weisheit letzter Schluss. Es musste doch noch eine einfachere Lösung geben diese Dopplung loszuwerden, ohne eine Template Kopie, ohne feste Verdrahtung. Erst ein Kommentar bei dem Artikel von Joachim Weinbrenner half weiter. Die Lösung besteht aus wenigen Zeilen Typoscript code, die direkt in das DO des Templates eingebunden werden (dort wo die doppelten Einträge erscheinen):

10 = RECORDS
10 {
	source.current = 1
	tables = tt_content
	conf.tt_content.stdWrap.if.isFalse.field = irre_parentid
}

Es werden keine Tags angezeigt

Nachdem diese ersten Hürden beseitigt waren, band ich als nächstes die anderen Widgets ein, alles lief – nur das Widget TagCloud wollte nicht, es blieb einfach leer. Ein Blick in die setup.txt des Widgets half auch nicht weiter, die Anzahl an an gleichen Tags zu erhöhen war ebenso erfolglos. Aber auch hier konnte das am Ende das Netz weiterhelfen – bei der Knowlegdebase der Universität Konstanz wurde ich fündig, es darf keine Einträge ohne Tags geben, dann entsteht ein SQL Fehler und die Cloud bleibt leer – also wirklich bei jedem Eintrag Tags einpflegen, dann funktioniert das Widget auch.
Zweites Problem, die TagCloud kommt per default mit Inlinestyles – eigentlich unverständlich – um das dauerhaft abzuschalten sollte man den PHP Code direkt verändern. Da ich ohnehin gerade im PHP Code gelandet war, habe ich die Sortierung der Tags so verändert, dass sich diese nun auch alphabetisch sortieren lassen.

// Zeile 216 auskommentieren,
// sonst wird immer ueber
// die Anzahl sortiert
//uasort($_tags, 'sortByCountValue');

Das RSS Widget

Als nächstes fügte ich dass RSS-Widget ein. Das Widget schreibt die Verweise für rss 0.91 und 2.0, ich klickte auf den Verweis – und anstelle der Verweise erschien der gesamte RSS-Code – nicht gerade dass, was ich erwartet hatte. Also gut, gemäß einer gefundenen Anleitung ergänzte ich den Typoscript Code des Templates, aber auch das brachte zunächst keine Veränderung. Mein Problem lag darin, dass ich simulateStaticDocuments aktiviert hatte -

Das RSS Widget einfach in die existente Blogseite integrieren, um dort die entsprechenden Verweise anzeigen zu können ist wohl nicht der richtige Weg. Das Widget generiert das RSS und die entsprechenden Verweise, aber öffnet man den Verweis, so erscheint die Blogseite und an der Stelle des Widgets der gesamte RSS Code. Wohl eher ein Anwenderfehler als etwas, was man der Software ankreiden sollte, also zweiter Versuch, diesmal seperate Seite, nur das RSS eingebunden.

Chaotische Inhalte

Soweit vorgedrungen fing ich an echte Inhalte in den Blog zu pflegen, aus einfachen Lorem Ipsum Einträgen wurden reale Posts, die z.T. aus vielen einzelnen Content Elementen bestanden. Ein Blick auf die Ausgabe, und ich stand vor der nächsten Hürde, die einzelnen Content Elemente waren zwar dem richtigen Eintrag zugeordnet, aber entsprachen nicht der eingepflegten Reihenfolge, zunächst dachte ich an einen Eingabefehler, sortierte um, gab den nächsten Eintrag ein, und wieder waren alle alten Einträge im chaotischem Zustand. Die nächste Recherche – auch hier liegt das Übel im mangelhaften Zusammenspiel zwischen T3blog und TemplaVoila.
TemplaVoila nutzt zur Sortierung eben nicht das Feld sorting – daher das entstandene Chaos. Johannes Dörr beschreibt in seinem Blog einen Workaround, der dieses Problem behebt. Allerdings erzwingt diese Lösung eine Änderung in einer TemplaVoila PHP Klasse, was für künftige Updates sicher nicht der optimale Weg ist. Aber – um mit Napoleon zu sprechen – Wenn man schon dummes tut, dann sollte es wenigstens funktionieren – jetzt sollte dieser Blog endlich vernünftig laufen. Habe die Änderungen vorgenommen, und das Chaos innerhalb der Blogeinträge hatte ein Ende.

function correctSortingAndColposFieldsForPage($flexformXML, $pid) {
  // Ergaenzung - disable recording...
  require_once(PATH_t3lib . 'class.t3lib_page.php');
  require_once(PATH_t3lib . 'class.t3lib_tsparser_ext.php');
  $sysPageObj = t3lib_div::makeInstance('t3lib_pageSelect');
  $rootLine = $sysPageObj->getRootLine($pid);
  $TSObj = t3lib_div::makeInstance('t3lib_tsparser_ext');
  $TSObj->tt_track = 0;
  $TSObj->init();
  $TSObj->runThroughTemplates($rootLine);
  $TSObj->generateConfig();
  $arr = split(
    ',',
    $TSObj->setup['config.']['tx_templavoila.' ['disableReordering']
  );
  if (in_array($pid , $arr)) {
    return;
  }
  // Ende der Ergaenzung
  [... urspruenglicher Methodeninhalt]
}

Die Erweiterung der Funktion schaltet – abhängig von einem gesetzten config Wert, die Sortierung in TemplaVoila ab – es muss also noch per typoscript der entsprechende Wert gesetzt werden.

#der wert entspricht der blogseite
config.tx_templavoila.disableReordering = 15

Die Artikelnavigation läuft nicht…

Drei, vier, fünf Einträge eingestellt, und das nächste Problem. Die Artikelnavigation (nächster Artikel, vorheriger Artikel, zur Übersicht) funktioniert nicht richtig. Bei den ersten Einträgen ist noch alles in Ordnung, aber bei Einträgen, die nicht auf der ersten Seite liegen, werden die Navigation gar nicht angezeigt.
Der Fehler entsteht bei der Zusammensetzen eines Verweises für die Detailansicht, es fehlt der Parameter tx_t3blog_post_pointer. Dieser Parameter enthält die aktuelle Seitenlaufnummer, und wird beim Zusammensetzen der SQL Anfrage als Berechnungsgrundlage für das LIMIT genutzt. Eigentlich kein Problem – die entsprechenden Zeilen im Typoscript werden um die folgende Angabe ergänzt:

#Ergaenzung um den Parameter der Seitenlaufnummer
&#038;tx_t3blog_post_pointer={GPvar:tx_t3blog_post_pointer}

Aber wie sieht es mit den Verweisen für den Permalink und dem Trackbacklink aus? – Hier die Seitenzahl mitzulieferen wäre Blödsinn, sie ändert sich mit jedem neuen Artikel, und lässt man den Parameter weg, so erscheint die Artikelnavigation nicht. Es half nichts, wenn die Artikel-Navigation sauber funktionieren sollte, musste die Funktion umgeschrieben werden. Die geänderte Methode getSingleNavigation kommt ohne Pointer aus, stellt nur eine Abfrage an die Datenbank und das Ergebnis muß zudem auch durch keine PHP-Schleife geleitet werden, um die korrekten Einträge zu finden – durchaus ein Gewinn – auch wenn diese Syntax so nur mit MySQL funktioniert:

function getSingleNavigation($current)	{
    include_once('class.listFunctions.php');
    // das sql statement mit subselects fuer previous und next
    $sql = 'SELECT uid,title,@a:=date ,
    ( SELECT CONCAT_WS( \'|\',uid,title,crdate )
      FROM tx_t3blog_post
      WHERE date < @a
      ORDER BY date DESC
      LIMIT 1
    ) AS previous,
    ( SELECT CONCAT_WS( \'|\',uid,title,crdate )
      FROM tx_t3blog_post
      WHERE date > @a
      ORDER BY date ASC LIMIT 1
    ) AS next
    FROM tx_t3blog_post
    WHERE uid='.$current;
 
  // datenbank abfrage...
  $RES = $GLOBALS['TYPO3_DB']->sql(TYPO3_db,$sql);
  $singeNavigationItems = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($RES);
 
  //schreiben der verweise, inhaltlich aendert sich hier nicht mehr viel..
  if(!empty($singeNavigationItems['previous'])) {
    // trennen der ermittelten previous eigenschaften
    $temp = explode('|',$singeNavigationItems['previous']);
    // ab hier wie vorab
    $title = $temp[1];
  	if(strlen($title)>28){
  		$title = substr($title,0,25).'...';
  	}
    $data['previous'] = $this->getTitleLinked(
      $title, $temp[0], $temp[2], 'singleNavTitleLink',$temp[1]
    );
  }
  if(!empty($singeNavigationItems['next'])) {
    // trennen der ermittelten previous eigenschaften
    $temp = explode('|',$singeNavigationItems['next']);
    // ab hier wie vorab
    $title = $temp[1];
  	if(strlen($title)>28){
  		$title = substr($title,0,25).'...';
  	}
    $data['next'] = $this->getTitleLinked(
      $title, $temp[0], $temp[2], 'singleNavTitleLink',$temp[1]
    );
  }
  $data['backId'] = t3blog_div::getBlogPid();
  $data['backText'] = $this->pi_getLL('backText');
	//schreiben der navigation...
  return t3blog_div::getSingle($data, 'singleNavigation');
}

Und so klappt es dann auch mit den Nachbarn – egal auf welcher virtuellen Seite sich der Eintrag befindet.

Fazit

Die Tatsache, dass sich fast alle Bestandteile des Blogs über Typoscript ändern bzw. konfigurieren lassen ist gar nicht schlecht, aber nichts für Einsteiger. Zumindest eine Flexform-Maske für die Basiseinstellungen (bspw. Anzahl von Einträgen auf einer Seite, Anzeige Captcha, Tags usw.) würde den Komfort noch deutlich erhöhen. Schade ist auch, dass die Widget / Typoscript Idee dann eben doch nicht vollständig durchgehalten wurde. So hatte ich vor, die bereits erwähnte Artikelnavigation von oben nach unten zu verlegen, ging nicht – SingleNavigation und Artikel werden in der PHP Klasse zusammengezogen, arbeitete ich mit dem ###MORE### Platzhalter, um auf der Übersicht die Länge des Artikels einzugrenzen, so fand ich ein <br /> der Text und Verweis trennte – auch hier half nur ein Eingriff auf PHP Ebene.
Erstaunlich fand ich, dass keine Möglichkeit vorgesehen war, sich die Tags eines Eintrages ausgeben zu lassen, es wurden ausschließlich die Kategorien genutzt. Die mangelhafte Logik zur Ermittlung von vorherigen und nächsten Einträgen ebenso.
Auf die Unverträglichkeit mit TemplaVoila sollte dann doch explizit hingewiesen werden, damit man in einem solchen Typo3 Szenario lieber eine andere Alternative sucht.
Mir persönlich gefällt die JavaScript-Lastigkeit einiger Widgets nicht (z.B. das Artikel Archiv), allerdings ließen sich diese Dinge recht einfach beseitigen.
Eine Konkurenz zu ausgewachsenen Blogsystemen wie Wordpress ist T3Blog, oder andere Blog Extensions wie bspw. timtab aber noch lange nicht. Für den professionellen Bereich ist daher eine Kombination verschiedener Systeme sicher vorzuziehen.