< SHOPWARE FRONTEND EDITING />

Entwicklerhandbuch

In dieser Anleitung wird beschrieben, wie man technische Erweiterungen an dem Frontend Editing Plugin vornimmt.

Stellen Sie vor dem Start sicher, dass das Frontend Editing Plugin in Ihrem Shopware korrekt installiert und aktiviert ist.

Wenn Sie im Umgang mit dem Frontend Editor vertraut werden möchten, lesen Sie bitte das Benutzerhandbuch.

< Plugin erstellen />

Erstellen Sie im ersten Schritt ein neues Shopware Plugin, in welchem Sie die Erweiterungen vornehmen möchten.

Für die Beispiele innerhalb dieser Anleitung wurde ein Plugin mit dem Namen "RopiCmsExtensions" erstellt. Das vollständige Beispiel-Plugin können Sie hier herunterladen.

< Inhaltsbereiche erstellen />

Da Inhaltsbereiche einfache DIV-Elemente sind, müssen Sie um eigene Inhaltsbereiche zu erstellen, nur die entsprechenden Shopware Templates erweitern.

Registrieren Sie dazu über Ihr Plugin ein neues Template-Verzeichnis, wie im Beispielcode angegeben.

<?php
namespace RopiCmsExtensions;

class RopiCmsExtensions extends \Shopware\Components\Plugin
{

    public static function getSubscribedEvents()
    {
        return [
            'Enlight_Controller_Action_PreDispatch_Frontend' => ['onPreDispatch', -100],
            'Enlight_Controller_Action_PreDispatch_Backend' => ['onPreDispatch', -100],
            'Enlight_Controller_Action_PreDispatch_Widgets' => ['onPreDispatch', -100]
        ];
    }

    public function onPreDispatch()
    {
        $this->container->get('Template')->addTemplateDir(
            $this->getPath() . '/Resources/Views/'
        );
    }
}

Erstellen Sie die Template-Datei "RopiCmsExtensions/Resources/Views/frontend/detail/content/header.tpl" und fügen Sie den Beispielcode ein. Löschen Sie anschließend den Shopware-Cache und navigieren Sie über den Frontend Editor auf eine beliebige Produktdetailseite. Sie sehen nun über dem Produkttitel den neuen Inhaltsbereich.

Mit dem Attribut "data-ropi-content-area" wird ein HTML-Element als Inhaltsbereich markiert. Dadurch lässt sich das HTML-Element später im Frontend Editor mit Inhaltselementen via Drag and Drop befüllen. Jeder Inhaltsbereich muss innerhalb einer Seite einen eindeutigen Bezeichner haben (im Beispiel "myArea").

Innerhalb des HTML-Elements wird die Smarty-Funktion "ropicmsareacontent" aufgerufen. Diese Funktion rendert später im Frontend die Inhaltselemente des Inhaltsbereiches. Hier ist zu beachten, dass der Parameter "area" stets den gleichen Bezeichner haben muss, wie das Attribut "data-ropi-content-area". Die Smarty-Variable $ropiCmsContentDocumentStructure wird vom Frontend Editing Plugin automatisch definiert und enthält die Struktur der Inhaltselemente, die im Inhaltsbereich gerendert werden sollen.

{extends file="parent:frontend/detail/content/header.tpl"}

{block name='frontend_detail_index_product_info' prepend}
<div data-ropi-content-area="myArea">
{ropicmsareacontent area="myArea" contentAreas=$ropiCmsContentDocumentStructure.children}
</div>
{/block}
Der neue Inhaltsbereich

< Inhaltselemente erstellen />

Inhaltselemente sind herkömmliche Shopware-Widgets. In diesem Beispiel erstellen wir ein Inhaltselement namens "Hello". Legen Sie als erstes den entsprechenden Widget-Controller unter dem Dateipfad "RopiCmsExtensions/Controllers/Widgets/RopiCmsExtensionsHello.php" an. Beachten Sie hierbei, dass der Widget-Controller von der Klasse AbstractContentElementController erben muss. Diese Klasse implementiert bereits die indexAction, deshalb ist kein weiterer Code notwendig.

<?php
use RopiCms\Controller\AbstractContentElementController;

class Shopware_Controllers_Widgets_RopiCmsExtensionsHello extends AbstractContentElementController
{
}

Legen Sie als nächstes die zugehörige Template-Datei unter "RopiCmsExtensions/Resources/widgets/ropi_cms_extensions_hello/index.tpl" an. Inhaltselemente dürfen auf der obersten Ebene nur aus einem HTML-Element bestehen und müssen diverse spezielle Attribute definiert haben. Diese Attribute beinhalten beispielsweise die ID, den Namen und die Konfigurationen des Inhaltselements. Die Attribute müssen Sie nicht selbst definieren, sondern können einfach mit Hilfe der Datei "editor_attributes.tpl" inkludiert werden. Die "general_classes.tpl" bindet ein paar Hilfsklassen ein, die beispielsweise den konfigurationsabhängigen Abstand nach unten erzeugen.

Das Inhaltselement soll, sofern kein Name konfiguriert ist, den Text "Hello Guest" ausgeben. Wenn ein Name über den Frontend Editor konfiguriert wird, soll der hinterlegte Name statt dem Wort Guest ausgegeben werden. Des Weiteren beinhaltet das Inhaltselement einen pflegbaren Text mit dem Bezeichner "someEditableText". Falls Sie mehrere pflegbare Texte innerhalb eines Widgets haben, müssen diese verschiedene Bezeichner haben.

<div
  {include file='widgets/_ropi_cms_includes/editor_attributes.tpl'}
  class="{include file='widgets/_ropi_cms_includes/general_classes.tpl'}">
  Hello {if $data.configuration.name|strip}{$data.configuration.name}{else}Guest{/if}
  <div data-ropi-content-editable="someEditableText">{$data.contents.someEditableText|unescape}</div>
</div>

Als letztes muss das neue Inhaltselement noch im Frontend Editor registriert werden. Außerdem müssen noch die Konfigurationsmöglichkeiten für dieses Inhaltselement definiert werden.

Da der Frontend Editor vollständig auf Web Components basiert, kann dieser einfach über HTML erweitert werden. Deshalb erweitern wir die Template-Datei des Frontend Editors selbst. Legen Sie dazu die Datei "RopiCmsExtensions/Resources/backend/ropi_cms/index.tpl" an und befüllen diese mit dem Beispielquellcode.

Unser neues Inhaltselement registrieren wir, indem wir im Smarty-Block "backend_ropi_cms_content_elements" ein neues "ropi-content-element"-Tag anhängen. Hierbei muss das Attribut "type" den Controller-Namen des Widgets beinhalten. Im Attribut "icon" können Sie ein Material Icon definieren. Tragen Sie hierfür den Bezeichner des gewünschten Icons ein. Der Wert für das Attribut "group" ist frei wählbar und definiert unter welcher Gruppe das neue Inhaltselement im Frontend Editor gelistet werden soll. Mit dem Attribut "name" wird der sprechende Name des Inhaltselements festgelegt. Das Attribut "src" muss die Widget-URL beinhalten. Diese wird im Frontend Editor via Ajax aufgerufen, um das Inhaltselement zu rendern.

Innerhalb des "ropi-content-element"-Tags muss sich ein "template"-Tag als Kindknoten befinden. Dieses kann mit beliebigem HTML befüllt werden und definiert, wie das Konfigurationsmenü für das Inhaltselement aussehen soll. Wichtig ist hierbei, dass wie bei gewöhnlichen HTML-Formularen auch, bei allen Eingabefeldern das Attribut "name" gesetzt und definiert ist. Denn über den darüber definierten Bezeichner, greifen Sie im Widget-Template auf die Werte zu. Im Beispielcode hat das "name"-Attribut von unserem Texteingabefeld den Wert "name" und somit kann im Widget-Template über die Template-Variable "$data.configuration.name" der eingegebene Wert ausgelesen werden.
Wie Sie sehen, werden im Beispielcode einige Custom-Tags verwendet, welche Ihnen die Arbeit erleichtern. Sie können natürlich aber auch gewöhnliche HTML-Elemente verwenden. Wenn Sie jedoch komplexe HTML-Elemente benötigen, welche JavaScript verwenden, sollten Sie diese als Web Components einbinden. Um mehr über Web Components zu erfahren, klicken Sie hier .

{extends file="parent:backend/ropi_cms/index.tpl"}

{block name='backend_ropi_cms_content_elements' append}
<ropi-content-element
  type="RopiCmsExtensionsHello"
  icon="extension"
  group="{s name='basis' namespace='backend/ropi_cms/content_element_settings'}Basis{/s}"
  name="Hello"
  src="widgets/RopiCmsExtensionsHello/index">
  <template>
    <ropi-tabs scrollable tablistposition="bottom">
      <ropi-touchable slot="tab">General</ropi-touchable>
      <div slot="tabpanel">
        <ropi-subheader>Basic</ropi-subheader>
        <div class="ropi-cms-margin-h">
          <ropi-textfield name="name">
            <div slot="placeholder">Name</div>
          </ropi-textfield>
        </div>
      </div>
      {foreach from=$breakpoints item=breakpoint}
          <ropi-touchable slot="tab">{$breakpoint.name}</ropi-touchable>
          <div slot="tabpanel">
            {include file="backend/ropi_cms/content_elements/common/general_settings.tpl"}
          </div>
      {/foreach}
    </ropi-tabs>
  </template>
</ropi-content-element>
{/block}

Löschen Sie den Shopware-Cache und laden Sie den Frontend Editor neu. Sie sollten nun das neue Hello-Element sehen.

Im Verzeichnis "RopiCms/Resources/backend/ropi_cms/content_elements/" finden Sie die Definitionen zu den Standard-Inhaltselementen. Nutzen Sie diese als Referenz, wenn Sie Ihre eigenen Inhaltselemente erstellen. Des Weiteren sehen Sie dort, wie die Custom-Tags verwendet werden können.

Hello-Element

< Document Context />

Haben Sie sich eigentlich schon gefragt, wie das Frontend Editing Plugin die Relation zwischen einer Seite und den Inhaltselementen herstellt? Dies geschieht über den sogenannten Document Context.

Navigieren Sie im Frontend Editor zu einer Shopseite (z. B. die Seite Datenschutz). Inspizieren Sie innerhalb des Frontend Editors mit den Entwicklertools Ihrers Browsers das Body-Tag der Shopseite. Sie sehen dort das Attribut "data-ropi-document-context". Dieses Attribut enthält ein JSON, welches dem Frontend Editor mitteilt, für welche Seite die Inhaltselemente gespeichert werden sollen. Die JSON-Eigenschaften "scope" (=module), "controller", "action" und "site" (=shop ID) werden vom Frontend Editor Plugin standardmäßig immer gesetzt. Bei Shopseiten ist aber nicht nur die Controller-Action und der Shop selbst relevant, sondern auch welche konkrete Shopseite aktuell angezeigt wird (z. B. Impressum, Datenschutz etc.). In dem Beispiel-JSON wird dies über die Eigenschaft "staticPageId" definiert, welche die ID der aktuellen Shopseite enthält. Solche Eigenschaften werden als Subcontext bezeichnet.

data-ropi-document-context='{"scope":"frontend","controller":"custom","action":"index","site":1,"staticPageId":"7"}'

< Subcontext für eigene Controller-Actions />

Als Beispiel erstellen Sie in Ihrem Plugin einen neuen News-Controller unter dem Pfad "RopiCmsExtensions/Controllers/Frontend/RopiCmsExtensionsNews.php" . Die showAction ist für die Anzeige einer einzelnen News zuständig. Über den GET-Parameter "newsId" wird gesteuert, welche News ausgegeben werden soll.

<?php
class Shopware_Controllers_Frontend_RopiCmsExtensionsNews extends Enlight_Controller_Action
{

    public function showAction() {
        $news = [];

        if (isset($_GET['newsId'])) {
            if ($_GET['newsId'] === '1') {
                $news = ['title' => 'News 1', 'body' => 'My first news'];
            } else if ($_GET['newsId'] === '2') {
                $news = ['title' => 'News 2', 'body' => 'My second news'];
            }
        }

        $this->View()->assign('news', $news);
    }
}

Legen Sie unter "RopiCmsExtensions/Resources/Views/frontend/ropi_cms_extensions_news/show.tpl" die Template-Datei für die showAction an und befüllen diese mit dem Beispielquellcode. In dieser sind die zwei Inhaltsbereiche "top" und "bottom" definiert. Wenn wir jetzt schon die Inhaltsbereiche mit Inhalte befüllen würden, würden bei jeder News die gleichen Inhalte angezeigt werden, da noch kein Subcontext definiert ist.

{extends file="frontend/index/index.tpl"}

{block name="frontend_index_content"}
<div>
  {if $news}
    <h1>{$news.title}</h1>
    <div data-ropi-content-area="top">
    {ropicmsareacontent area="top" contentAreas=$ropiCmsContentDocumentStructure.children}
    </div>
    <div>{$news.body}</div>
    <div data-ropi-content-area="bottom">
    {ropicmsareacontent area="bottom" contentAreas=$ropiCmsContentDocumentStructure.children}
    </div>
  {else}
    NEWS NOT FOUND
  {/if}
</div>
{/block}

Um die Inhalte von den einzelnen News abhängig zu machen, müssen Sie sich auf das Event "RopiCms_BuildSubcontext_RopiCmsExtensionsNews" subscriben, wobei "RopiCmsExtensionsNews" immer durch den Namen des Controllers zu ersetzen ist, für welchen Sie einen Subcontext definieren möchten. Dazu ergänzen Sie die Datei "RopiCmsExtensions/RopiCmsExtensions.php" so wie im Beispielcode angegeben. In der neuen Methode "onBuildSubcontext" wird für die showAction die aktuelle "newsId" als Subcontext hinterlegt. Rufen Sie nun im Frontend Editor die URL https://yourshopdomain.example/frontend/RopiCmsExtensionsNews/show?newsId=1 auf, wobei yourshopdomain.example durch Ihre Shop-Domain zu ersetzen ist.

<?php
namespace RopiCmsExtensions;

class RopiCmsExtensions extends \Shopware\Components\Plugin
{

    public static function getSubscribedEvents()
    {
        return [
            'Enlight_Controller_Action_PreDispatch_Frontend' => ['onPreDispatch', -100],
            'Enlight_Controller_Action_PreDispatch_Backend' => ['onPreDispatch', -100],
            'Enlight_Controller_Action_PreDispatch_Widgets' => ['onPreDispatch', -100],
            'RopiCms_BuildSubcontext_RopiCmsExtensionsNews' => 'onBuildSubcontext'
        ];
    }

    public function onPreDispatch()
    {
        $this->container->get('Template')->addTemplateDir(
            $this->getPath() . '/Resources/Views/'
        );
    }

    public function onBuildSubcontext(\Enlight_Event_EventArgs $args)
    {
        $request = $args->getController()->Request();

        if ($request->getActionName() === 'show') {
            if (!isset($_GET['newsId']) || $_GET['newsId'] <= 0) {
                return [];
            }

            return ['newsId' => $_GET['newsId']];
        }

        return [];
    }
}

Inspizieren Sie im Frontend Editor nun das Body-Tag der Newsseite. Sie sehen, dass im JSON des Attributs "data-ropi-document-context" die Eigenschaft "newsId" hinterlegt ist und mit dem Wert des übergebenen "newsId"-Parameters befüllt ist. Nun kann jede Newsseite individuell mit Inhaltselementen befüllt werden.

data-ropi-document-context="{"newsId":"1","scope":"frontend","controller":"RopiCmsExtensionsNews","action":"show","site":1}"