Türchen 03: Magento Template Filter

03.12.2011   //   von Bastian Ike   //   Adventskalender, Magento  //  6 Kommentare

Jeder, der sich schonmal Magento angeguckt hat, kennt diese Konstrukte mit {{ und }} in CMS-Seiten, -Blöcken, E-Mails. Ich bin einfach so frei zu behaupten das sich die meisten schon ziemlich damit rumgeschlagen haben, ohne genau die Funktionalität, und vor allem die Erweiterbarkeit, dieser Filter zu kennen.

Nach dem tollen Einstieg in den Adventskalender möchte ich in diesem Türchen also die Funktion und das Erweitern des Template-Filters in Magento erklären. Anfangs die Funktionsweise, danach das Erweitern und am Ende einen Überblick was uns Magento schon im Standard zur Verfügung stellt.

Zu aller erst einen kurzen Blick auf den Template-Filter: Wir können in E-Mails, CMS-Seiten und weiteren Stellen Platzhalter einbauen die z.B. durch bestimmte Blöcke, URLs oder (in E-Mails) Kundendaten ersetzt werden. Der Syntax dafür ist super einfach: {{platzhalter argumente…}}.

Als Beispiel nehmen wir einfach mal {{var customer.email}} oder {{store url=”checkout/cart”}}. Das erste würde zum Beispiel in E-Mail-Templates durch die E-Mail-Adresse des Kunden ersetzt. Das zweite könnte einen Link auf /checkout/cart/ erzeugen.

Funktionsweise

Das Herzstück der ganzen Angelegenheit ist die Klasse Varien_Filter_Template die Zend_Filter_Interface Die damit  die Funktion filter($value)) implementieren muss. Varien_Filter_Template::filter($value) verarbeitet zuerst {{if…}} und {{depend…}} Anweisungen (bzw. Direktiven), und danach alles, was auf {{([a-z]{0,10})(.*?)}} passt. Dabei wird so vorgegangen das mit is_callable geprüft wird ob $1 (die ersten 0-10 Zeichen) mit dem Zusatz “Directive” aufgerufen werden kann. Sollte dies der Fall sein wird die Funktion mit dem Argument $2 aufgerufen, und der Rückgabewert in der Ausgabe eingefügt.

Nutzung in E-Mails und CMS-Seiten

E-Mails nutzen Mage_Core_Model_Email_Template_Filter welcher von Varien_Filter_Template erbt und einige weitere Funktionen (Magento-Spezifisch) zur Verfügung stellt. CMS-Seiten und Blöcke nutzen Mage_Widget_Model_Template_Filter welches wiederum von Mage_Cms_Model_Template_Filter und dieser vom E-Mail-Filter erbt, allerdings von der Funktionalität nichts weiter bietet, außer das noch die widgetDirective() hinzugefügt wurde. Welcher Filter genau für CMS-Elemente genutzt wird ist übrigens in der Konfiguration in global/cms/page/tempate_filter und global/cms/block/tempate_filter definiert.
Achtung: tempate ist kein Schreibfehler von mir, sondern von Magento ;-)

An dieser Stelle könnten wir jetzt ansetzen und uns ein eigenes Model bauen: Test_Filter_Model_Template_Filter soll von Mage_Widget_Model_Template_Filter erben und per config.xml den Wert von global/cms/page/tempate_filter auf filter/template_filter setzen.
Danach können wir z.B. eine Methode mit dem Namen testDirective($construction) definieren die dann durch {{test}} aufgerufen wird.

public function testDirective($construction) {
	return 'Es ist ' . date('H:i:s') . ' ' . print_r($construction, 1);
}

Einfach mal einbauen und {{test foo bar}} in eine CMS-Seite schreiben.

Wenn wir jetzt z.B. in Produktbeschreibungen auch diese Filter nutzen möchten reicht es im entsprechenden Template folgendes zu platzieren:

// aus
echo $_product->getDescription();
// wird ein
echo Mage::helper(‘cms’)->getPageTemplateProcessor()->filter($_product->getDescription());

// bei Bedarf natürlich eleganter, aber das Prinzip dürfte klar sein

So können wir bei Bedarf auch Widgets in Produktbeschreibungen unterbringen oder vernünftige URLs oder oder oder .. der Fantasie sind keine grenzen Gesetz :)

Verschiedene Direktiven

Wir wissen jetzt wie die Filter im groben funktionieren, wie wir eigene Direktiven einbinden und diese auch woanders nutzen können.Doch was gibt es überhaupt out-of-the-box von Magento? Die wichtigsten will ich hier einmal erläutern:

varDirective

Die varDirective dient dem Zugriff auf Variablen. Diese können z.B. über setVariables(array $variables) dem Template-Filter übergeben werden. Für E-Mails gibt es als Beispiel die Mage_Sales_Model_Order::sendNewOrderEmail(…) Funktion:

$mailer->setTemplateParams(array(
    'order'        => $this,
    'billing'      => $this->getBillingAddress(),
    'payment_html' => $paymentBlockHtml
));

Im E-Mail-Template könnte man jetzt z.B: mit {{var order…}} auf das aktuelle Mage_Sales_Model_Order-Objekt zugreifen. Beispielsweise {{var order.getCustomer().getAddress().format(‘html’)}} oder alternativ {{var order.customer.address.format(‘html’)}}. So kommt man an alle wichtigen Informationen die man irgendwo einbauen möchte.

widgetDirective

wird genutzt um Widgets einzubinden. Die Benutzung lernt man am einfachsten über den Assistenten im WYSIWYG-Editor.

blockDirective

Schonmal den Wunsch gehabt einfach einen Block in einer CMS-Seite einzubinden? Hier ist die Lösung:
{{block type=”catalog/product_list” id=”cms_product_list” output=”toHtml” category_id=”3”}}
Die Parameter type, id und output werden für die Erstellung und das anzeigen des Blocks genutzt, alle andere Parameter werden mit setDataUsingMethod($paramter, $value) auf den Block gesetzt.

configDirective

Unter Umständen möchte man Werte aus der Konfiguration anzeigen, zum Beispiel die Store-Owner-Daten. {{config path=”path/to/xml/node”}} gibt dafür den entsprechenden Wert zurück.

htmlescapeDirective

Zum Schutz vor XSS kann man mit {{htmlescape var=”gefährlich<h1>”}} Strings maskieren. Nützlich z.B. auch für E-Mail-Adressen u.ä.

mediaDirective / skinDirective

Im Prinzip dieselben Funktionen zum ermitteln der entsprechenden URLs für das skin und media Verzeichnis.
Als Parameter wird jeweils url genutzt: {{media url=”somepicture.png”}}

storeDirective

Eigentlich sollte storeDirective urlDirective heißen, denn genau das macht sie: eine URL zurückgeben:
{{store url=”somewhere”}} Wer sich immer schon bei Sachen wie {{store url=”somewhere.html”}} über das / am Ende geärgert hat sollte übrigens mal {{store direct_url=”somewhere.html”}} ausprobieren statt das (leider) oft genutzte {{store url=””}}somewhere.html.

Fazit

Ich persönlich finde die Filter ziemlich mächtig. Das einfache hinzufügen eigener Funktionen macht das ganze nur noch schöner. Auf jeden Fall lohnt sich auch nochmal ein Blick in die entsprechenden Klassen um selber ein Bild davon zu bekommen wie der Code aussieht. Trotzdem hoffe ich hiermit eine gute Einführung gegeben zu haben. In dem Sinne: schonmal einen schönen 2. Advent und bis zum nächsten Türchen :-)

P.S.: Noch ein Tipp aus Sicht der Sicherheit: Lasst niemals Daten von dritten durch den Template-Filter (z.B. durch Erweiterungen für Kundenkommentare, Schlagwörter, o.ä.). Der einfachste Weg so etwas auszunutzen wäre das Auslesen von Datenbankkonfiguration u.ä. per configDirective, ein etwas erfahrener Angreifer könnte u.U. auch eigenen Code ausführen!

Der Autor

Bastian Ike hat Mitte 2010 mit Magento begonnen und arbeitet bei der Firma Hucke Media aus Oldenburg. Gelegentlich bloggt er unter thebod.de und moderiert den Security-Bereich im Forum auf magentocommerce.com.

6 Kommentare

  • Ich finde den Beitrag übrigens super spannend – hatte mich bis dato nicht so intensiv mit der Problematik beschäftigt, danke!

  • Danke :)

    Wenn noch jemand anderes Feedback hat: immer her damit! :)

  • Erst mal sind alle im Wochenende :)

  • Super, vielen Dank für den Post! Bin begeistert eine gute Referenz zu haben auf die in Zukunft bei Bedarf verwiesen kann :)
    Vielleicht hast Du ja Lust noch was in einem neuen Post zu schreiben zu der Verwendung von {{if …}} und {{depends …}}? Dazu werden ja auch öfter Fragen gestellt… :)

  • Ui so viel darf ich vertraten Vinai: Es kommt noch ein Post der auf dem Thema hier aufbaut :)

  • [...] Ike hat im Türchen 03 bereits Magento Template Filter behandelt. Ich möchte mich für die Vorarbeit bedanken und nutze die Gelegenheit, auf die [...]

Kommentar schreiben

eMail-Benachrichtigung bei weiteren Kommentaren.
Auch möglich: Abo ohne Kommentar.

Magento-Support

Sie benötigen kurzfristig Unterstützung in einem Magento-Projekt oder möchten eine individuelle Extension einsetzen? Sprechen Sie uns an.