Testen Sie Scrivito 30 Tage lang kostenlos
Testen Sie Scrivito 30 Tage lang kostenlos

Seiten und Widgets mit Massenoperationen migrieren

Seiten und Widgets mit Massenoperationen migrieren

Wann immer es zu zeitaufwendig wird, bei mehreren Seiten eine Eigenschaft mit Hilfe der Bearbeitungsschnittstelle zu ändern, ist es überlegenswert, hierfür ein Skript über die JavaScript-Konsole auszuführen. In diesem Artikel geben wir Ihnen ein paar Beispiele dafür, wie man eine beliebige Anzahl Seiten oder Widgets mittels Massenoperationen modifizieren kann. Als Ausgangsbasis dient uns hierbei die Example App, sodass Sie mit dem Code experimentieren und ihn nach Ihren Wünschen anpassen können.

Tags zu Seiten hinzufügen

Nehmen wir einmal an, dass Ihre Scrivito-basierte Website mit einem Blog ausgestattet ist und Sie Blogposts als solche in Navigationen, auf Suchergebnisseiten und dergleichen kennzeichnen möchten. Es ist naheliegend, dies mit Hilfe des Tags „Blogpost“ umzusetzen, das Sie nun jedem Blogpost zuweisen möchten. Dies können Sie mit einer einzigen (zugegebenermaßen etwas längeren) Anweisung in der JavaScript-Konsole erreichen:

  • Legen Sie eine Arbeitskopie an oder wählen Sie eine aus,
  • öffnen Sie die Konsole,
  • wählen Sie den Kontext scrivito_application
  • und führen Sie diesen Code aus:
Scrivito.load(() => { return [...Scrivito.getClass('BlogPost').all()]; }).then(objs => { objs.forEach(o => { console.log(`Touching ${o.id()}...`); o.update({ tags: [...o.get("tags"), "Blogpost"] }); }); console.log("Done tagging all blogposts."); });

Wir verwenden Scrivito.load, um alle CMS-Objekte der Klasse BlogPost abzurufen. Scrivito.load stellt sicher, dass das Scrivito SDK die angeforderten Objekte bereits vollständig (asynchron) geladen hat, bevor auf sie zugegriffen wird. Nachdem das zurückgegebene Promise gelöst wurde, wird unser Code zur Änderung der Objekte ausgeführt. In diesem Beispiel iterieren wir das Array objs, um das tags-Attribut (eine stringlist) eines jeden ermittelten Objekts um das “Blogpost”-Tag zu ergänzen.

Der obige Code eignet sich bestens als Vorlage für alle möglichen Massenoperationen, weil seine beiden Hauptbestandteile – die Suche nach den relevanten Seiten sowie der für jede von ihnen auszuführende Code – leicht angepasst werden können, um ähnliche und auch anspruchsvollere Anwendungsfälle zu lösen.

Um zu prüfen, ob Ihr Code wie vorgesehen funktioniert hat, können Sie im Content Browser dem obigen Beispiel entsprechend nach dem betreffenden Tag suchen oder in der JavaScript-Konsole eine Suche wie die folgende ausführen:

[...Scrivito.Obj.where('tags', 'equals', 'Blogpost')]

Tags von Seiten entfernen

Sollten Sie einmal versehentlich ein Tag oder mehrere Tags bestimmten Seiten zugewiesen haben und nun entfernen wollen, können Sie einfach den Wert der tags-stringlist durch eine gefilterte Version ihrer selbst ersetzen:

Scrivito.load(() => { return [...Scrivito.getClass("BlogPost").all()]; }).then(objs => { objs.forEach(o => { console.log(`Touching ${o.id()}...`); o.update({ tags: [ ...o.get("tags").filter(t => { return t !== "Blogpost"; }) ] }); }); console.log("Done untagging all blogposts."); });

Um Attribute eines anderen Typs als stringlist zu ändern, siehe Updating attributes im SDK cheat sheet.

Widgets ändern, ersetzen oder löschen

Der Wunsch, Massenoperationen auch für Widgets ausführen zu können, ist meist durch die Anforderung motiviert, das Aussehen oder Verhalten aller Komponenten eines bestimmten Typs zu ändern. Wenn plötzlich alle Überschriften zentriert anstatt links ausgerichtet sein sollen, wäre dies in größerem Umfang bei manueller Herangehensweise nur mit hohem Aufwand machbar. Mit einem Skript dagegen ist diese Aufgabe schnell erledigt:

Scrivito.load(() => { return [...Scrivito.Obj.where("_objClass", "equals", ["Page", "Homepage"])]; }).then(objs => { objs.forEach(o => { console.log(`Touching "${o.get("title")}" (${o.id()})...`); o.widgets() .filter(w => { return w.objClass() == "HeadlineWidget"; }) .forEach(headlineWidget => { headlineWidget.update({ alignment: 'center' }); }); }); console.log("Done aligning HeadlineWidgets"); });

Das Skript ruft die Methode widgets() auf, die die Liste aller Widgets auf einer Seite zurückgibt. Dann wird diese Liste nach dem Widget-Typ gefiltert, um den es uns geht, in diesem Fall HeadlineWidget. Abschließend iterieren wir die verbliebenen Widgets, um sie entsprechend zu aktualisieren, d.h. ihr alignment-Attribut auf den gewünschten Wert zu setzen.

Sie könnten sogar den Typ der Widgets, die Sie ermittelt haben, ändern. Wenn Sie beispielsweise die alten HeadlineWidgets durch Ihre neuen Widgets vom Typ CatchyHeadingWidget ersetzen möchten, brauchen Sie lediglich den Namen ihrer Modellklasse zu ändern und die Attribute hinzuzufügen, die die Widgets haben sollen:

... .forEach(headlineWidget => { headlineWidget.update({ _objClass: 'CatchyHeadingWidget', backgroundImage: image }); }); ...

Auf diese Weise reduziert sich eine Migration von Widgets darauf, ihren Klassennamen zu ändern und Attributwerte wie benötigt zu übertragen oder zu setzen.

Mittels widgets() können Sie auch Widgets von einer Seite entfernen (unabhängig davon, worin sie enthalten sind), indem Sie ihre jeweilige Instanzmethode destroy() aufrufen.

Die Widget-Hierarchie durchlaufen

Manchmal müssen nur solche Widgets einer Spezialbehandlung unterzogen werden, die sich in einem anderen Widget eines bestimmten Typs befinden. Im folgenden (etwas umfangreicheren) Beispiel entfernen wir DividerWidgets aus den Inhalten der Example App, jedoch nur, wenn sie sich in einem SectionWidget oder ColumnWidget befinden (und nicht beispielsweise in einem BoxWidget). Auch beschränken wir uns auf die Homepage-Instanz sowie Page-Instanzen:

function removeWidget(source, attributeName, widgetClassName) { var attributeValue = {}; if (source instanceof Scrivito.Widget) { attributeValue[attributeName] = [ ...source.get(attributeName).filter(w => { return w.objClass() !== widgetClassName; }) ]; source.update(attributeValue); } source.get(attributeName).forEach(w => { switch (w.objClass()) { case 'SectionWidget': case 'ColumnWidget': removeWidget(w, 'content', widgetClassName); break; case 'ColumnContainerWidget': removeWidget(w, 'columns', widgetClassName); break; } }); } Scrivito.load(() => { return [ ...Scrivito.Obj.where("_objClass", "equals", ["Page", "Homepage"]) ]; }).then(objs => { const widgetClassName = 'DividerWidget'; objs.forEach(o => { console.log(`Inspecting "${o.get('title')}" (${o.id()})...`); removeWidget(o, 'body', widgetClassName); }); console.log(`Done removing ${widgetClassName}s.`); });

Der obige Code definiert zunächst eine generische Funktion namens removeWidget, die Widgets des übergebenen Typs (widgetClassName) rekursiv aus einem widgetlist-Attribut entfernt, sofern dieses Attribut nicht zu der Seite selbst gehört (in diesem Fall ist source kein Scrivito.Widget, sondern ein Scrivito.Obj). Die Funktion berücksichtigt diejenigen Container-Widgets, auf die es uns ankommt (etwa SectionWidgets), indem sie sich selbst aufruft, wenn sie ein solches Widget in dem widgetlist-Attribut findet, für das sie aufgerufen wurde.

Im zweiten Teil des Skripts werden, analog zum tags-Beispiel, die zu behandelnden Seiten ermittelt. Für jede Seite in dem Ergebnis-Array objs wird removeWidget aufgerufen, um deren body-widgetlist durchzugehen.

Zusammengefasst ...

Immer dann, wenn Sie mehr als nur ein paar Seiten oder Widgets in ähnlicher Weise modifizieren möchten, bietet es sich an, eine Massenoperation durchzuführen. Hierfür braucht es nicht mehr, als die betreffenden Elemente zu ermitteln und sie zu iterieren, um beispielsweise deren Attribute zu ändern. Denken Sie daran, stets Scrivito.load zu verwenden, um die zu verarbeitenden CMS-Objekte abzuholen und dabei sicherzustellen, dass deren Daten zugreifbar sind.