Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Neos CMS: Node Kingdom

Neos CMS: Node Kingdom

How to handle content modelling and integration with Neos CMS, talk about best practice for today webproject.

5c35ea5ff0ba6e46116b1470509f96d1?s=128

Dominique Feyer

March 14, 2014
Tweet

Transcript

  1. Inspiring Conference 2014 TYPO3 Neos Node Kingdom

  2. Inspiring Conference 2014 Dominique Feyer Cofounder of ttree ltd +

    medialib.tv ltd
  3. Inspiring Conference 2014 What’s next Short introduction to Structured Content

    Think about content Your first custom Node Type A bit more advanced example
  4. Inspiring Conference 2014 Let’s start with some definitions

  5. Inspiring Conference 2014 What’s a Kingdom ? A really nice

    place to live in Where a king take care of each citizen A really romantic vision, no ?
  6. Inspiring Conference 2014 Do you see the magic ?

  7. Inspiring Conference 2014 What’s a Node ? A node is

    a small piece of content Content is your first class citizen Can be structured Nodes can contain other node
  8. Inspiring Conference 2014 Entity or Node A different way to

    interact with content In Neos, try to focus on Node - Inline editing, Workspace, Content Dimension - FlowQuery & EEL, Import / Export, … ! Standard Doctrine Entity can be used for specific needs
  9. Inspiring Conference 2014 We are not 
 Page Centric

  10. Inspiring Conference 2014 It’s not a 
 Page Tree It’s

    a 
 Document Tree
  11. Inspiring Conference 2014 A Page is just a specific Document

    A document can be anything
  12. Inspiring Conference 2014 When did you need a new Document

    Type You need an URL to access this content - Customer - Project Reference - People - Blog post, News, …
  13. Inspiring Conference 2014 Do you remember your first jQuery plugin

    ? That was maybe not « yours » What about JavaScript internals ?
  14. None
  15. Inspiring Conference 2014 Keep your 
 content clean As an

    editor I need special formatting for some paragraph
  16. Inspiring Conference 2014

  17. Inspiring Conference 2014

  18. Inspiring Conference 2014 How it’s done ? A new Node

    Type based on the default Text Node Type A small piece of TypoScript
  19. Inspiring Conference 2014 Please define
 Node Type A Node Type

    define the structure of a node A Node Type can have Super Type (inheritance) The structure of a node can change
  20. Inspiring Conference 2014 Add a Node Type 'Ttree.OfficialWebsite:Teaser': superTypes: -

    'TYPO3.Neos.NodeTypes:Text' ui: label: Teaser inspector: groups: theme: label: Theme position: 5 properties: reverse: type: boolean ui: label: 'Gray Theme' reloadIfChanged: true inspector: group: theme
  21. Inspiring Conference 2014 A short TypoScript prototype(Ttree.OfficialWebsite:Teaser) > prototype(Ttree.OfficialWebsite:Teaser) <

    prototype(TYPO3.Neos.NodeTypes:Text) { attributes.class = ${node.properties.reverse ? 'teaser teaser-reverse' : 'teaser'} }
  22. Inspiring Conference 2014 A bit more advanced As a client

    I need to display a list of persons on my website Each person has their own profile I need to be able to insert person address and a link to the profile on any page
  23. Inspiring Conference 2014 What’s a Person Your first 
 Abstract

    Node Type 'Ttree.InspiringConf:Schema.Person': abstract: true ui: inspector: groups: person: label: 'Person' position: 1 properties: personName: type: string ui: label: 'Name' reloadIfChanged: TRUE inspector: group: person
  24. Inspiring Conference 2014 Your second 
 Abstract Node Type What’s

    a Postal Address 'Ttree.InspiringConf:Schema.PostalAddress': abstract: true ui: inspector: groups: postalAddress: label: 'Postal Address' position: 2 properties: postalAddressStreetAddress: type: string ui: label: 'Street Address' reloadIfChanged: TRUE inspector: group: postalAddress postalAddressPostalCode: type: string ui: label: 'Postal Code' reloadIfChanged: TRUE inspector: group: postalAddress postalAddressLocality: type: string ui: label: 'Locality' reloadIfChanged: TRUE
  25. Inspiring Conference 2014 Document 
 Node Type Extend the TYPO3.Neos:Document

    Extend your abstract nodes Add a content collection 'Ttree.InspiringConf:Person': superTypes: - 'TYPO3.Neos:Document' - 'Ttree.InspiringConf:Schema.PostalAddress' - 'Ttree.InspiringConf:Schema.Person' ui: label: 'Person' icon: 'icon-user' group: inspiringCon childNodes: profile: type: 'TYPO3.Neos:ContentCollection'
  26. Inspiring Conference 2014 TypoScript prototype(TYPO3.Neos:PrimaryContent).TtreeInspiringConfPerson { condition = ${q(node).is('[instanceof Ttree.InspiringConf:Person]')}

    type = 'Ttree.InspiringConf:Person' @position = 'start' } prototype(Ttree.InspiringConf:Person) < prototype(TYPO3.TypoScript:Template) { templatePath = 'resource://Ttree.InspiringConf/Private/Templates/NodeTypes/Perso ! attributes = TYPO3.TypoScript:Attributes { class = 'person-profile-page' data-ttree-region = ${node.properties.postalAddressRegion} data-ttree-country = ${node.properties.postalAddressCountry} } ! personName = ${node.properties.personName} postalAddressStreetAddress = ${node.properties.postalAddressStreetAddress} postalAddressCountry = ${node.properties.postalAddressCountry} postalAddressPostalCode = ${node.properties.postalAddressPostalCode} postalAddressLocality = ${node.properties.postalAddressLocality} postalAddressRegion = ${node.properties.postalAddressRegion} …
  27. Inspiring Conference 2014 You say PrimaryContent ? Use it only

    one time in your page Neos know where to render the main content Don’t use it for your sidebar prototype(TYPO3.Neos:PrimaryContent).TtreeInspiringConfPerson { condition = ${q(node).is('[instanceof Ttree.InspiringConf:Person]')} type = 'Ttree.InspiringConf:Person' @position = 'start' }
  28. Inspiring Conference 2014 Let’s preparing the view … Will be

    rendered in your PrimaryContent area ! prototype(Ttree.InspiringConf:Person) < prototype(TYPO3.TypoScript:Template) { templatePath = 'resource://Ttree.InspiringConf/Private/Templates/NodeTypes/Pers ! attributes = TYPO3.TypoScript:Attributes { class = 'person-profile-page' data-ttree-region = ${node.properties.postalAddressRegion} data-ttree-country = ${node.properties.postalAddressCountry} } ! personName = ${node.properties.personName} postalAddressStreetAddress = ${node.properties.postalAddressStreetAddress} postalAddressCountry = ${node.properties.postalAddressCountry} postalAddressPostalCode = ${node.properties.postalAddressPostalCode} postalAddressLocality = ${node.properties.postalAddressLocality} postalAddressRegion = ${node.properties.postalAddressRegion} ! profile = TYPO3.Neos:ContentCollection { nodePath = 'profile' } }
  29. Inspiring Conference 2014 The Fluid template {namespace neos=TYPO3\Neos\ViewHelpers} <div{attributes ->

    f:format.raw()}> <div itemscope itemtype="http://schema.org/Person"> <h1 itemprop="name">{personName}</h1> <span itemprop="address" itemscope itemtype="http://schema.org/PostalAddress <span itemprop="streetAddress"> {postalAddressStreetAddress} </span> | <span itemprop="addressCountry">{postalAddressCountry}</span>, <span itemprop="postalCode">{postalAddressPostalCode}</span>, <span itemprop="addressLocality">{postalAddressLocality}</span>, <span itemprop="addressRegion">{postalAddressRegion}</span> </span> </div> ! <div class="customer-profile"> {profile -> f:format.raw()} </div> </div>
  30. Inspiring Conference 2014 The final rendering

  31. None
  32. None
  33. Inspiring Conference 2014 Link to the profile from any page

    ?
  34. Inspiring Conference 2014

  35. Inspiring Conference 2014 What about a new node type ?

    As an editor I need to insert on any page a contact address
  36. Inspiring Conference 2014 Node Type 'Ttree.InspiringConf:ContactAddress': superTypes: - 'TYPO3.Neos:Content' nodeLabelGenerator:

    'Ttree\InspiringConf\Domain\Model\PersonNodeLabelGenerator' ui: label: 'Contact Address' group: 'inspiringCon' inspector: groups: document: label: 'Address' position: 1 properties: person: type: reference ui: label: 'Person' reloadIfChanged: true inspector: group: 'document' editorOptions: nodeTypes: - 'Ttree.InspiringConf:Person'
  37. Inspiring Conference 2014 TypoScript prototype(Ttree.InspiringConf:ContactAddress) > prototype(Ttree.InspiringConf:ContactAddress) < prototype(TYPO3.Neos:Content) {

    templatePath = 'resource://Ttree.InspiringConf/Private/Templates/NodeTypes/ContactAdd ! person = ${node.properties.person} hasPerson = ${node.properties.person ? TRUE : FALSE} ! attributes = TYPO3.TypoScript:Attributes { class = 'person-profile-inline' style = 'background: #CCC; padding: 10px; margin-bottom: 10px;' data-ttree-region = ${this.person.properties.postalAddressRegion} data-ttree-country = ${this.person.properties.postalAddressCountry} } ! personName = ${this.person.properties.personName} postalAddressStreetAddress = ${this.person.properties.postalAddressStreetAddress} postalAddressCountry = ${this.person.properties.postalAddressCountry} postalAddressPostalCode = ${this.person.properties.postalAddressPostalCode} postalAddressLocality = ${this.person.properties.postalAddressLocality} postalAddressRegion = ${this.person.properties.postalAddressRegion} }
  38. Inspiring Conference 2014 Template {namespace neos=TYPO3\Neos\ViewHelpers} <div{attributes -> f:format.raw()}> <f:if

    condition="{hasPerson}"> <f:then> <div itemscope itemtype="http://schema.org/Person"> <div style="font-weight: bold;" itemprop="name">{personName}</div> <div itemprop="address" itemscope itemtype="http://schema.org/PostalAddres <div itemprop="streetAddress"> {postalAddressStreetAddress} </div> <div> <span itemprop="addressCountry">{postalAddressCountry}</span> - <span itemprop="postalCode">{postalAddressPostalCode}</span> <span itemprop="addressLocality">{postalAddressLocality}</span> </div> </div> </div> </f:then> <f:else> <strong> Please select a Person<br/> in the Inspector </strong> </f:else> </f:if> </div>
  39. Inspiring Conference 2014

  40. Inspiring Conference 2014 nodeLabelGenerator: 'Ttree\InspiringConf\Domain\Model\PersonNodeLabelGenerator' Custom node label, but why

    ?
  41. Inspiring Conference 2014 class PersonNodeLabelGenerator extends DefaultNodeLabelGenerator { public function

    getLabel(AbstractNodeData $nodeData, $crop = TRUE) { if ($nodeData->hasProperty('person') === TRUE && $nodeData->getProperty('person $label = 'Link to: ' . strip_tags($nodeData->getProperty('person')-> getProperty('personName')); } else { $label = parent::getLabel($nodeData, FALSE); } if ($crop === FALSE) { return $label; } ! $croppedLabel = \TYPO3\Flow\Utility\Unicode\Functions::substr($label, 0, NodeInterface::LABEL_MAXIMUM_CHARACTERS); return $croppedLabel . (strlen($croppedLabel) < strlen($label) ? ' …’ : ''); } }
  42. Inspiring Conference 2014 class PersonNodeLabelGenerator extends DefaultNodeLabelGenerator { public function

    getLabel(AbstractNodeData $nodeData, $crop = TRUE) { if ($nodeData->hasProperty('person') === TRUE && $nodeData->getProperty('person $label = 'Link to: ' . strip_tags($nodeData->getProperty('person')-> getProperty('personName')); } else { $label = parent::getLabel($nodeData, FALSE); } if ($crop === FALSE) { return $label; } ! $croppedLabel = \TYPO3\Flow\Utility\Unicode\Functions::substr($label, 0, NodeInterface::LABEL_MAXIMUM_CHARACTERS); return $croppedLabel . (strlen($croppedLabel) < strlen($label) ? ' …’ : ''); } }
  43. Inspiring Conference 2014 class PersonNodeLabelGenerator extends DefaultNodeLabelGen public function getLabel(AbstractNodeData

    $nodeData, $cr if ($nodeData->hasProperty('person') === TRUE && $no $label = 'Link to: ' . strip_tags($nodeData->getPrope getProperty('personName')); } else { $label = parent::getLabel($nodeData, FALSE); } if ($crop === FALSE) { return $label; } ! $croppedLabel = \TYPO3\Flow\Utility\Unicode\Function return $croppedLabel . (strlen($croppedLabel) < strlen($ } }
  44. Inspiring Conference 2014 Lazy mode pseudo live demonstration

  45. Inspiring Conference 2014

  46. Inspiring Conference 2014 We, as a community, are currently redefining

    the future of CMS Thanks
  47. Inspiring Conference 2014 Fork me on Github
 https://github.com/dfeyer/Ttree.InspiringConf

  48. Follow us @ttreeagency Follow me @dfeyer Questions