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.

Dominique Feyer

March 14, 2014
Tweet

More Decks by Dominique Feyer

Other Decks in Technology

Transcript

  1. Inspiring Conference 2014
    TYPO3 Neos
    Node Kingdom

    View Slide

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

    View Slide

  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

    View Slide

  4. Inspiring Conference 2014
    Let’s start with
    some definitions

    View Slide

  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 ?

    View Slide

  6. Inspiring Conference 2014
    Do you see the magic ?

    View Slide

  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

    View Slide

  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

    View Slide

  9. Inspiring Conference 2014
    We are not 

    Page Centric

    View Slide

  10. Inspiring Conference 2014
    It’s not a 

    Page Tree
    It’s a 

    Document Tree

    View Slide

  11. Inspiring Conference 2014
    A Page is just a
    specific Document
    A document can
    be anything

    View Slide

  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, …

    View Slide

  13. Inspiring Conference 2014
    Do you remember
    your first jQuery
    plugin ?
    That was maybe not « yours »
    What about JavaScript internals ?

    View Slide

  14. View Slide

  15. Inspiring Conference 2014
    Keep your 

    content clean
    As an editor
    I need special formatting for some paragraph

    View Slide

  16. Inspiring Conference 2014

    View Slide

  17. Inspiring Conference 2014

    View Slide

  18. Inspiring Conference 2014
    How it’s done ?
    A new Node Type based on the default Text Node Type
    A small piece of TypoScript

    View Slide

  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

    View Slide

  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

    View Slide

  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'}
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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'

    View Slide

  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}

    View Slide

  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'
    }

    View Slide

  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'
    }
    }

    View Slide

  29. Inspiring Conference 2014
    The Fluid template
    {namespace neos=TYPO3\Neos\ViewHelpers}
    f:format.raw()}>

    {personName}

    {postalAddressStreetAddress}
    |
    {postalAddressCountry},
    {postalAddressPostalCode},
    {postalAddressLocality},
    {postalAddressRegion}


    !

    {profile -> f:format.raw()}


    View Slide

  30. Inspiring Conference 2014
    The final
    rendering

    View Slide

  31. View Slide

  32. View Slide

  33. Inspiring Conference 2014
    Link to the
    profile from any
    page ?

    View Slide

  34. Inspiring Conference 2014

    View Slide

  35. Inspiring Conference 2014
    What about a new
    node type ?
    As an editor
    I need to insert on any page a contact address

    View Slide

  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'

    View Slide

  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}
    }

    View Slide

  38. Inspiring Conference 2014
    Template
    {namespace neos=TYPO3\Neos\ViewHelpers}
    f:format.raw()}>



    {personName}

    {postalAddressStreetAddress}


    {postalAddressCountry} -
    {postalAddressPostalCode}
    {postalAddressLocality}






    Please select a Person

    in the Inspector




    View Slide

  39. Inspiring Conference 2014

    View Slide

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

    View Slide

  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) ? ' …’ : '');
    }
    }

    View Slide

  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) ? ' …’ : '');
    }
    }

    View Slide

  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($
    }
    }

    View Slide

  44. Inspiring Conference 2014
    Lazy mode
    pseudo live
    demonstration

    View Slide

  45. Inspiring Conference 2014

    View Slide

  46. Inspiring Conference 2014
    We, as a community, are currently
    redefining the future of CMS
    Thanks

    View Slide

  47. Inspiring Conference 2014
    Fork me on Github

    https://github.com/dfeyer/Ttree.InspiringConf

    View Slide

  48. Follow us @ttreeagency
    Follow me @dfeyer
    Questions

    View Slide