Skip to main content

Liferay 6.2 Web-Content mit Transformerlistener zur Laufzeit manipulieren

Herausforderung

Wer in Liferay mit Web-Content arbeitet, kommt dabei zwangsläufig mit Strukturen und Vorlagen in Berührung. Mittels Strukturen wird festgelegt, welche Inhalte ein Redakteur im CMS erstellen kann. Die Vorlage, die einer Struktur zugeordnet ist, entscheidet wie diese Inhalte dargestellt werden. Dadurch lassen sich Teile eines Web-Content prominenter gestalten (z.B. Überschriften, Info-Kästen, Links zu weiterführenden Inhalten, …), aber auch Teile komplett ausblenden. Beispielsweise wäre es für einen Teaser nur notwendig, eine Überschrift, ein Thumbnail, einen Textauszug und einen Link zur Hauptdarstellung bzw. Inhaltsseite anzuzeigen.

Die Implementierung und Anwendung einer Vorlage ist sehr einfach. Eine neue Vorlage wird im Kontrollbereich von Liferay angelegt, bearbeitet, abgespeichert und anschließend bei einem Web-Content-Display Portlet zugeordnet. Muss etwas an der Darstellung verändert werden, wird einfach nur die Vorlage bearbeitet. Die Struktur mit den eigentlichen Inhalten bleibt erhalten.

In einigen unserer Projekte sahen wir uns jedoch mit der Anforderung konfrontiert, dass bestimmte Vorlagen zur Laufzeit (damit ist der Zeitpunkt gemeint, an dem ein Nutzer eine Seite aufruft und Liferay sämtliche Inhalte lädt) zusätzlichen dynamischen Inhalt anzeigen müssen.
Ein Beispiel ist eine Artikelvorlage, die eine „Linkbox“ anzeigt. Eine Linkbox ist eine Komponente, die Links zu weiterführenden oder ähnlichen Artikeln darstellen soll. Nun können diese Artikel aber abgelaufen oder im Zuge von Aufräumarbeiten gelöscht sein. Die Linkbox würde die Links jedoch immer noch anzeigen.
Ein anderes Beispiel: In einer Vorlage wird eine HTLM5-Video Komponente eingebunden. Bestimmte Parameter, wie z.B. die Video-Ressource und ein Sicherheitstoken, werden dynamisch eingebunden.

Die Lösung mithilfe der Liferay Transformer-Engine

Im folgenden möchte ich beschreiben, wie wir die Anforderung, Inhalte zur Laufzeit zu manipulieren, mittels der Transformer-Engine umsetzen konnten.

Wo erzeugt Liferay die Web-Content Ausgabe?

Die erste Herausforderung war es herauszufinden, wo der Prozess zur Generierung der Web-Content Ausgabe beginnt. Nach einigem Suchen fanden wir folgende Zeile in der JournalArticleLocalServiceImpl Klasse:

content = JournalUtil.transform(themeDisplay, tokens, viewMode, languageId, xml, script, langType);

Mit diesem Funktionsaufruf wird der rohe, unformatierte Web-Content in das Format umgewandelt, welches der Nutzer anschließend im Browser sieht. Hervor stechen dabei folgende Parameter:

  • tokens: Eine Map<String, String> welche die Variablen speichert, die in der Vorlage aufgelöst werden müssen. Wenn z.B. eine Velocity-Vorlage eine Variable $reserved-article-id auflöst und als Zahl darstellt, dann nur weil es einen Token mit den Namen „reserved-article-id“ gibt.
  • xml: Das Roh-Format des Web-Content
  • script: Der Inhalt der Vorlage
  • langType: Eine Liferay Konstante, die für die verwendete Script-Sprache der Vorlage steht

Diese Parameter werden weiter gereicht, bis etwas später die Klasse com.liferay.portal.templateparser.Transformer erreicht wird. Diese implementiert die Logik zur Anwendung einer Vorlage auf einen Web-Content.

Wie kann die Ausgabe manipuliert werden?

Im nächsten Schritt analysierten wir den Transformer und fanden heraus, dass er vor und nach Erzeugung der Ausgabe Zugriffsmöglichkeiten für TransformerListener definiert. TransformerListener werden in der portal.properties über die Property journal.transformer.listener definiert. Die Reihenfolge mit der Listener in den Properties definiert werden, bestimmt dabei die Reihenfolge, mit der sie im Transformer angewendet werden.
Ein Transformerlistener implementiert das Interface com.liferay.portal.kernel.templateparser.TransformerListener und damit auch folgende Methoden:

  • onXml(..): Bearbeitung des rohen Web-Content, bevor der Transformer die Ausgabe generiert
  • onScript(..): Bearbeitung der Web-Content Vorlage, bevor der Transformer die Ausgabe generiert
  • onOutput(..): Bearbeitung der Ausgabe, nachdem der Transformer diese generiert hat

Was alle Methoden gemeinsam haben ist der Zugriff auf die Tokens. Mit ihnen lässt sich steuern, welche Variablen der Vorlage zur Verfügung stehen. Allerdings mit einer Beschränkung: In Tokens lassen sich nur Text-Werte, nicht jedoch komplexere Objekte, wie ModelBeans, speichern. Hierfür gibt es jedoch einen Workaround.

Komplexe Objekte in eine Vorlage einschleusen

Für diese Anforderung wird die Methode „onOutput(..)“ verwendet. In ihr erhalten wir Zugriff auf die finale Ausgabe, also das Ergebnis der Anwendung einer Vorlage auf einen Web-Content. Der Workaround ist, diese Ausgabe nicht als final zu betrachten, sondern als eine neue Vorlage. Warum das so ist veranschaulicht dieser Codeblock:

@Override
public String onOutput(String output, String languageId, Map<String, String> tokens) {
  try{
    // Zuerst wird eine neue TemplateResource erzeugt.
    // Der erste Parameter ist eine ID und für den weiteren Verlauf nicht von Interesse.
    // Der zweite Parameter ist der Inhalt der Vorlage. Der Trick ist, hier die Web-Content Ausgabe als Vorlage wiederzuverwenden.
    TemplateResource templateResource = new StringTemplateResource("randomTemplateId", output);

    // Als nächstes wird aus der TemplateResource und mit Angabe der verwendeten Script-Sprache das Template erzeugt.
    Template template = TemplateManagerUtil.getTemplate(TemplateConstants.LANG_TYPE_VM, templateResource, false);

    // Nun werden alle Token dem Template Kontext hinterlegt.
    for (String key : tokens.keySet()) {
      template.put(key, tokens.get(key));
    }

    // Im Gegensatz zu den Tokens, die nur Textwerte enthalten können,
    // kann der Template Kontext auch komplexere Objekte, z.B. DAOs, speichern.
    template.put("exampleDAO", new ExampleDao());

    // Zum Schluss wird das gefüllte Template ausgewertet und als Text zurückgegeben.
    StringWriter sw = new StringWriter();
    template.processTemplate(sw);
    return sw.toString();
  } catch (Exception e) {
    LOG.error("Error parsing Template", e);
  }
}

Fazit

TransformerListener sind eine elegante Möglichkeit die Ausgabe von Web-Content zu manipulieren. Ihnen zugute kommt die Tatsache, dass sie sich via Hooks umsetzen lassen und nicht als aufwändige Ext-Plugins eingebunden werden müssen. Allerdings ist der Weg zu dieser Erkenntnis unnötig kompliziert, da Liferay die API leider nicht dokumentiert hat. Wir sind sehr gespannt wie es sich in Zukunft bei Liferay 7 verhält. Hier hat mein Kollege Andreas auf der Liferay DevCon 2015 zumindest mitgenommen, dass die Dokumentation für Entwickler und auch die Beschreibung von Änderungen in den Liferay Versionen zukünftig deutlich verbessert werden soll.

Eine Alternative Lösung wäre, eine oder mehrere Helperklassen zu implementieren und diese per Spring in die Vorlage zu injizieren. Hierzu gibt es einen passenden Artikel im Liferay-Blog von Peter Mesotten.

Weiterführende Infos:

Wünschen Sie weitere Details oder haben Sie konkrete Fragen?

Vereinbaren Sie einfach einen Beratungstermin mit uns. Gerne präsentieren wir Ihnen den Funktionsumfang von Liferay in einer Live-Demo.

 

Über den Autor

empulse Team

Unser kompetentes und erfahrenes Team von (Java-)Entwickler ist stark in Beratung, technischer Konzeption und zuverlässiger Umsetzung komplexer Projekte. In unseren Reihen haben wir Spezialisten für unterschiedliche Themengebiete, die hier ihr Fachwissen zum Besten geben.