top

Modulentwicklung unter Silverstripe

Um ein Silverstripe-Modul zu entwicklen, gibt es zwei Herangehensweisen:

  1. Soll eine use case-spezifische "one-off" Erweiterung eines Moduls vorgenommen werden (so z.B. geschehen, um diesen Blog um Lernkompetenzen zu erweitern), können Extensions geschrieben werden. Hiermit können bestehende Objekte, ohne das Modul an sich zu verändern, um Datenbankfelder und Methoden ergänzt werden. Es können sogar bestehende Methoden angepasst werden, wenn dies vom Modulentwickler vorgesehen wurde.

    Extensions in Silverstripe werden umgesetzt, indem die Klassen DataExtension (für DataObjects) bzw. Extension extended werden (siehe Dokumentation). Mithilfe von Magic wird dann die Funktionalität der Zielklassen erweitert.
  2. Sind die Änderungen grösserer Natur oder sollen sie in anderen Projekten wiederverwendet werden, kann es sich lohnen, direkt am Modul zu arbeiten. Sind sie allgemein nützlich, können die Änderungen in die Allgemeinheit zurückfliessen, oder auch nur privat bzw. intern verwendet werden.

    Hier dokumentiert wird die Arbeit mit dem zweiten Ansatz an silverstripe-sendy, einem open source-Modul für Silverstripe. Die Arbeit wird dementsprechend auch der Allgemeinheit zugänglich gemacht.

Modul & Aufgabenstellung

Das Modul silverstripe-sendy dient als Schnittstelle zwischen dem Silverstripe-basierten CMS und dem Newsletterdienst Sendy. Im CMS kann mithilfe von Elemental-Blöcken eine Mail zusammengestellt und dann in Sendy exportiert werden.

Für das Rendern des übergeordneten Bodies, der die Blöcke umgibt, kann aktuell nur ein Template verwendet werden. Um ein anderes Template zu verwenden, muss man die Template-Datei ersetzen. Bei regelmässigen Wechseln ist dies mühsam und fehleranfällig.

Das Modul soll also so weiterentwickelt werden, dass in der Konfiguration mehrere Templates definiert und im CMS ausgesucht werden können. Das streamlined den Prozess erheblich.

In einem zweiten Schritt wird ein zweites Problem angegangen: Da man sich in E-Mails bestenfalls ausschliesslich auf Inline-CSS verlassen sollte, muss auch das Styling für die Elemental-Blöcke modifiziert werden. Statt externe Stylesheets zu laden, sollen auch die Blöcke auf Templates zurückgreifen, die nach Vorgabe benannt und abgelegt wurden.

Implementierung mehrerer Styles für Newsletter

Das Modul silverstripe-sendy enthält eine Klasse SendyCampaign, zu der unter anderem die Elemental Area für die Inhaltsblöcke gehört und die anhand eines Templates gerendert wird.

Nach aktuellem Stand verwendet die Render-Methode starr ein Template, dessen Name dem Klassennamen entspricht - also SendyCampaign.ss, falls keine Extension vorliegt. Die entsprechende Methode im Code der Klasse:

                
// SendyCampaign.php

public function getHTMLNewsletter()
{
$template = SSViewer::create(self::class);
// [...]
return $this->renderWith($template);
}

Um die Verwendung verschiedener Styles zu ermöglichen, gehen wir folgendermassen vor:

  1. SendyCampaign braucht ein Datenbankfeld, um eine entsprechende Einstellung speichern zu können.
  2. Die Styles müssen erstens in der SilverStripe-Config genannt werden. Die Klasse muss diese Konfiguration aber auch auslesen und verarbeiten.
  3. Im CMS muss eine Auswahl aus den konfigurierten Styles getroffen werden können.
  4. Die statische Methode SSViewer::create muss mit dem enstprechenden Style aufgerufen werden.

Das Datenbankfeld für Schritt 1 ist schnell hinzugefügt:

                
// SendyCampaign.php

/**
* Database fields
* @config
* @var array
*/
private static $db = [
'Title' => 'Varchar',
'FromName' => 'Varchar',
'FromEmail' => 'Varchar',
'ReplyToEmail' => 'Varchar',
'Subject' => 'Varchar',
'IsTransferred' => 'Varchar',
'Style' => 'Varchar' // neuer Eintrag
];

Die Schritte 2 & 3 lösen wir gemeinsam.

Eine SilverStripe-Config wird im YAML-Format geschrieben. Hier lagern wir unter dem Key styles eine Liste von key-value-Paaren. Der Key verweist auf den Dateinamen des Styles, der Wert repräsentiert den front-facing name für das CMS. Testweise sieht das so aus:

                
# app.yml

Syntro\SilverStripeSendy\Model\SendyCampaign:
styles:
red: Red Style
blue: Blue Style

Weiters legen wir eine statische Variable styles an, welche die ausgelesenen Styles beinhaltet. Um sicherzustellen, dass wir beim Aufrufen der Bearbeitung im CMS auf dem neusten Stand sind, rufen wir die Config innerhalb der getCMSFields()-Methode auf und befüllen mit ihr die styles-Variable.

Anschliessend prüfen wir, ob überhaupt styles konfiguriert sind. Falls ja, wird ein Dropdown-Feld zur Auswahl eingeblendet. Die Auswahl wird dann in das DB-Feld Style geschrieben. Ausserdem konfigurieren wir einen "empty default" und stellen sicher, dass kein Feld angezeigt wird, wenn nichts konfiguriert ist. Der user facing-String für den "empty default" wird mit einer hier nicht näher zu erklärenden Übersetzungslösung angezeigt.

                
// SendyCampaign.php

/**
* Holds styles (format: <template name> => <human name>)
*
* @config
* @var array
*/
private static $styles = [];

// [...]

public function getCMSFields()
{
// [...]
$styles = $this->config()->get('styles');

if (count($styles) > 0) {
$fields->addFieldToTab(
'Root.Main',
$styleDropdownField = DropdownField::create('Style', 'Style', $styles),
'ElementalArea'
);
$styleDropdownField->setHasEmptyDefault(true);
$styleDropdownField->setEmptyString(_t("DNADesign\\Elemental\\Models\\BaseElement".'.CUSTOM_STYLES', 'Select a style..'));
} else {
$fields->removeByName('Style');
}
}

Nun müssen wir den Rendering-Code anpassen.

Wir ergänzen zuerst die Klasse um eine Methode getStyles(). Sie soll uns anhand des DB-Feldes Style einen Array zurückgeben, den wir wiederum als Argument an SSViewer::create verwenden können. Wir stellen dabei sicher, dass der Output bei nicht gesetzem DB-Feld dem üblichen Output entspricht. Beim gesetzten Feld hingegen stellen wir den gewählten Style an die erste Stelle des Arrays. Darauf folgt, um einen Fallback zu implementieren, der übliche Style als zweite Wahl.

Schliesslich müssen wir die neue Methode in der getHTMLNewsletter()-Methode einbauen.

Das Ergebnis: Wir können an der gewohnten Stelle (/app/templates/Syntro/SilverStripeSendy/Model/) Styles im Format SendyCampaign_<style>.ss lagern. Sind sie korrekt konfiguriert, können sie im CMS ausgewählt werden und werden dann geladen. Bei Konfigurationsfehlern oder nicht entsprechend abgelegtem Style wird auf den Default SendyCampaign.ss zurückgegriffen.

                
// SendyCampaign.php

/**
* If no entry in Style field, return default style
* If there is entry in Style field, return that with default fallback
*
* @return array
*/
public function getStyles()
{
$styles = [];
if (!($this->Style)) {
array_push($styles, self::class);
} else {
array_push(
$styles,
self::class."_".$this->Style,
self::class
);
}

return $styles;
}

// [...]

/**
* getHTMLNewsletter - returns the HTML render of the Newsletter
*
* @return DBHTMLText
*/
public function getHTMLNewsletter()
{
$template = SSViewer::create($this->getStyles());
$template->includeRequirements(false);
return $this->renderWith($template);
}