SuebenIT



Anfänger Tutorial Neos 3
Workshop für Quer-, Um-, und Einsteiger

 

Teil 3 - Neos 3 anpassen

Bild 1, links: Neos Logo klappt um → sollte in gleicher Reihe sein
Bild 2, rechts: Bild zu breit → kein auto fit, kein auto size

Schaue dir den Quelltext an. Oben vom Logo-Bereich. Sachverhalt Bild 1. (PS.: Habe ein wenig "umformatiert". Tatsächlich sinds mehr Tabulatoren/Einrückungen und Leerzeichen. Dann wärs aber noch schlechter zu lesen gewesen.)

...
<div class="neos-contentcollection">
<div class="neos-nodetypes-image">

<figure>


        <a href="/de/webentwicklung/neos.html">
                    
    <img alt="Neos CMS" src="https://suebenit.com/_Resources/Persistent/b5ad8ceeb57aa5a7d650564580d7c644a1c42e38/bereich-neos.png" width="300" height="90" />

        </a>


</figure>

</div>
</div>
...

Allgemeineres Beispiel:

...
<div class="neos-contentcollection">
<div class="neos-nodetypes-headline">
    <div><h1>Eine Überschrift</h1></div>
</div>
<div class="neos-nodetypes-text">
    <div><p>Ein Text.</p></div>
</div>
<div class="neos-nodetypes-image">
    <figure>     … hier das mit dem img-Tag …     </figure>
</div>
</div>
...

Du hast jede Menge umschließende div nach dem Muster <div class="neos-elementname">. Denke zurück an Teil 2. Im Neos Backend, linke Spalte, unten – der Strukturbereich der Seite die gerade editiert wird. Das "div-wrapping" entspricht genau dieser Struktur. Du hast eine ContenCollection. Das sieht du im Html-Quelltext als <div class="neos-contentcollection">. In der ContentCollection hast du ein Inhaltselement Headline, dann Text, dann Image und wieder Text. Das siehst du im Quelltext als <div class="neos-nodetypes-headline"> usw.

Bereits in unserem Beispiel ist das extrem hinderlich. Mit divs, die sich als eigener Block darstellen, werden wir unser Neos-Logo-Bild ganz oben nie und never als "inline" neben das SuebenIT-Logo stellen können.

Dieses div-wrapping stört aber nicht nur uns. Das Netz ist voll von Beschwerden und Diskussionen darüber. Suche mal nach

delete neos div wrapping
get rid of these content divs in neos
neos div für inhaltselemente löschen
neos wrapping divs entfernen

usw. Suchen in dieser Art.

Um es kurz zu machen. Zusammenfassend für das "warum das ganze?":

Neos nutzt als Editor den Aloha Editor. Der heißt so: Aloha. Der hat eine bestimmte Art, wie er funktioniert. Und eine Eigenheit dieser Funktionsweise ist es, daß er für das Inhaltselement das bearbeitet werden können soll ein dieses Inhaltselement umschließendes Etwas benötigt, in dem er (also Aloha) einige seiner Dinge (zwischen-)speichert.

Dh. Neos ansich brauchts eigentlich garnicht. Durch die Entscheidung den Aloha zu benutzen kam das in den Quellcode. Wir wollen hier nicht weiter erörtern, obs mit einem anderen Editor "besser" wäre. Die Neos-Entwickler werden sich nicht für gerade diesen Editor entschieden haben, wenn er nicht einige Vorteile bringen würde.

Bleibt die Frage: was nun? Wie damit arbeiten? Wie darauf reagieren?

Hier will ich es wieder kurz machen und direkt zu meiner Zusammenfassung aller mir untergekommenen Lösungsvorschläge und Diskussionsbeiträge schreiten.  Man kann im Template mit if-Konstrukten unterscheiden/erkennen, ob man sich im Backend oder Frontend befindet. Der Aloha Editor arbeitet nur im Backend, nicht im Frontend. Wir werden im Backend die wrapping divs lassen – damit Aloha arbeiten kann. Aber im Frontend werden wir sie "ausblenden".

Das scheint aus meiner Sicht die einfachste Lösung mit den wenigsten Seiteneffekten zu sein. Es ist eine Lösung, die sich auch nachträglich sehr leicht und unkompliziert in bereits bestehende Installationen integrieren läßt. Man kann ohne weiteres entscheiden, bei welchen Inhaltselementen (NodeTypes) man den Workaround anwenden will und bei welchen nicht. Und das ganze kann man jederzeit und ohne großen Aufwand wieder rückgängig machen oder auf andere NodeTypes verlagern. Usw. usw. Einziger Wermutstropfen den ich sehe: Im Backend bleiben die umschließenden divs. Dh. im Backend klappt sich das Neos-Logo weiterhin unter das SuebenIT-Logo. Erst im Frontend sehe ich, daß beide Grafiken nebeneinander zu stehen kommen.

TODO: Am besten eine neue Seite anlegen. Dort dann immer auch die neuen NodeTypes erstellen lassen → das gibt "konkrete" Screenshots, evtl. Zweideutigkeiten oder "wo tue ich das jetzt?" werden (noch grundsätzlicher als eh schon :-)) vermieden.

Fangen wir aber nicht mit dem Inhaltselement Image an, sondern mit Headline. Headline ist kleiner und kompakter. Am Headline lösen wir das Problem der wrapping divs. Dann können wir später beim Image darauf aufbauen und verteilen unsere Problemlösung auf mehrere kleine Einheiten.

Als "Vorlage" nehmen wir das Headline NodeType, das Neos bereits standardmäßig mitbringt. Wir finden die betreffenden Dateien hier:

Die NodeTypes-Definition ist hier:

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Configuration

Das Template mit dem HTML-Grundgerüst ist hier:

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Resources/Private/Templates/NodeTypes

Neos hat die Definition für die NodeType Headline zweigeteilt. Einmal gibt es einen (abstrakten) NodeType TitleMixin. Dann gibt es den eigentlichen NodeType Headline, der von TitleMixin erbt. Ich gehe mal schwer davon aus, daß die Jungs von Neos hier Wiederverwendbarkeit im Auge hatten. Bei Image und anderen Standard-NodeTypes arbeiten die ebenfalls mit vorgelagerten xyz-Mixin Vorfahrobjekten. Wir wollen da garnicht weiter darauf eingehen. Nur soviel: wir könnten für unser neues Headline-NodeType ebenfalls von TitleMixin erben. Wir tun es aber nicht. Nachteil: Wir müssen mehr selber schreiben und mehr selber definieren. Vorteil 1: Mit mehreren (Teil-)Definitionen wirds komplizierter und das hätte den Lerneffekt beeinträchtigt. Was wir durch unsere Lösung umgehen. Vorteil 2: Wenn bei einem Neos Update auch die TitleMixin oder eine der Elternobjekte geändert würde, tangiert uns das nicht. Wir bleiben, wie wir es ursprünglich definiert haben. Wobei man das im "echten" Leben auch als Nachteil sehen kann, da wir dann auch von Verbesserungen nicht profitieren würden. Für den Lerneffekt ist es aber wieder besser, es so zu machen. Bei solch grundlegenden Basistypen ist es nicht zu erwarten, aber doch möglich, daß sich in der Zwischenzeit in der Neos Version mit der du das Tutorial bei dir daheim durcharbeitest etwas geändert hat, im Vergleich zur Version welche ich als Grundlage beim Tutorialschreiben hatte. So hättest du andere Ergebnisse, eine andere Anzeige als meine Screenshots, obwohl du dich penibel an die Anleitung hältst.

Im Pfad

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Configuration

betreffen uns zwei Dateien.

NodeTypes.Content.yaml – enthält die Definiton von Headline.

...
'Neos.NodeTypes:Headline':
  superTypes:
    'Neos.Neos:Content': TRUE
    'Neos.NodeTypes:TitleMixin': TRUE
  ui:
    label: i18n
    icon: 'icon-header'
    position: 100
...

Hinweis: Beim "label: i18n"; das i18n dient der Mehrsprachigkeit. Das Backend von Neos gibts für mehrere Sprachen. Wir setzen statt dem i18n direkt unseren Namen ein. Mehrsprachigkeit ist für uns erstmal kein Thema.

NodeTypes.Mixins.yaml – enthält das Basisobjekt TitleMixin.

...
'Neos.NodeTypes:TitleMixin':
  abstract: TRUE
  properties:
    title:
      type: string
      defaultValue: '<h1>Enter headline here</h1>'
      ui:
        inlineEditable: TRUE
        aloha:
          'format':
            'p': FALSE
            'h1': TRUE
            'h2': TRUE
            'h3': TRUE
            'removeFormat': TRUE
          'link':
            'a': TRUE
...

a) Wir sagten, wir wollen es nicht aufteilen, sondern nur eine Definition haben. b) Dem neuen Objekt (Neos-Sprech: NodeType) geben wir den Namen "sitHeadline". Hinweis: das sit steht als Abkürzung für suebenit. c) Die i18n ersetzen wir durch "echte" Namen.
Beachte: Die erste Zeile, der Name unseres Objekts/Inhaltselements/NodeTypes ist nicht "Neos.NodeTypes:TitleMixin", sondern "SuebenIT.NeosTutorial:sitHeadline".

Die Punkte a bis c sehen dann zusammengesetzt so aus.

Gehe ins Verzeichnis

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Configuration

Erstelle dort eine neue Textdatei:

NodeTypes.sitHeadline.yaml

Der Vorsatz "NodeTypes." sorgt dafür, daß es Neos automatisch lädt und entspr. parst.

Der Inhalt der NodeTypes.sitHeadline.yaml ist am Ende wie nachfolgend. Beachte wie üblich: utf-8, Unix-Lineendings, etc.

'SuebenIT.NeosTutorial:sitHeadline':
  superTypes:
    'Neos.Neos:Content': TRUE
  ui:
    label: 'sitHeadline'
    icon: 'icon-header'
    position: 1000
  properties:
    title:
      type: string
      defaultValue: '<h1>Enter headline here</h1>'
      ui:
        inlineEditable: TRUE
        aloha:
          'format':
            'p': FALSE
            'h1': TRUE
            'h2': TRUE
            'h3': TRUE
            'removeFormat': TRUE
          'link':
            'a': TRUE

Das Template befindet sich im Pfad

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Resources/Private/Templates/NodeTypes

Die Datei heißt

Headline.html

Öffne die Datei. Wir werden uns den Inhalt gleich im Anschluss kopieren.

Gehe nun ins Verzeichnis

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Resources/Private/Templates/NodeTypes

Hinweis: Falls das Verzeichnis NodeTypes nicht existiert, erstelle es.

Erstelle dort eine neue Textdatei:

sitHeadline.html

Kopiere den Inhalt der im Schritt davor geöffneten Headline.html. Wir entfernen dabei gleich auch diesen Tabulator. Beachte wie üblich: utf-8, Unix-Lineendings, etc.

{namespace neos=Neos\Neos\ViewHelpers}
<div{attributes -> f:format.raw()}>
{neos:contentElement.editable(property: 'title')}
</div>

Dieses umschließende div ist das welches wir bezüglich der wrapping divs Problematik angesprochen haben. Hier wenden wir nun das mit dem if-Konstrukt an, daß es im Backend angezeigt wird, im Frontend aber nicht.

Nochmal mit Einrückungen, falls du es so besser lesen kannst:

{namespace neos=Neos\Neos\ViewHelpers}
<f:if condition="{neos:rendering.inBackend()}">
    <f:then>
        <div{attributes -> f:format.raw()}>
            {neos:contentElement.editable(property: 'title')}
        </div>
    </f:then>
    <f:else>
        {title -> f:format.raw()}
    </f:else>
</f:if>

Deine sitHeadline.html sieht am Ende so aus:

{namespace neos=Neos\Neos\ViewHelpers}
<f:if condition="{neos:rendering.inBackend()}">
<f:then>
<div{attributes -> f:format.raw()}>
{neos:contentElement.editable(property: 'title')}
</div>
</f:then>
<f:else>
{title -> f:format.raw()}
</f:else>
</f:if>

Zuletzt müssen wir das ganze noch Flow/Fusion "bekannt geben" - keine Ahnung, was da der richtige Neos-Fachausdruck dafür ist.

Gehe ins Verzeichnis:

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Resources/Private/Fusion/NodeTypes

Hinweis: Falls das Verzeichnis NodeTypes nicht existiert, erstelle es. Das Verzeichnis Fusion muß existieren. Im Verzeichnis Fusion hatten wir in Tutorial Teil 2 die Root.fusion für unser Page-Objekt angepaßt.

Erstelle dort eine neue Textdatei:

sitHeadline.fusion

Der Inhalt der sitHeadline.fusion ist am Ende wie nachfolgend. Beachte wie üblich: utf-8, Unix-Lineendings, etc.

prototype(SuebenIT.NeosTutorial:sitHeadline) < prototype(Neos.Neos:Content) {
    templatePath = 'resource://SuebenIT.NeosTutorial/Private/Templates/NodeTypes/sitHeadline.html'
}

Die Fusion-Datei wird nicht automatisch aufgerufen und berücksichtigt. Bei der NodeType-Datei hats gereicht den Dateinamen mit "NodeTypes." beginnen zu lassen. Für die Fusion-Includes ist das nicht vorgesehen. Die Root.fusion unseres SitePackage wird automatisch durchlaufen. Deshalb fügen wir dort ein include ein.

Gehe ins Verzeichnis:

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Resources/Private/Fusion

Öffne die Datei:

Root.fusion

Füge dort unten ans Ende an:

...

# Include all .fusion files in subdirectory NodeTypes
include: NodeTypes/*

Zum Thema Fusion und Includes siehe auch:

http://neos.readthedocs.io/en/stable/CreatingASite/Fusion/InsideFusion.html#fusion-files

Kurz wiederholt und zusammengefaßt. Wir haben drei Dateien erstellt. Einmal die Definition des Objekt, des Inhaltselements – in Neo-Fachsprache NodeType. Mit der Datei NodeTypes.sitHeadline.yaml. Dann das Template sitHeadline.html. Und als drittes die Fusiondatei sitHeadline.fusion, welche wir mit einem include in der Root.fusion in den "Neos-Programmablauf" integriert haben.

Probiere das jetzt mal aus. Erstelle in einer Seite – oder in einer neuen Seite, wenn du magst – ein sitHeadline. Das erstellst du vorher das Headline. Wähle die Seite im linken Strukturbaum. Dann darunter im Baum der Inhaltselemete die ContentCollection Main. Dort wählst du ein "Hinzufügen". Im Auswahlfenster, wo du auch Headline und die anderen zur Wahl bekommst, mußt du nun auch unser sitHeadline sehen. Wähle es aus.

Bei einem Testlauf hatte ich an einer Stelle versehentlich sitHeader anstatt sitHeadline geschrieben. Ich konnte dann zwar das neue Element hinzufügen, bekam dann aber im mittleren Contentbereich statt dem erwarteten "Enter headline here" eine Fehlermeldung. Erhältst du Fehlermeldungen, prüfe, ob du nicht Tippfehler hast. Wenn du nicht die Beispieldaten genommen hast, sondern bspw. statt SuebenIT.NeosTutorial gleich deinen eigenen VendorName.SitePackage eingesetzt hast, kannst du da recht schnell ein Vorkommen vergessen haben zu ersetzen.

Wenden wir uns nun den Bildern zu. Als "Vorlage" nehmen wir das Image NodeType, das Neos bereits standardmäßig mitbringt. Wir finden die betreffenden Dateien hier:

Die NodeTypes-Definition ist hier:

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Configuration

Das Template mit dem HTML-Grundgerüst ist hier:

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Resources/Private/Templates/NodeTypes

Das Image NodeType ist dem eben "abgearbeiteten" Headline NodeType recht ähnlich – lediglich ein wenig "aufgeblähter". Es erbt nicht ein Mal, sondern über zwei Ebenen – Nachfahre von Nachfahre; und gleich von mehreren Vorfahr-Mixins. Direkt vom (abstrakten) ContentImageMixin. Dieser ist aufgeteilt auf die (abenfalls abstrakten) NodeTypes ImageMixin, LinkMixin, ImageCaptionMixin und ImageAlignmentMixin. Das dazu zu sagende hat sich oben beim Headline NodeType bereits erschöpft ... wir legen also gleich los.

Im Pfad

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Configuration

betreffen uns zwei Dateien.

NodeTypes.Content.yaml – enthält die Definiton von Image.

...
'Neos.NodeTypes:Image':
  superTypes:
    'Neos.Neos:Content': TRUE
    'Neos.NodeTypes:ContentImageMixin': TRUE
  ui:
    label: i18n
    icon: 'icon-picture'
    position: 300
...

Hinweis: Beim "label: i18n"; das i18n dient der Mehrsprachigkeit. Das Backend von Neos gibts für mehrere Sprachen. Wir setzen statt dem i18n direkt unseren Namen ein. Mehrsprachigkeit ist für uns erstmal kein Thema.

NodeTypes.Mixins.yaml – enthält das Basisobjekt ContentImageMixin mit ImageMixin usw.

...
# Image mixin
'Neos.NodeTypes:ImageMixin':
  abstract: TRUE
  ui:
    inspector:
      groups:
        image:
          label: i18n
          position: 5
          icon: 'icon-image'
  properties:
    image:
      type: Neos\Media\Domain\Model\ImageInterface
      ui:
        label: i18n
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 50
    alternativeText:
      type: string
      ui:
        label: i18n
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 100
    title:
      type: string
      ui:
        label: i18n
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 150

# Image caption mixin
'Neos.NodeTypes:ImageCaptionMixin':
  abstract: TRUE
  properties:
    hasCaption:
      type: boolean
      ui:
        label: i18n
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 200
    caption:
      type: string
      defaultValue: ''
      ui:
        inlineEditable: TRUE
        aloha:
          placeholder: i18n
          autoparagraph: TRUE

# Image alignment mixin
'Neos.NodeTypes:ImageAlignmentMixin':
  abstract: TRUE
  properties:
    alignment:
      type: string
      defaultValue: ''
      ui:
        label: i18n
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 400
          editor: 'Neos.Neos/Inspector/Editors/SelectBoxEditor'
          editorOptions:
            placeholder: i18n
            values:
              '':
                label: ''
              center:
                label: i18n
              left:
                label: i18n
              right:
                label: i18n

# Link mixin
'Neos.NodeTypes:LinkMixin':
  abstract: TRUE
  properties:
    link:
      type: string
      ui:
        label: i18n
        reloadIfChanged: TRUE
        inspector:
          position: 300
          editor: 'Neos.Neos/Inspector/Editors/LinkEditor'

# Content image mixin
'Neos.NodeTypes:ContentImageMixin':
  abstract: TRUE
  superTypes:
    'Neos.NodeTypes:ImageMixin': TRUE
    'Neos.NodeTypes:LinkMixin': TRUE
    'Neos.NodeTypes:ImageCaptionMixin': TRUE
    'Neos.NodeTypes:ImageAlignmentMixin': TRUE
  properties:
    link:
      ui:
        inspector:
          group: 'image'
...

a) Wir sagten, wir wollen es nicht aufteilen, sondern nur eine Definition haben. b) Die Teilbereiche Link und Alignment werden wir mit übernehmen. Aber dieses Caption-Ding werfen wir raus. c) Dem neuen Objekt (Neos-Sprech: NodeType) geben wir den Namen "sitImage". Hinweis: das sit steht als Abkürzung für suebenit. d) Die i18n ersetzen wir durch "echte" Namen. e) Ich war schon versucht, auch gleich die Properties alternativeText und title so zu ersetzen, daß wir flexibler sind und unserem NodeType mehr Einstellungsmöglichkeiten durch den User geben. Das lassen wir aber mal, damits nicht zu viel auf einmal wird. Wir werden aber als minimum enhancement schonmal dem User eine Eingabemöglichkeit für eine CSS-Klasse und Styleangaben geben.

Die Punkte a bis e sehen dann zusammengesetzt so aus.

Gehe ins Verzeichnis

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Configuration

Erstelle dort eine neue Textdatei:

NodeTypes.sitImage.yaml

Der Vorsatz "NodeTypes." sorgt dafür, daß es Neos automatisch lädt und entspr. parst.

Der Inhalt der NodeTypes.sitImage.yaml ist am Ende wie nachfolgend. Beachte wie üblich: utf-8, Unix-Lineendings, etc.

'SuebenIT.NeosTutorial:sitImage':
  superTypes:
    'Neos.Neos:Content': TRUE
  ui:
    label: 'sitImage'
    icon: 'icon-picture'
    position: 1050
    inspector:
      groups:
        image:
          label: 'Image'
          position: 5
          icon: 'icon-image'
  properties:
    image:
      type: Neos\Media\Domain\Model\ImageInterface
      ui:
        label: 'Image'
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 50
    alternativeText:
      type: string
      ui:
        label: 'Alternativer Text'
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 100
    title:
      type: string
      ui:
        label: 'Titel'
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 150
    classTag:
      type: string
      ui:
        label: 'Class Tag (CSS-Klasse/n)'
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 200
    styleTag:
      type: string
      ui:
        label: 'Style Tag'
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 250
    link:
      type: string
      ui:
        label: 'Link'
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 300
          editor: 'Neos.Neos/Inspector/Editors/LinkEditor'
    alignment:
      type: string
      defaultValue: ''
      ui:
        label: 'Alignment'
        reloadIfChanged: TRUE
        inspector:
          group: 'image'
          position: 350
          editor: 'Neos.Neos/Inspector/Editors/SelectBoxEditor'
          editorOptions:
            placeholder: 'Default'
            values:
              '':
                label: ''
              center:
                label: 'center'
              left:
                label: 'left'
              right:
                label: 'right'

Das Template ist ebenfalls mehrgeteilt.

Im Pfad

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Resources/Private/Templates/NodeTypes

gibts die Datei:

Image.html

Im Verzeichnis "Partials" darunter, dh. im Pfad

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Resources/Private/Templates/NodeTypes/Partials

betreffen uns zwei Dateien:

Image.html
SimpleImage.html

Wir werden nicht breit auf jede einzelne eingehen. In Kurzform: Die obere Image.html hat einen Bereich in dem sie die untere Image.html aufruft. Diese wiederum ruft in einem dortigen Bereich die SimpleImage.html auf. Ähnliche Ursachen und Beweggründe wie oben bei der NodeTypes-Definition. Wir werden es uns wieder vereinfachen und packen alles in eine einzelne Datei.

Setzen wir es erstmal schrittweise zusammen. Am Ende bekommst du den kompletten Code zum kopieren.

Die obere Image.html, also die im Pfad

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Resources/Private/Templates/NodeTypes

sieht so aus

{namespace neos=Neos\Neos\ViewHelpers}
<div{attributes -> f:format.raw()}>
    <f:render partial="Image" arguments="{_all}" />
</div>

Das div ist die ganz oben angesprochene wrapping divs Problematik. Die lösen wir genauso wie bei unserer sitHeadline – siehe oben.

Das "<f:render partial="Image" arguments="{_all}" />" bewirkt, daß diese andere Image.html aufgerufen und an dieser Stelle eingebunden wird. Wir rufen nichts auf, sondern nehmen das direkt rein und machen wie gesagt aus mehreren Dateien eine.

Im Original siehts so aus:

{namespace neos=Neos\Neos\ViewHelpers}
<figure{f:if(condition: imageClassName, then: ' class="{imageClassName}"')}>
    <f:render partial="SimpleImage" arguments="{_all}" />
    <f:if condition="{hasCaption}">
        <figcaption>
            {neos:contentElement.editable(property: 'caption', node: node)}
        </figcaption>
    </f:if>
</figure>

Diesen figure-Tag werfen wir komplett raus. Das entscheide ich einfach mal so, ohne einen Roman dazu zu schreiben. Wir wollen es (hier/aktuell) nicht haben. Diesen Bereich mit dem hasCaption haben wir auch oben in der NodeTypes-Definition herausgeworfen – du erinnerst dich. Dann bleibt aber eigentlich nur noch der Aufruf der SimpleImage.html. Was es uns leichter macht. Wir behalten lediglich dieses if-Konstrukt beim öffnenden figure-Tag im Hinterkopf. Das nehmen wir später als Kopiervorlage für unsere neu eingeführten Usereingaben für classTag uns styleTag.

Die SimpleImage.html ist wie folgt:

{namespace neos=Neos\Neos\ViewHelpers}
{namespace media=Neos\Media\ViewHelpers}
<f:if condition="{image}">
    <f:then>
        <f:if condition="{link}">
            <f:then>
                <a href="{link}">
                    <f:render section="imageRendering" arguments="{_all}" />
                </a>
            </f:then>
            <f:else>
                <f:render section="imageRendering" arguments="{_all}" />
            </f:else>
        </f:if>
    </f:then>
    <f:else>
        <f:if condition="{neos:rendering.inEditMode()}">
            <img src="{f:uri.resource(package: 'Neos.Neos', path: 'Images/dummy-image.svg')}" title="Dummy image" alt="Dummy image" class="neos-handle" />
        </f:if>
    </f:else>
</f:if>

<f:section name="imageRendering">
    <media:image image="{image}" alt="{alternativeText}" title="{title}" width="{width}"
                 maximumWidth="{maximumWidth}" height="{height}" maximumHeight="{maximumHeight}"
                 allowUpScaling="{allowUpScaling}" allowCropping="{allowCropping}" />
</f:section>

Unten wird eine section imageRendering definiert. Die wird oben zwei mal genutzt. Es ist Geschmackssache, ob man das tatsächlich extra auslagern muß. Zum einen ist es so klein, daß mans hätte oben auch zwei mal direkt aufführen können. Oder man strukturiert die if/else leicht anders. Wie dem auch sein, wir werfen es eh ganz raus.

Zu dem ViewHelper für "media" siehe die Neos-Dokumentation:

http://neos.readthedocs.io/en/stable/References/ViewHelpers/Media.html

Dh. diese maximumWidth ist etwas völlig anderes als der HTML max-width Tag – den wir nutzen wollen. Auch wird von Neos über diese Methode immer auch ein width und ein height Tag eingefügt. Was wir garnicht wollen. Im Gegenteil. Die sollen eben gerade nicht eingefügt werden. Wir machens also anders. Coden direkt. Und können dadurch das mit dem namespace zum Media ViewHelper ganz rauswerfen.

Mal ein erster Zwischenstand. Einiges von dem oben Geschriebenen in Code gegossen. PS.: Wir räumen dabei gleich ein wenig mit den vielen Tabs auf.

{namespace neos=Neos\Neos\ViewHelpers}
<f:if condition="{neos:rendering.inBackend()}">
<div{attributes -> f:format.raw()}>
</f:if>

<f:if condition="{image}">
    <f:then>
        <f:if condition="{link}">
<a href="{link}">
        </f:if>

[… hier kommt unser Code für den img Tag hin …]

        <f:if condition="{link}">
</a>
        </f:if>
    </f:then>
    <f:else>
        <f:if condition="{neos:rendering.inEditMode()}">
            <img src="{f:uri.resource(package: 'Neos.Neos', path: 'Images/dummy-image.svg')}" title="Dummy image" alt="Dummy image" class="neos-handle" />
        </f:if>
    </f:else>
</f:if>

<f:if condition="{neos:rendering.inBackend()}">
</div>
</f:if>

Kleine Anmerkung zum Code. Das if zum inBackend – also das entfernen störender wrapping divs im Frontend. Es hat gereicht es einmal oben zum öffnenden und einmal unten zum schließenden div einzufügen. In der Mitte, das mit dem Dummy image, wars nicht nötig. Das ist abhängig von inEditMode. inEditMode aber kann es nur bei inBackend geben. Da blieb uns einiges an if-else-Konstruktion erspart :-)

Das Grundgerüst, damit unser Bild responsive wird, sich automatisch verkleinert, etc.:

<img src=“[...]“style="max-width: 100%; height: auto" border="0" alt="[…]" />

Das erweitert auf die Werte die der User angeben kann. Die die wir vom Neos-Original übernommen haben und unsere neu hinzugefügten.

<img src="[...]" class=“[...]“ style="max-width: 100%; height: auto; [...plus das vom User...]" border="0" align=“[...]“ title=[…] alt="[…]" />

Jetzt ist ein guter Zeitpunkt, sich zu erinnern, daß wir uns aus der "mittleren Inhalt.html" dieses eine if-Konstrukt merken wollten.

<figure{f:if(condition: imageClassName, then: ' class="{imageClassName}"')}>

Das nutzen wir als Kopiervorlage. Einzeln sieht es so aus:

{f:if(condition: alternativeText, then: ' alt="{alternativeText}"')}

{f:if(condition: title, then: ' title="{title}"')}

{f:if(condition: alignment, then: ' align="{alignment}"')}

{f:if(condition: classTag, then: ' class="{classTag}"')}

{f:if(condition: styleTag, then: '; {styleTag}')}

Und alles zusammengesetzt dann so:

<img src="{imageUri}"{f:if(condition: classTag, then: ' class="{classTag}"')} style="max-width: 100%; height: auto{f:if(condition: styleTag, then: '; {styleTag}')}{f:if(condition: alignment, then: '; float: {alignment}')}" border="0"{f:if(condition: title, then: ' title="{title}"')}{f:if(condition: alternativeText, then: ' alt="{alternativeText}"')} />

Hinweis: Nimm das src="{imageUri}" mal so hin. Wir kommen recht bald etwas weiter unten darauf zurück. Glaube fürs erste einfach mal, daß imageUri die Url zum Bild hat.

Gehe nun ins Verzeichnis

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Resources/Private/Templates/NodeTypes

Erstelle dort eine neue Textdatei:

sitImage.html

Der Inhalt der sitImage.html ist am Ende wie nachfolgend. Beachte wie üblich: utf-8, Unix-Lineendings, etc.

{namespace neos=Neos\Neos\ViewHelpers}
<f:if condition="{neos:rendering.inBackend()}">
<div{attributes -> f:format.raw()}>
</f:if>
<f:if condition="{image}">
<f:then>
<f:if condition="{link}">
<a href="{link}">
</f:if>
<img src="{imageUri}"{f:if(condition: classTag, then: ' class="{classTag}"')} style="max-width: 100%; height: auto{f:if(condition: styleTag, then: '; {styleTag}')}{f:if(condition: alignment, then: '; float: {alignment}')}" border="0"{f:if(condition: title, then: ' title="{title}"')}{f:if(condition: alternativeText, then: ' alt="{alternativeText}"')} />
<f:if condition="{link}">
</a>
</f:if>
</f:then>
<f:else>
<f:if condition="{neos:rendering.inEditMode()}">
<img src="{f:uri.resource(package: 'Neos.Neos', path: 'Images/dummy-image.svg')}" title="Dummy image" alt="Dummy image" class="neos-handle" />
</f:if>
</f:else>
</f:if>
<f:if condition="{neos:rendering.inBackend()}">
</div>
</f:if>

Zuletzt noch unsere fusion-Datei. Gehe ins Verzeichnis:

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Resources/Private/Fusion

Erstelle dort eine neue Textdatei:

sitImage.fusion

Fangen wir mit nachfolgendem Code an. Beachte wie üblich: utf-8, Unix-Lineendings, etc.

prototype(SuebenIT.NeosTutorial:sitImage) < prototype(Neos.NodeTypes:Image) {
    templatePath = 'resource://SuebenIT.NeosTutorial/Private/Templates/NodeTypes/sitImage.html'
}

Hinweis: Mittels Root.fusion brauchen wir diesmal nicht sicherstellen, daß unsere sitImage.fusion auch tatsächlich beachtet wird. Wir haben oben zur sitHeadline bereits ein include mit * auf dieses Verzeichnis gemacht – siehe dort.

Die erste Zeile weicht von der ersten Zeile unserer sitHeadline ab.

Erste Zeile bei sitImage:
prototype(SuebenIT.NeosTutorial:sitImage) < prototype(Neos.NodeTypes:Image) {

Erste Zeile bei sitHeadline:
prototype(SuebenIT.NeosTutorial :sitHeadline) < prototype(Neos.Neos:Content) {

Die Headline ist so einfach, daß sie kein "prototype" für Fusion gebraucht hat. Schaue dir im Verzeichnis

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Resources/Private/Fusion

die Datei

Root.fusion an.

Dort der Teil mit "prototype(Neos.NodeTypes:Image)". Dort werden unter anderem auch die URIs berechnet – u.a. auch für den Link, dh. den a-Tag. Das wollen wir nicht alles kopieren. Hier ist das Stichwort: Vererbung – und gut ist.

Bleibt noch die etwas oben offen gelassene Frage zum imageUri. Dh. wie kommt die Url zum Bild in unseren Html-Code?

Variante 1:

Das Template hat aktuell die Zeile

<img src="{imageUri}"{f:if(condition: ...usw...

Das könnte man ersetzen durch

<img src="{media:uri.image(image:image)}"{f:if(condition: ...usw...

In diesem Fall wird die Url zum Bild hier berechnet.

Zu beachten wäre lediglich, daß ein

{namespace media=Neos\Media\ViewHelpers}

dem Template hinzugefügt wird, da der media-Befehl nicht im Neos-Namespace enthalten ist, sondern im Media-Namespace.

Die korrespondierende sitImage.fusion ist dann der Dreizeiler, wie oben als letzte Variante angegeben.

Fertig würde es dann so aussehen.

sitImage.html

{namespace neos=Neos\Neos\ViewHelpers}
{namespace media=Neos\Media\ViewHelpers}
<f:if condition="{neos:rendering.inBackend()}">
<div{attributes -> f:format.raw()}>
</f:if>
<f:if condition="{image}">
<f:then>
<f:if condition="{link}">
<a href="{link}">
</f:if>
<img src="{media:uri.image(image:image)}"{f:if(condition: classTag, then: ' class="{classTag}"')} style="max-width: 100%; height: auto{f:if(condition: styleTag, then: '; {styleTag}')}{f:if(condition: alignment, then: '; float: {alignment}')}" border="0"{f:if(condition: title, then: ' title="{title}"')}{f:if(condition: alternativeText, then: ' alt="{alternativeText}"')} />
<f:if condition="{link}">
</a>
</f:if>
</f:then>
<f:else>
<f:if condition="{neos:rendering.inEditMode()}">
<img src="{f:uri.resource(package: 'Neos.Neos', path: 'Images/dummy-image.svg')}" title="Dummy image" alt="Dummy image" class="neos-handle" />
</f:if>
</f:else>
</f:if>
<f:if condition="{neos:rendering.inBackend()}">
</div>
</f:if>

sitImage.fusion

prototype(SuebenIT.NeosTutorial:sitImage) < prototype(Neos.NodeTypes:Image) {
    templatePath = 'resource://SuebenIT.NeosTutorial/Private/Templates/NodeTypes/sitImage.html'
}

Variante 2:

Im Template lassen wir das imageUri. Können es so mehrfach im Template verwenden, ohne es mehrfach berechnen zu müssen. Nebenbei brauchen wir den Media-Namespace nicht im Template.

Um das Geheimnis nun zu lüften: die imageUri berechnen wir in unserer Fusion-Datei. Doch der Reihe nach. Erstmal das Template.

sitImage.html

{namespace neos=Neos\Neos\ViewHelpers}
<f:if condition="{neos:rendering.inBackend()}">
<div{attributes -> f:format.raw()}>
</f:if>
<f:if condition="{image}">
<f:then>
<f:if condition="{link}">
<a href="{link}">
</f:if>
<img src="{imageUri}"{f:if(condition: classTag, then: ' class="{classTag}"')} style="max-width: 100%; height: auto{f:if(condition: styleTag, then: '; {styleTag}')}{f:if(condition: alignment, then: '; float: {alignment}')}" border="0"{f:if(condition: title, then: ' title="{title}"')}{f:if(condition: alternativeText, then: ' alt="{alternativeText}"')} />
<f:if condition="{link}">
</a>
</f:if>
</f:then>
<f:else>
<f:if condition="{neos:rendering.inEditMode()}">
<img src="{f:uri.resource(package: 'Neos.Neos', path: 'Images/dummy-image.svg')}" title="Dummy image" alt="Dummy image" class="neos-handle" />
</f:if>
</f:else>
</f:if>
<f:if condition="{neos:rendering.inBackend()}">
</div>
</f:if>

Die hast du bereits so gespeichert. Brauchst nichts zu ändern. Habs hier nur nochmal hinkopiert, damit sie  für die Variante 2 mit der korrespondierenden Fusion-Datei zusammen aufgeführt ist.

sitImage.fusion

prototype(SuebenIT.NeosTutorial:sitImage) < prototype(Neos.NodeTypes:Image) {
    templatePath = 'resource://SuebenIT.NeosTutorial/Private/Templates/NodeTypes/sitImage.html'
    imageUri = Neos.Neos:ImageUri {
        asset = ${q(node).property('image')}
        maximumWidth = 2560
        maximumHeight = 1280
        @if.image = ${q(node).property('image')}
    }
}

Übrigens: In diesem prototype kannst du Variablen und Berechnungen ganz nach Bedarf einfügen. Möchtest du eine eigene MeineBeispielNode programmieren, könnte es so aussehen:

prototype(SuebenIT.NeosTutorial:MeineBeispielNode) < prototype(Neos.Neos:Content) {
    templatePath = 'resource://SuebenIT.NeosTutorial/Private/Templates/NodeTypes/sitImage.html'
    eineVariable = 'TestWert'
    nochEineVariable = ..... berechne hier etwas ..…
    meineNode = ${q(node)}
    eineObjektInstanz = ..... instanziiere ein anderes Objekt, wie oben bspw. beim imageUri ..…
    ..... du hast Konstrukte wie if, Schleifen jedoch habe ich noch keine gefunden ..…
    ..... du kannst auf "Unter"-Elemente zugreifen ..…
    text = ${q(node).property('MeinText2')}
    unterwert = ${q(node).property('EinObjekt').property('EinWert')}
    numberOfComments = ${q(node).children('comments').children("[spam = false]").count()}
    ..... siehe bspw. ..…
    http://neos.readthedocs.io/en/stable/CreatingASite/Fusion/EelFlowQuery.html
}

Unsere Anfangsprämisse ist noch nicht vollständig gelöst. Die beiden Grafiken in der Kopfzeile unserer Seiten werden immer noch nicht nebeneinander dargestellt.

Gehe in den Browser und lade die Seite. Also so, daß du im Frontend bist und nicht im Backend.

https://example.com/......todo-betreffende-unterseite......

Schaue den Html-Quelltext der Seite an.

Wie du siehst, sind beim Bild – das den img-tag, a-tag, usw. enthält – keine umgebenden divs mehr. Das hat also geklappt. Aber die ContentCollection schleust uns immer noch ein umschließendes div "neos-contentcollection" in den Code. Dieses div sorgt dafür, daß die zweite Grafik unter die erste geklappt wird; selbst wenn genügend Platz wäre, um beide nebeneinander darzustellen.

Es hat sich herausgestellt, daß wir tiefer in die Struktur von Neos einsteigen müßten, um das zu lösen. Das machen wir: wir sind hier ja schließlich im Anfänger-Tutorial. Aber eine weitere grundlegende Methode möchte ich dir noch zeigen, mit dem wir unseren Einstieg in Neos Struktur dann beenden wollen.

sitHeadline und sitImage hatten wir als neue NodeTypes angelegt. Wir können jedoch die vorhandenen Inhaltselemente – spricht NodeTypes – auch direkt ändern. Dabei spielt es keine Rolle, obs die Neos-eigenen sind oder bspw. welche die durch Plugins hinzugekommen sind.

Als Beispiel nehmen wir das Neos-eigene Text Inhaltselement. Wir haben neben Headlines und Images auch Text auf unseren Beispielseiten angelegt. Auch diese Textelemente produzieren im Neosstandardverhalten ein umschließendes div. Die wollen wir ebenfalls eliminieren. Das Beispiel erweitern  wir um des weiter lernen Willens gleich ein wenig.

Die Neos-Definition der Text NodeType findest du in

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Configuration

Datei: NodeTypes.Content.yaml – enthält die Definiton von Text.
Datei: NodeTypes.Mixins.yaml – enthält die Defintion des TextMixin.

Hier diesmal keine Romane. Du hast es schon zwei mal gemacht, kennst es bereits.

Unsere Reihenfolge war bisher: NodeType definieren, Template erstellen, Fusion-Code "programmieren". Das ändern wir hier und fangen aus didaktischen Gründen gleich mit dem Fusioncode an. Würden wir analog zur sitHeadline eine neue eigene NodeType definieren, würde unser Code starten mit:

prototype(SuebenIT.NeosTutorial:sitText) {
    hier dann unser Code
}

Das machen wir diesmal anders.

Gehe ins Verzeichnis:

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Resources/Private/Fusion/NodeTypes

Erstelle dort eine neue Textdatei:

Text.fusion

Anmerkung: nicht sitText.fusion, sondern Text.fusion, also ohne das Präfix "sit". Letztlich ist es egal, wir hätten völlig freie Hand, wie wir die Datei nennen. Wir müßten nichtmal eine neue Datei anlegen, sondern könnten die Definition in eine bereits bestehende einfügen. Das hat sich aber so als Konvention entwickelt. Die Dateien nennt man NameMeinesInhaltselements.fusion. So findet sich ein dritter oder vierter oder sonstwer, der sich irgendwann mal mit deinem Code, deiner Struktur auseinandersetzen muß, viel leichter zurecht. Alle halten sich daran, alle kommen gleich damit klar. Zumindest in der Theorie :-) Stichwort in der Softwareentwicklung: Style Guides.

Beachte wie immer: utf-8, Unix-Lineendings, etc.

prototype(Neos.NodeTypes:Text) {
    templatePath = 'resource://SuebenIT.NeosTutorial/Private/Templates/NodeTypes/Text.html'
}

Dir fällt sofort die erste Zeile auf. Es heißt nicht

SuebenIT.NeosTutorial:sitText

sondern

Neos.NodeTypes:Text

Das heißt, wir definieren kein neues Objekt, sondern "redefinieren" ein bereits bestehendes. Das bestehende ist das Objekt, das Neos bereits standardmäßig mit sich bringt.

Ich habe zwei Jahrzehnte Softwareentwicklung auf dem Buckel. Mir stellt sich bei solch einem Vorgehen sofort die Frage nach dem Scope. Wenn ich das Objekt/Inhaltselement/NodeType hier an genau dieser Stelle in genau dieser Datei also redefiniere ... Ab wann gilt das? Auch für alle vorher schon vorkommenden Stellen oder erst für alle danach kommenden? Ich komme aus der objektorientierten Ecke. Mit diesem Prototyping muß ich mich erst noch anfreunden. Es tut mir Leid, daß ich dir deshalb nicht viel dazu sagen kann. Ich habe mich aber natürlich durch die offizielle Neos-Doku kreuz und quer gelesen. Mein Ergebnis: anscheinend gilts für alles und überall, immer und an jeder Stelle.

Wirklich neu ist dieses "ich kanns redefinieren" für uns aber nicht. In Teil 2 haben wir in der Root.fusion das Neos-Standard-NodeType "Page" redefiniert und es wirkte sich auch auf allen von Neos bereits vorhandenen Code aus.

Trotzdem, als objektorientierter und typgebundener Entwickler mag ich mir nicht ausmalen, was es heißt, wenn ich 30 Plugins habe, dazu 5 Jahre vorhergehende Entwicklung an der Site durch 3 verschiedene Agenturen; alle bringen eigene Veränderungen an den Standarddatentypen und -Objekten ein. Und ich armer Tropf soll da dann noch durchsteigen, das ganze warten, erweitern? Prost Mahlzeit.

Da das aber der Neos-Weg ist, werden wir ihn gehen. Unsere Text.fusion haben wir gespeichert. Erstellen wir nun das Template.

Das "Original" – als Kopiervorlage – findest du hier:

<rootpfad>/Neos/Packages/Application/Neos.NodeTypes/Resources/Private/Templates/NodeTypes

Gehe ins Verzeichnis:

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Resources/Private/Templates/NodeTypes

Erstelle dort eine neue Textdatei:

Text.html

Auch hier nicht sitText.html, sondern Text.html. Selber Hintergrund wie eben. Beachte wie immer: utf-8, Unix-Lineendings, etc.

{namespace neos=Neos\Neos\ViewHelpers}
<f:if condition="{neos:rendering.inBackend()}">
<f:then>
<div{attributes -> f:format.raw()}>
{neos:contentElement.editable(property: 'text')}
</div>
</f:then>
<f:else>
{text -> f:format.raw()}
</f:else>
</f:if>

Gehe mit dem Browser wieder zu deiner Seite, dh. in den Frontend.

https://example.com/......todo-betreffende-unterseite......

Schaue dir den Html-Quelltext an. Wie du siehst, sind die wrapping divs bei den Text-Inhaltselementen weg. Schön.

Wir könnten nun auch die NodeType-Definition "überschreiben". Einen guten Anwendungsfall haben wir hier aber nicht wirklich. Was mich an Neos sofort gestört hat – übrigens auch bei WordPress, deren TinyMCE handhabt das genauso –, ist, daß die alles im Textfeld mit p-Tags lösen. Drücke ich die Enter-Taste habe ich ein neues p-Tag. Für ein br muß ich Shift-Enter drücken. Das ansich ist ok. Man weiß, daß man die Shift-Taste mitdrücken muß, gewöhnt sich dran, gut ist. Aber diese p-Tag-Philosophie bleibt ja trotzdem. Warum p-Tags? Läuft denn nicht mittlerweile alles über div-Tags? Bei älteren Systemen kann man nachvollziehen, daß es auf ein "das ist historisch bedingt" hinausläuft. Aber Neos ist relativ neu und hat sich doch auf die Fahnen geschrieben, alte Zöpfe abzuschneiden!? Dabei können sowohl TinyMCE also auch Aloha beides – den p und den div. Das ginge alles bereits out of the box, aber WordPress und Neos blenden es wieder aus. Nun, das "es geht auch div" soll für unser Text-Objekt der Anwendungsfall sein, daß wir auch die NodeTypes-Definition verändern.

Theoretisch gehen wir es kurz mit der redefinieren Methode durch. Damit du es gesehen hast. Fallen dann aber wieder in unser altes Verhaltensmuster zurück und werden letztlich nicht Text redefinieren, sondern unser eigenes neues sitText einführen.

Gehe ins Verzeichnis

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Configuration

Erstelle dort eine neue Textdatei:

NodeTypes.Text.yaml

Der Vorsatz "NodeTypes." sorgt dafür, daß es Neos automatisch lädt. Beachte wie üblich: utf-8, Unix-Lineendings, etc.

'Neos.NodeTypes:Text':
  properties:
    text:
      ui:
        aloha:
          'format': # Enable specific formatting options.
            'strong': true
            'b': false
            'em': true
            'i': false
            'u': true
            'sub': true
            'sup': true
            'p': true
            'h1': true
            'h2': true
            'h3': true
            'h4': true
            'h5': true
            'h6': true
            'code': true
            'removeFormat': true
          'table':
            'table': true
          'link':
            'a': true
          'list':
            'ul': true
            'ol': true
          'alignment':
            'left': true
            'center': true
            'right': true
            'justify': true
          'formatlesspaste':
            # Show toggle button for formatless pasting.
            'button': true
            # Whether the formatless pasting should be enable by default.
            'formatlessPasteOption': false
            # If not set the default setting is used: 'a', 'abbr', 'b', 'bdi', 'bdo', 'cite', 'code', 'del', 'dfn',
            # 'em', 'i', 'ins', 'kbd', 'mark', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'small', 'strong', 'sub', 'sup',
            # 'time', 'u', 'var'
            'strippedElements': ['a']
          'autoparagraph': false # Automatically wrap non-wrapped text blocks in paragraph blocks.

Wir geben kein superTypes an, kein inspector, usw. Alles vom "alten", überschriebenen bleibt erhalten. Die "neue-alte" Text NodeType ist durch das redefinieren nicht erstmal leer und wir füllen es neu. Vielmehr bleibt alles alte, bisherige erhalten. Was wir hier definieren ist zusätzlich. Wenn wir etwas definieren, was es im alten schon gibt, wirds durch unser neues überschrieben. Dh. da gilt dann nicht mehr das alte, sondern unser neues. Also: alles bisherige, plus alles durch uns neu hinzugefügte und bei einem "gabs auch vorher schon" gilt unser neues.

Das zum "Aloha" habe ich direkt aus der Neo-Dokumentation kopiert:

http://neos.readthedocs.io/en/stable/CreatingASite/NodeTypes/NodeTypeDefinition.html

Dann lediglich die FALSE in TRUE geändert, die ich erreichbar machen wollte.

PS.: autoparagraph habe ich ausgeschaltet. Ist "Geschmackssache". Du stellst es ein, wie du magst. Siehe Aloha-Doku zu autoparagraph:

http://www.alohaeditor.org/guides/plugin_autoparagraph.html

Laut

http://www.alohaeditor.org/guides/using_aloha.html

sind div und span als "supported" gelistet. Aber: mglw. heißt supported in diesem Zusammenhang nicht, daß mans als Formatierung beim gerade editierenden Element nutzen kann, sondern daß div ein zu editierendes Element sein kann. Dh. div kann editable gemacht werden. Keine Ahnung. Ich habe jedenfalls div und span ausprobiert. Kannst du auch. Nimm die Zeile mit dem "'p': true" als Kopiervorlage und füge zwei neue Zeile darunter ein. Eine mit div und eine mit span statt dem p. Nichts. Also entweder hat das Neos absichichtlich deaktiviert. Man kann Aloha so konfigurieren, daß man manches auch deaktivieren kann. Oder es geht halt nicht und die angegeben Quelle ist so zu verstehen, wie sie gemeint ist und nicht wie man es gerne hätte :-)

Wie dem auch sei. Durch das was wir auf true gesetzt haben, hat sich unser Editor bereits gut erweitert. Und da wir es nun haben, geben wir es nicht mehr her.

Wie geschrieben, wollten wir in alte Verhaltensmuster zurückfallen. Wir löschen diese ganze "Redefinierungskiste" bzgl. Text und führen unser eigenes NodeType sitText ein.

PS.: Nachfolgend: statt zu löschen, könntest du auch umbenennen und in den Dateien ändern. Wir wollen hier aber auf Nummer sicher, daß sich dir keine Tippfehler einschleichen. Der Lernfrust wäre höher, als die wenigen "unnötig langweiligen" Schritte.

Gehe zu

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Configuration

Lösche dort die

NodeTypes.Text.yaml

Erstelle dort eine neue Textdatei:

NodeTypes.sitText.yaml

Der Vorsatz "NodeTypes." sorgt dafür, daß es Neos automatisch lädt. Beachte wie üblich: utf-8, Unix-Lineendings, etc.

'SuebenIT.NeosTutorial:sitText':
  superTypes:
    'Neos.Neos:Content': TRUE
  ui:
    label: 'sitText'
    icon: 'icon-file-text'
    position: 1020
  properties:
    text:
      type: string
      defaultValue: ''
      ui:
        inlineEditable: TRUE
        aloha:
          'format': # Enable specific formatting options.
            'strong': true
            'b': false
            'em': true
            'i': false
            'u': true
            'sub': true
            'sup': true
            'p': true
            'h1': true
            'h2': true
            'h3': true
            'h4': true
            'h5': true
            'h6': true
            'code': true
            'removeFormat': true
          'table':
            'table': true
          'link':
            'a': true
          'list':
            'ul': true
            'ol': true
          'alignment':
            'left': true
            'center': true
            'right': true
            'justify': true
          'formatlesspaste':
            # Show toggle button for formatless pasting.
            'button': true
            # Whether the formatless pasting should be enable by default.
            'formatlessPasteOption': false
            # If not set the default setting is used: 'a', 'abbr', 'b', 'bdi', 'bdo', 'cite', 'code', 'del', 'dfn',
            # 'em', 'i', 'ins', 'kbd', 'mark', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'small', 'strong', 'sub', 'sup',
            # 'time', 'u', 'var'
            'strippedElements': ['a']
          'autoparagraph': false # Automatically wrap non-wrapped text blocks in paragraph blocks.

Gehe zu

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Resources/Private/Templates/NodeTypes

Löschen wäre hier Quatsch, da der Inhalt unverändert, identisch bleibt. Einfach nur umbenennen.

vorher: Text.html
nachher: sitText.html

Gehe zu

<rootpfad>/Neos/Packages/Sites/SuebenIT.NeosTutorial/Resources/Private/Fusion/NodeTypes

Lösche dort die

Text.fusion

Erstelle dort eine neue Textdatei:

sitText.fusion

Beachte wieder: utf-8, Unix-Lineendings, etc.

prototype(SuebenIT.NeosTutorial:sitText) {
    templatePath = 'resource://SuebenIT.AllSites/Private/Templates/NodeTypes/Text.html'
}

Teste es aus. Erstelle einen (oder mehrere) sitText-Elemente, bearbeite sie, publish, prüfe im Frontend. Auch den Html-Quelltext.

TODO: Neue Seite, headline, text, image, text, image, text → keine wrapping divs → jetzt geht sogar ein align für bspw. Bilder wunschgemäß. Fehlen nur noch divs, nicht nur als block sondern auch in-line, mit css-Klasse und style-Angabe, etc. → Hier oder ein extra Teil?

Schlußbemerkung

Das mit dem "wir bleiben unserem Verhaltensmuster treu" dient in diesem Teil des Tutorials der Didaktik. Es ist ein Tutorial für Anfänger/Einsteiger. Da aber auch Umsteiger angesprochen werden, eine kleine Schlußbemerkung zur Vererbung. Als Umsteiger kommst du von einem anderen CMS, hast dort evtl. Plugins programmiert. Vielleicht bist du Softwareentwickler und bringst viele Jahre Background aus der Praxis mit. Usw. usw. Das nachfolgende ist aber durchaus auch für Anfänger interessant. Nicht damit du das alles sofort kannst und von jetzt auf nachher Vollprofi sein mußt. Aber wenn du es bereits gehört hast, ist es (hoffentlich) im Hinterkopf, aus dem du es rauskramen kannst, wenn du es später brauchen wirst.

Unser "Verhaltensmuster" war, für unseren Fall eine neue NodeType zu definieren, zu schauen, wie es Neos (oder andere Quellen) gemacht haben und uns das für uns notwendige zusammenkopieren. Der zweite Weg war das mit dem "redefinieren". Wir hatten kein eigenes sitText erstellt, sondern das vorhandene Text verändert. Wie oben bereits angesprochen, kann das im Alptraum enden. Mehrere Agenturen betreuen nacheinander die Seite, unterschiedlichste Plugins werden eingesetzt, alle und alles ändert das Standardverhalten, systemweit.

Beide Wege sind Extreme. Im ersten Fall schaffst du dir eine Insellösung. Die gilt nur für deine Insel, nur für dich allein. Installierst du ein Plugin, das nicht horizontal seine einzelne in sich abgeschlossene Aufgabe erledigt, sondern ein Plugin, das vertikal übergreifend etwas tun soll, hat dieses Plugin keine Chance auch deine NodeTypes dabei zu berücksichtigen, da es keinerlei Aufhängungspunkt mehr gibt, woran das Plugin erkennt was dein NodeType sein könnte, in welchen Teil oder welche Kategorie es es einordnen soll. Der zweite Fall endet bei langjährigem Bestehen vermutlich im Chaos, in der nicht mehr Wartbarkeit.

Beispiel. Du installierst ein Plugin, das den Aloha ersetzen kann. Dafür "klinkt" sich das Plugin – wie auch immer – so in Neos ein, daß es erkennen kann, wann der Aloha dargestellt würde, den wegschubst und sich selbst auf den Hocker setzt. Darüberhinaus wäre es toll, daß du sogar bequem angeben konntest, daß es das nicht bei Headlines tun soll, sondern nur bei Text-Inhaltselementen. Das Plugin wird mit soetwas wie isinstanceof arbeiten. Brauchst das nicht wissen, nur daß du es mal gehört hast. Das Plugin hat keine Chance zu wissen, daß unser sitText ein Text-Inhaltselement ist. Es kann nicht hellsehen.

Deshalb der dritte Weg: mit Vererbung arbeiten. Statt ein komplett neues sitText zu erstellen, lassen wir unser sitText von dem Standard Text abgeleitet sein. So kann in unserem Beispiel das Plugin auch in unserem sitText den Editor austauschen. Das Plugin erkennt daran, daß Text unser Vorfahr ist, daß wir eine "Variante" dieses Typs sind. Da wir aber ein Nachfahr sind, verändern wir durch unsere Definition nicht alle anderen Teile des Gesamtsystems. Wir müssen keine Angst haben, daß wir bestehende uralte Seiten damit verändern. Auch Plugins beeinflussen wir nicht und stören sie nicht in ihrer Arbeit.

Im "echten" Leben wirst du also nicht die beiden Extreme nutzen, sondern mit Vererbung arbeiten. Im Falle von Headline oder Text ist es recht einfach und eindeutig nachzuvollziehen. Bei Image müßte man abwägen. Wir hatten ja einen Teil "weggeworfen". Da müßte man sehen, ob man anstatt Image lieber SimpleImage unter den superTypes listet. Alles in allem: einen Königsweg gibt es nicht.

Vermißt du nicht etwas?

Wollten wir nicht oben im Logobereich das Neos-Logo neben das SuebenIT-Logo bekommen? Die wrapping divs haben wir für die Standard-NodeTypes in den Griff bekommen. Aber bei der ContentCollection fügt es Neos immer noch ein. Wir landen wieder bei der Metapher "wie im richtigen Leben" ... Mann kann nichts alles haben :-) Die ContentCollections scheinen tiefer im System zu liegen. Wir werden einen Kompriss finden müssen. Anstatt Neos an alle unsere Bedürfnisse anzupassen, werden wir bei manchen Punkten uns an Neos anpassen. Bei den ContentCollections heißt das, daß wir damit leben und unser Page-Template (in Tutorial Teil 5) so umgestalten, daß wir die beiden Grafiken nebeneinander bekommen.

Lassen wir es soweit gut sein. Weiter gehts mit dem nächsten Teil: Menüs in Neos 3. (TODO verlinken)


Hinweis. Im letzten Teil Neos 3 Tutorial Quintessenz findest du eine kleine Übersicht, wo du was auf dem Webspace in den Neos-Verzeichnisses findest. Dh. wo kommt der Fusion-Code hin, wo die Templates? Wo finde ich die von Neos definierten Inhaltselemente? Usw.