In progress for 4.5

Introduces new templating in preparation for Magnolia 5.0. Implementation tracked in SCRUM@jira.

Goals

  • improve the templating to make it more intuitive
  • prepare the new page editing
  • align JSP and FreeMarker templating
  • provide maximum possible backward compatibility

Summary

  • rename paragraphs to (page) components
  • introduce areas
    • sub elements of a template
    • container for components
  • provide a new minimal set of directives
    • area, render, edit
    • better attribute names, aligned to the JCR API
  • provide a set of standard functions to allow more complex operations
  • streamline content expressions
    • implementation is based on the Map interface
    • allow using the JCR API: cmsfn.asNode(content).getProperty("bal")
  • align component, area and page template definitions
    • use the same renderer
    • use the same definition beans

Paragraphs become (page) components

  • for new Magnolia users and developers it was always difficult to grasp the term 'paragraph' as used in Magnolia
  • CMS like Magnolia are known as component based system

Align page and component templates

Definitions

  • use a single registry
    • to avoid checks like in the rendering engine
  • use a common template definition object
  • drop the differentiation of components and page templates
    • structure the templates with folders
    • components/content/textImage, pages/article
    • the path is used to reference a component/dialog

Renderers

  • one renderer interface and a single renderer registry
  • same set of context objects
  • JSP: decide based on situation if we forward or include (forward if main content is the same as the current)

Template Definitions (Enhancements)

  • additional context objects can be configured per template
  • dialog can be configured in the template definition rather than being referenced

Template Variations

Template variations are similar to the former sub-templates.

  • all templates can have variations (including areas and paragraphs)
  • configured as a Map

Default variations

  • json: renders a json representation of the content
  • xml: renders a xml representation of the content
    • formated similar to the JCR document view

Selection mechanism

  • the variation is set in the aggregation state
  • by default this is the extention
  • other example: variations per device

Inheritance (from the base template)

  • the template is merged with the 'base' template
  • works the same way as in STK: current template and site's prototype

Areas

  • templates have areas (regions having components)
  • configured as a map in the template definition
  • list available components (like in STK)
  • defines the template script to be use
  • enabled flag
  • can have its own edit dialog

Types

Type

Description

Structure

Context

list

  • collection of components
  • creates an area node which contains the components nodes
  • minimal and maximal number of components can be set
  • the area bar has an "add component" button
page (mgnl:page)
  - main (mgnl:area)
    - component[0]
    - component[1]
    - component[2]
  • content: area node
  • components: list of components

single

  • an area which holds one and only one single paragraph
  • creates one single node which is the paragraph's node
    • no container node like for lists
  • optional single areas
    • allows the explicit creation and deletion of the paragraph
  • the area bar is the edit bar
    • the edit bar is not going to be rendered
page (mgnl:page)
  - stage (mgnl:area)
    - title
    - component (mgnl:component)
      - title
  • content: area node
  • component: the component node

no-component

  • areas where no component can be added
  • examples: navigation, bread crumbs, ..
page (mgnl:page)
  - navigation (mgnl:area)
    - levels
  • content: area node

Template script

  • can be inlined (in the area tag)
  • content object is the area's node

Non existing areas

  • content is null if the area is not existent
  • the script can render a placeholder

Model

  • an area has a model like all templates do

Available components

  • list of roles, groups and users
  • enabled flag
  • bean: ordered map

Sub Areas

  • areas can have sub areas

Auto generations

  • both single and list types can be auto generated
    • on creation of the page
  • if a generated component has a required property it is not deletable
  • if a generated component has its orderable property set to false it can't be moved

Inheritance

  • can be configured
  • see chapter 'inheritance'

Tags/Directives

  • minimal set of directives
  • use jcr naming: node, property, path

Common attributes for passing the content to use

Attribute

Description

Default Value

content

a Node or ContentMap

 

workspace

the workspace used if the uuid or path is defind

same as of the current content

uuid

 

 

path

the path in the workspace

 

cms:area

  • type list: replacement of the iterator tag and new bar
  • type single: combines the new and edit bar

Attribute

Description

Default Value

name

the area name, will work on def.areas[MAGNOLIA5:name ]

 

area

an area definition object

 

components

list of available components

by area definition

script

 

by area definition

placeholderScript

 

null, by area definition

type

list or single

list, by area definition

dialog

 

null, by area definition

inherit

 

false

Context

In addition to the 'normal' context objects the followings objects are provided

  • components: list of the area's components, includes inherited components and respects the order information
  • component: in case of a single area, might be inherited

Inline Areas

  • Status: Proposal, not implemented yet, see MAGNOLIA-4586
  • Allow definitions directly in the template script (without definition)
# every thing is configured at def.areas[name]
[@cms.area name="main"/]

# inline
[@cms.area name="main" components="paragraphs/content/textImage, paragraphs/content/linkList"]
   <div id="main">
   [#list components as component]
       [@cms.render content=component ]
   [/#list]
   </div>
[/@cms.area]

# inline single
[@cms.area name="stage" components="paragraphs/stages/stageA, paragraphs/stages/stageB" type="single"]
   <div id="stage">
       [@cms.render content=component ]
   </div>
[/@cms.area]

cms:component

  • Renders a component
  • Similar to the former includeTemplate
  • Needs an existing node

Attribute

Description

Default Value

editable

if any editing elements should be shown. mainly useful if content is inherited

cmsfn.isFromCurrentPage()

template

name of the template definition to use

the template defined on the node

cms:edit

  • Renders an edit bar (or button)
  • Requires a node (is not a new bar)
  • Can edit any node (for instance data module content)

Attribute

Description

Default Value

dialog

name of the edit dialog

 

format

button or bar

bar

[@cms.edit content=contact dialog="data/contact"/]

In practice, you rarely need to add the cms:edit to a script. It is injected into editable areas and components automatically.

cms:context

  • former attribute tag used for cms:render
  • ctx.name
  • attributes are removed after the rendering
    • former values are restored
[#assign counter = counter +1]

[@cms.render] content=child]
    [@cms.context name="counter" value=counter/]
[/@cms.render]


# in the paragraph script
${ctx.counter}

Functions

  • Freemarker: cms context object provides functions
  • JSP: static class delegating to the context object
    • standard prefix cmsfn

Function

Description

asJCRNode(content)

to allow calls to the jcr API

asContent(node)

 

parent(content, [MAGNOLIA5:type])

if the type is passed the first matching ancestor is returned

children(content, [MAGNOLIA5:type])

 

uuid(content)

 

wrap(content)

add wrappers used in the renderer

link(content)

 

edit(content, propertyName)

adds editor markup attributes

decode(text)

decode content if the text is plain HTML code

....

 

Utils

  • other function libraries can be added
    • like cmsutil.*
  • cmsfn.* should just provide the essential functions

Content expressions

  • align freemarker and JSP
  • use a Map instead of content (Magnolia)
  • drop ContentModel -> allow method calls on JCR nodes
    • but still support TemplateNodeModel for ?children, ?parent, ...

ContentMap

  • extends Map interface
    • does not implement Content or Node
    • has a asJCRNode() method
  • all values are encoded
  • all links are processed
  • resolution
    • property
    • child node
  • content.@uuid == cmsfn.uuid(content)
  • content?children == cmsfn.children(content)

Binaries

  • are now nodes (nt:resource)
  • content.image --> cmsfn.link(content.image) to create a link
  • content.image.size returns the size property

Namespaces

  • content["mgnl:template"]
  • content.template --> if no property "template" exists it checks if an other (with namespace) matches

Compatibility

  • a separate compatibility package is needed (to keep new code clean)

Template and 'paragraph' definitions

  • definition has to be migrated (converter will be provided)
  • type is: jsp4x, ftl4x

Scripts (if of type jsp4x or ftl4x)

  • old directives/tags are provided
  • content (of type Content) is passed
  • a single template script cannot mix old and new style (also true for includes)

Editor

  • render the edit bars as before (where the cms:edit tag is)
  • new bar -> render them as before (no autocreation of areas)
  • no area bars

Inline (Magnolia 5)

  • annotated tags can be edited inline

    <h2 ${cmsfn.edit(content, "title"}>${content.title}</h2>
    

Inheritance

Inheritance is supported by areas. In addition a set of functions and standardized content properties support custom solutions.

An area with inheritance enabled inherits properties and components from areas in its parent pages. Inheritance can be set to include only properties, only components or both.

By default properties and only components with a property 'inheritable' set to true are included.

Configuration

Options for components

  • all
  • none
  • filtered, all components with a property 'inheritable' set to true (default)

Options for properties

  • all (default)
  • none

Component order

  • a property 'nodeComparatorClass' set to a class name of a class implementing java.util.Comparator<Node>

Component filtering

  • a property 'predicateClass' set to a class name of a class extending info.magnolia.jcr.predicate.AbstractPredicate<Node>

Editing

  • inherited paragraphs are not editable

Functions

  • cmsfn.inherit(content, innerPath): ContentMap
  • cmsfn.inheritProperty(content, innerPath): String
  • cmsfn.inheritList(content, innerPath, aggregate): Collection<ContentMap>
    • aggregate: collect over several ancestors
    • respects the orderdering information
  • cmsfn.isFromCurrentPage(content)
  • cmsfn.isInherited(content)

Special properties and mixins

TODO: use mixins?

TODO: list all special properties

References

TODO

  • property mgnl:reference
  • a wrapper replaces the node with the referenced node
  • transparent for the template
  • properties can be overriden
  • very similar (or the same?) as the extends feature in configurations

Editor markup

The editor markup is only rendered if the page is shown in author mode. The editor uses this elements to inject his components into the page.

separators
Added by the Area- or EditComponent to mark the boundaries

<!-- cms:begin cms:content="ws:path" -->
<!-- cms:end cms:content="ws:path" -->

area

<cms:area content="ws:path" name="name"paragraphs="paragraphs/textImage, ..." type="single" dialog="areas/main">

edit bars

<cms:edit content="ws:path" format="bar" dialog="paragraphs/textImage">

inline
Bind an element to a property to make it directly editable

<h2 ... cms:edit="inline" cms:content="ws:path@prop"">

Editor

Magnolia 4.5

TODO

Magnolia 5

  • the content is rendered in an iframe
  • this isolates the editor's and page's javascripts, css ...
  • no initialization code in the page
  • No labels

81 Comments

  1. Would it be possible to have the inherit define the levels, or have an option to hide/show an inherited paragraph?

    1. level
      we could, as an alternative you can do the following

      [#assign footer = cms.inherit(myPage, "footer")]
      [#if cms.level(footer) < 1]
      

      hiding
      You probably think about collections where you define on each paragraph wether it should be shown in sub pages. Yes I am going to add that.

      1. so you mean it will work if I have home/section/article1, article2 and I want the footer to be in home and in the articles but not on section?

        1. that is a tough one, in this case you can't get around some custom codingv

          1. I think a solution/possibility to this would be:

            That you can define inheritance itself on the Area:

            • If the Area should inherit from the parent's Area (the collection content). Of course thats the same as enabling the inheritance on the Area.
            • and/or the Page's content in this Area can be prohibited of beeing inherited down (comparable to the paragraphs config 'notInheritable'

            Like this you could inherit a completet collection from the parent (the footer) or not, and a subpage can still inherit form the page two levels up. Inheritance just jumps over the Area form the first parent where 'notInheritable' is enabled.

            1. yep, the inheritance can now be configured in the area: exclution and ordering will be supported

  2. I like the direction this is going. It seem like it should result in cleaner, easier templates, while at the same time providing richer functionality. Nice work!

    It does seem at some point like adding more functionality may have potential to break MVC, and have logic creep into the Freemarker code. For example, does exposing the method calls on JCR nodes allow you to update JCR content, or make unit testing your Freemarker templates harder? (Probably a minor concern, but it occurred to me while reading through this.)

    1. we should ship a patrol module which detects mischief and sends a notification to the system architect (wink)

  3. Topic: Area - Paragraphs to add:

    1. Paragraphs in Areas should have an 'enabled' flag (info.magnolia.module.templatingkit.templates.ParagraphConfig bean right now)

    2. The paragraphs should be placed into a Map and not collection (most preferable a LinkedHashMap for keeping the original order) so:

    • They can be added only once
    • While merging def's a site def defined paragraph can be overwritten and there fore being disabled. That does not only count got STKs def mergin, it alos applies to the 'ectends' possibilies and mechanism.

    3. Not only 'role' should be defineable on the Paragraphs (and the Templates and) availability, but also Groups (and Users). Mainly groups, cause it a complete group should be able to add it, its possible the a group is defined by many roles. You would have to add all of them to fetch for sure all according groups.

  4. Topic: Area - Creating singletonParagraphs

    Wouldn't it make more sence to implement a default case on Area:

    • Auto Generate Paragraphs -> you can just define paragraphs which get automatically generated
    • And then on top of it the special case/more specific case -> singleton of a auto generated paragraph

    I have bit trouble to understand the singleton ideology when more than one paragraph is generated. Does that mean, that you can define more than one, but only one of each?

    If so that would mean for me, that the general case is configurable as such on a area:

    Area/paragraphs/concreteParagraphToAdd/option1=paragraphName

    option2=isAutoGenerated

    option3=isSingleton

    And the is singleton just prohibits, that the newBar allows to add the same type again.

    The newBar:

    1. Just contains all paragraphs with no property 'singleton'
    2. Only shows up at all, if there is a praragraph without 'singleton'

    -> adding one paragraph with 'singleton' -> just creates it and no new bar appears

    -> adding a second with 'autoGenerate', will be auto generated. A new bar appears, but only the second is addable.

    Another question that rises here is:

    Are autogenerated paragraphs (and singletons) movable over the edit bar? I think they shouldn't.

    1. there was probably some confusion. the auto generation has nothing to do with the fact wether this area is a collection or not. I renamed the attribute to 'collection' which can be set to true or false

      1. I still don't see the differentiation of the colleciton and the singleton. Basically you always have to define what paragraph can be added, only one or more then one.

        With allowing on the 'paragraphToAdd' the two options 'autoGenerated and/or singletong' you have all in one. No differentiation needed.

        And you have some additional functionality you don't have otherwise: you can define, that a Flash paragraph can be added only once, but others still can be added. So the Flash paragraph would be some sort of singleton too (only once in a Area).

        1. The availability of paragraphs and the auto generation are two independent things. An example illustrating this is the following:

          • available: text, image
          • auto generation: abstract (text), article image (image), content (text)

          This two configurations can not be merged into one in a meaningful way.

          The difference of collections and singletons (do we need a better name?) is the structure which gets created. In case of a singleton, the area node IS the paragraph node. The examples as found in todays STK:

          • collection: main, extras
          • singleton: stage, footer
  5. General note on Map vs. Collections in beans:

    I think all collection nodes definable on templates and paragraphs should always be Maps and not Lists or Collections. So the same item can't be added twice.

    So on pobulation of the beans map you get automatically overwrite of already predefined configurations.

  6. Topic: Functions

    • getArea(paragraphContentNode) -> find out in which are the paragraph is located (think of subsub paragraphs)
    • getAllAreas(pageContentNode) -> return directly all area collection nodes of a certain page node.

    So you could do in a paragraph: getAllAreas(page(paragraphContentNode))and having a list of all area collection nodes

    • getAncestors(paragraphOrPageNode) -> returns all page nodes of the ancestors (only page nodes)
    1. we are going to add more functions as needed for now I am rather reluctant

  7. Topic: Content Expressions:

    • the @name and @handle is very usfull too
    • @metaData would be nice for directly accessing the metaData itself for rendering (modificationDate could be useful) -> @metaData.templae , @metaData.activation and so on
    1. you can already do ${content.metaData.xxx) where xxx is a property of the info.magnolia...MetaData class.

      1. Oh, didn't know, that suprises me! Thought that were not providing any acces directly to the content's API for not beeing able to write into the content by the template. So I ecpected that only the .@attributes are provided for this, and no compley object is server by the content's map.LIke this (just tested) I can write by the template script directly into the meta data of this content node.

        Good to know, thnx

    2. @path to be aligned with the JCR API

  8. Topic: Common attributes for passing the content to use:

    Do you mean by workspace the Area? Or the JCR workspace?

  9. Topic: Inheritance ordering:

    To your question of ordering the collections:

    You wrote, that the paragraph will have by default an injected edit bar.

    Couldn't be injected a different edit bar on inherited parahaphs so:

    • It knows the complete merged inherited collection
    • It only provides a move node
    • When moving, it stores on the paragraph a property or even a MetaData value 'ordered before' or/and 'orderedAfter' containing the uuid of the nodes before and after.

    So when the inheritance tag is collection the nodes, it can reorder them if the uuid is a part of the complete merged inherited collection

    1. I updated the following:

      • ordering is now possible (stored in the area node)
      • no magic edit bars
      • no editing of inherited content (just moving)
  10. Topic: Templates and Paragraphs in the same registry:

    I personally don't think thats  good idea, even I see the benefits of it. The cons:

    • You can't register a paragraph and a template with the same name
    • Its now seeable to users, if it is a template or a paragraph, what leads to creating substructure -> next point
    • We already have quite deep subsctructures of the paragraphs, so i guess it would become even more confusing/harder to find a element
    • If there wouldn't be a substructre on the as first subfolders as templates & paragraphs, you don't see in extends property what you're extending
    • If creating as best practice substructure on the as first subfolders as templates & paragraphs, then I don't see the gain compared to having them on top level in the module definition.

    Probably I understand it wrong, and you mean its just one Registry, but still they are defined separetly on the module's level, but just registered by the same ManagerRegistry.

    Then its a good Idea I think.

    1. We are going to merge them. So you will structure them in folders and reference them by path. This has the following advantages:

      • no system wide unique names (no module prefixes)
      • one knows where the component is configured (path)
      • rendering code can be simplified
      • easier to understand, especially if you are new to Magnolia (there are templates and dialogs)
      1. Referencing them by path makes it very hard to maintain projects.

        If ever something is changed in the structure, you must know, what all is referencing it to change the path pointing to it.

        No prefixes sounds nice, but I think in projects it tells something about the parargaphs to newbees too -> where it is located. A stkTextImage is clear where it comes from, as a basfTextImage too. No prefixes you talk about textImage paragraph, but which one. So you'll say/document anyhow the textImage of the stk.

        Do I understand that right, that the property 'template' in the metadata will have a path and not only the name?

        ONe thing you can do right now wich results in a lot of work is: rename a paragraph. But at least you coudlr estructure them within the modules without causing any additional work. When the path is stored, then even restructuring paragrpahs results in a lot of work (chaning all the values in the metadata). I'm really not sure, if this path/name adressing helps in projects, I think it results in many cases in more work and less flexibility.

        1. hm yes, the restructuring is an issue I have not yet thought about. we definitely had to provide some tooling for this. still I like the straightness of the new approach.

  11. Topic: Templates and Paragraphs in the same registry -> more extendet paragraph definition

    I assume, that we still register paragraphs in the module's paragraphs node.

    The I would suggest having in the paragraph definition a more extendet configuration for making them interchangable within different collections. Mostly they just differ in two things:

    • Differend parameters
    • Different dialog

    As the template will have a specialized config as a Map of Areas I would suggest something similar on paragraph level. A Configuration of the areas where it can be added, which acts a a subTemplate (paragraphs def meant) definition for the accoring area.

    The paragraph def itself is the default, used for every area if not defined different. But then you can ad into a node as subTemplates, fore example:

    • subTemplates/areas/main
    • subTemplates/areas/extras

    There you just define for example a differen dialog for subTemplates/areas/extras/dialog

    So if added to the mainArea, it will pick up this dialog, and so on. This is comparable as a implicit extends mechanism.

    I think subTemplates should be implmented for paragraphs too (which I think is planned) so the same config could be used for the area definitions (areas where it should pick up the subDefinition, not where it can be added, that on the templateDef/areas).

    Actually I would suggest such a general base config:

    paragraphDefinition/subDefintions/mimeTypes/

    paragraphDefinition/subDefintions/areas/

    Replacing subTemplates/mimeTypeName to subDefintions/mimeTypes/mimeTypeName

    1. I renamed sub templates into variations. for now the 'extends' mechanism should be enough. I don't like to add yet an other kind of overlay.

      1. I agree, that another overlay can be confusing. But I'm not sure that the extends makes it easier to understand.

        The 'bad' thing about the extends is, that the structure is fix by the extends. Yuu can't move paragrapgs & dialogs and rename them, cause some extends could point to it. Hard to find out, what is extending me.

        So my idea was, that it's defined on the paragrpah itself, it's variations for the areas.

        1. I agree that the 'extends' mechanism can become cumbersome but I think the main reason lies in the lacking support in the UI. We should:

          • make the path clickable
          • show an indicator if a configuration is used in other places
          • possibility to see the resulting configuration

          But the configuration is yet another topic.

      2. I think this will cause confusion with the existing concept of image variations in EE imaging module.

        How about "template-variations" or "template-variants" or "alternate templates".

        1. true, if the context is not to obvious we will talk about template variations

      3. I'm not sure, if extending your own parent won't leed to many problems? (or in htis case the parent of the parent).

  12. Topic: Rendering Context Object:

    A dynamic mechanism of defining what additional context objects can be provided to the template scripts. Right now if you wan't to provide an additional rendering context object, you need to extend the renderer and register it. But that means that all standard components which use the former renderer don't know anything of it.

    So on the RenderableDefiniition (and therefore in stk in prototype), it would be nice to configure something like:

    • theRenderableDefinition/contextObjects/additionalObject  (contains the class property)
    • The renderer can pick that up dynamically in the setupContext() method this config value (which in STK would be a merged one) and adds the objects to the context
  13. Topic: Areas in TemplateDefs:

    Generally I like this concept a lot! A question I want to rise here about that:

    Shouldn't then a Area class contain a Map of Areas again?

    I know this sound cumberson and unmanagable/hard to understand and keeping the overview.

    But if you think of STK's etxras area. What we have there is actuall a top Area, and the two additional Areas again.

    An Area in a Area you can compare, as having a paragraph with a collection of paragraphs, just htat the paragraph istself is not rendering anything but is enable-able and is scriptwise on page level (page freemarker includes).

    1. areas and templates (same as paragraphs) are going to extend the same definition base class. areas can consequently have sub areas.

  14. Philipp, have we thought about namespacing definitions? This could address some of Christian's concern. One could reference a def without namespace, falling back to "the first", or an exception thrown in ambivalent cases.

    (the "namespace" would be the module's name?)

    Christian, regarding duplicate names - if 2 modules register the same name, yeah, that's a problem. Perhaps addressed with namespaces. If it's within one single module, the fact that names have to be unique is probably a good thing, to avoid confusion.

    1. I think so too, that not beeing allowed to register in a module a template/paragraph twice with the same name is a good thing.

      Its more about the 'fact', that in STK (and that is what I teach as best prectice), we have for the SingletonParagraphPages the same names as their paragraphs. So if the paragraphs and templates are handeled by the same registry, then you could not do that anymore. That would mean we have to rename all these paragraphs or templates -> think about the updatetasks we will have to write in demo-project to change all contents. And all customers which probably habe used original stk components... and the scripts are named accordingly to the paragraph's names and so on.

      The namespace wouldn't help, cause they are alle registered in the same module -> STK

    2. we use the path relative to the /modules/xxxx/templates node, but yes, having a module namespace might be needed
      --> samples:pages/content/article

  15. I'd like to give my comments to the above proposal. We have been doing extensive work expanding magnolia for our customer in some of these areas, so I think I have some experience to share, and of course also a vested interest in what happens here, since it has to do with the future compatibility of our extensions.

    Areas

    I like this idea a lot. It's a logical generalization of the STK concept of Areas. Storing the areas in the template definition in a map is a critical improvement. Removing the STKs fixed idea of what areas make up a website will also greatly improve the versatility of the STK.
    One difference to think about is that so far, areas have been handled "inline" (using #include), while paragraphs are normally handled in their own rendering context (using @cms.includeTemplate). I'm guessing that Areas will be able to have model-classes, like paras and pages, and so areas will in future have to be handled in "their own rendering contexts" (like paragraphs). This will have an impact on template design.
    Will the areas be associated with a node type, the way paragraphs are associated with a mgnl:contentNode, and pages with a mgnl:content? Maybe mgnl:contentArea? Or will areas be without their own node, as they can be at the moment?
    Will there be direct Area acess, in the same way there is direct Paragraph access?

    Aligning Paragraphs and Templates (and Areas)

    is a good idea! Do it. While you're doing it, add the subTemplate functionality to Paragraphs and Areas as well as Pages, subTemplates are useful.

    Tags / Directives

    We have created a complete set of CMS-Tags, implemented via freeMarker Directives, building on the ideas in the currently unfinished "templating-components" module.
    Our goals for our implementation included:

    • Improving the Look & Feel of the templating components (we added icons, a prettier design, the possibilty to set more labels and tool-tips)
    • More powerful components (we have pop-up menus, drag-n-drop support for moving and sorting, moving between different container nodes)
    • Extended Front-End editing features (we can create new pages, move pages, delete pages, activate and deactivate all from the front-end)

    The reasons for these goals were:
    Our customer's editors are not trained specialists, and the backend was deemed to complicated to learn. With our extended templating components, we can create front-end UIs that more or less guide a completely inexperienced user through the process of creating content.
    Implementing the CMS-Tags as Freemarker directives was no problem at all. It is somehow much nicer to work within freemarker templates using the directives, rather than the JSP Tags.
    Aligning the tag-attribute names is certainly a good idea - having to write "contentNodeCollection=" each time (when "node=" would be sufficient) really drives me crazy!
    The directives you propose look interesting, but I think there are still some flaws:

    • Providing automatic "bars" for the area and paragraph tags seems like a convenient idea, but I don't think it will work well in practice. Experience has shown that it is imporant to have control over the placement of the bars, otherwise the GUI can be hard to understand for the user. An example: A paragraph can contain quite a lot of HTML code that just renders decorative borders, etc... around the actual text content. For useability, it is important that the edit-bar be placed next to the text to be edited, within the decorative borders, otherwise it can be unclear for the editor what button edits what content.
    • cms:render - make the template script optional. If not provided, use the native script according to the type definition. Also consider allowing "inline-templates", eg: [@cms.render node=something]
      ... inline template goes here ...
      [/@cms.render]
    • cms:iterate - providing iteration in cms:area is ok, but what if I want to iterate over a different collection than an area? cms:iterate and cms:render would be essentially the same, with the difference that cms:render operates on the target node, while cms:iterate operates on the set of children of the target node.
    • cms:adminScripts - provide a tag to render the required admin-scripts, so that they can be rendered in the page-header, rather than the middle of the page.
    • cms:publicOnly, cms:adminOnly - these are useful shorthands!

    Another comment: in your current proposal, it is not clear how the move function will work? I take it that whether move is shown in the edit-bar in some way depends on being within the same area tag?

    Inline Editing

    A good idea, and logical extension of the current functionality.

    Inheritance

    These ideas seem good. Consider providing two kinds of inheritance:
    inheritAll()  --> like the current functionality: content is inherited from ancestors, and aggregated into a list. Result is all such named nodes in the path to root.
    inheritFirst() --> inherits only one such named node, the first one found when traversing ancestors towards the root. Similar to inheritAll()?first, but more efficient.

    Ok, that's my 2c worth for the direct feedback. I will include abother post below, with some additional ideas at a more general level.
    Richard Unger

    1. Areas

      • despite their configuration they are not much differ from templates
      • they will be rendered by a renderer and have their own context and model
      • in contrast to the STK areas they will always be reflected in the content structure

      Sub templates (now named templates variations)

      • there wont be a difference between page and paragraphs templates and hence both will support variations
      • the same is actually true for areas

      Tags / Directives

      • automatic edit bar:
        • I agree that this might cause more problems than it made things easier and I dropped it
        • I also dropped the cms:paragraph directive, in favor of the more generic cms:render directive
      • cms:render and inline templates:
        • I think this is not needed as one can use any content object without the cms:render directive
      • cms:iterate
        • in the area you have the list of paragraphs, otherwise I prefer to use the language's iteration feature
        • [#list content?children as paragraph]
      • cms:adminScripts
        • that is an old one but not an easy one. the problem is that we don't process the children first. So when the paragraph gets rendered the header is already processed and streamed. This is beneficial for performance reasons. To support this we had to buffer the content
        • as much I like the idea I don't add it to this proposal
        • an other solution might be found
      • cms:publicOnly, cms:adminOnly
        • again I prefer the more powerful standard 'if' directives in combination with expressions
        • they can be added later if we realize that they are more handy

      Inheritance

      • I think cms.inherit(content, innerPath) does that, it returns the first found node matching the condition
  16. Templating-Components-API

    As mentioned in my previous comment, we have developed considerable extensions to the magnolia templating components for our customer. With the current proposal, we are of course concerned about the future compatibility of our modules.

    In creating our customized templating components, I noted that currently there is a lack of a clear API via which the templating components talk to the rest of magnolia. Essentially, once it is rendered and displayed on the front-end, for a templating component (like an edit-button) to get something done, it currently has to make use of one of two "hooks" into magnolia, neither of which is a "clean API", and neither of which is "stable".

    These are:

    1. The cms-InterceptFiltor  - configured in the magnolia filter-chain, this filter reacts to certain (undocumented) get-parameters in the request, and performs content-manipulation tasks like "move", "delete", activating preview mode, and similar.

    2. The "javascript.js" javascript functions, also dialogs.js. These javaScript files provide convenience functions which take care of opening dialogs in pop-up windows (needed for "edit" and "new" buttons) and also provide the javaScript "GUI" code to highlight buttons, implement the move action, etc... These javaScript files are shared with parts of adminCentral, and can contain nasty interactions with the content javascripts. Also this javaScript API-layer is not documented, and not stable.

    In addition, the templating components use the MgnlContext, the AggregationContext, RenderingEngine, Request-Attributes, etc... - these generally availabile APIs are also available, and used where necessary.

    Proposal: Create a clean and stable API for templating components in magnolia.

    This would allow different implementations of the front-end GUI to co-exist in the magnolia world, and would provide security for the investments made by your customers in customized GUIs.

    The API would build on what exists today, and formalize it into a stable and general API.

    The API would have a request-based interface, handled by the InterceptFilter.

    API functions would include:

    MOVE

    Parameters: 

    • node (source node to move, it must exist), 
    • newParent  (destination parent node, created if not existing, optional, default is same parent as source),
    • orderBefore (for sorting, source will be sorted before this node, optional)
    • orderAfter (for sorting, source will be sorted after this node, optional)

    DELETE

    Parameters:

    • node (node to delete, it must exist)

    UPDATE (for inline-editing)

    Parameters:

    • node (node to update, it must exist)
    • property (property to update, created if not existing)
    • value (the new value to set)

    OPENDIALOG

    Parameters:

    • name of dialog
    • additional parameters as required by the specific dialog

    OPENPAGE

    Parameters:

    • name of page
    • additional parameters as required by the specific page

    I don't think much more than this would be needed.

    Additional Comments:

    MOVE and DELETE can operate on contentNodes or on pages. If moving pages, destination-parent must also be a page.

    Except for OPENPAGE and OPENDIALOG, which only make sense when you want to show the resulting dialog window, all other commands can be called in an "AJAX" mode, which only returns success/error information. Alternatively, the commands can be called in "normal" mode, which re-renders the page content as the response.

    In Magnolia 5, OPENPAGE and OPENDIALOG will probably be replaced by something like OPENVIEW which opens the given Vaadin view.

    Combined with your suggestions for improving the templating mechanism I think such an API would be a very nice feature for magnolia, and open the door to interesting new front-end GUIs for the editors.

    1. The way the new page editor works is, thanks to the Vaadin framework, much different. The UI events are handled on the server side and thanks to IOC the most of this can be customized. A good example is adding additional actions like launching a workflow or so.

      Note: the new directives produce editor markup which can be interpreted by other editors differently

      In addition we want to expose a set of web-services following the CMIS standard to allow content manipulations.

      So the both, the intercept filter and the "javascript.js" won't exist anymore.

      1. Does this mean I will be including Vaadin components in the authoring-view of my layouts?

        I would ver VERY concerned about this.

        One of the main problems we have with magnolia for our authors is with the templating-components.

        Basically, the existing templating-components do not integrate well into arbitrary designs. There is too little control over size, placement and style, and this was one of the main motivators for developing our own templating-components. We can now set the style, alignment, etc... with sufficient flexibility to allow us to position the editing buttons whereever we need them.

        I have also experimented with Vaadin - we have a few custom GUIs which we have already realized using Vaadin.

        Bringing Vaadin into the front-end will not be easy, I think - we will have to deal with both CSS and JS conflicts between Vaadin and the page's layout.

        Or expressed another way: In my opinion the templating-components need to be very "light-weight", having no impact on the site layout, and at the same time being tolerant to the layout's CSS and JS, over which we have little a priori control. Vaadin is definately not "light-weight".

        Or have I misunderstood something?

        1. No you don't include the Vaadin components in your layout. The editor loads the page in a iframe and all javascript is isolated. the editor will then use the 'hints' in the loaded page to inject the editing functionality. This edit bars are normal divs having proper CSS classes.

  17. After some confusion I renamed the singleton area and use now the term "slot". An area is of type "collection" or "slot". This allows us to define more types if neede.

    1. I like that naming, really tells what it is, and can't be confused with singleton terms already in use in STK.

  18. The directives/tags and the functions need a different name so I felt back on cmsfn for the functions. This is a bit uggly but I have not found a nicer solution. 

  19. cms:render

    If passing a node to render, why the script has to be passed. Is the definition not stored in the metadate anymore? Probably I understand it wrong. As an optional value its really nice, but if mandatory not.

    I big plus of rendering magnolia content is that you can render it, without knowing what the script it to render it with -> fetching some target node and you can just render it.

    1. Ups, that was a leftover and I changed it as you said.

  20. topic functions:

    parent(content, [type])

    children(content, [type])

    would be nice, if both could be used without a [type] -> when no type passed, the type of the passed content is taken.

    1. Using the brackets means that the parameter is optional param. Passing no type means that the method doesn't care about node types then and will return the direct parent, respectively all children.

  21. Here are a few requests I often get from content authors:

    1. Ability to copy a paragraph from one page to another. Basically, they want a copy button, along with the move, edit and delete buttons. Once the paragraph has been moved to the new page, it's content should be editable without effecting the original content that it was copied from.
    2. Related to copy, and the inheritance discussion, ability to reference a paragraph (think alias) in another page. This would help with content reuse, like inheritance does, but with out the hierarchy rules of inheritance. If the original content is edited, all references would be updated. Magnolia already has the reference node available for dialog configuration, etc. in the STK. UI wise, content that is referenced should be marked as such when in edit mode (maybe something like the alias arrow in Mac OS X could be used). There should also be a button available in the editBar to create the reference from the original paragraph. Additionally, similar to the current dependencies tab, content authors should be able to view a list of all pages that refer to a particular paragraph. Developers should be able to configure whether content can be referenced, perhaps a referenceable node with a boolean value in the {{paragraph/template def)?
    3. Undo/Redo - Opps, I didn't mean to delete that paragraph (wink)

    It would also be nice to see a list of all pages that have existing content that use a particular paragraph/template def. This would help with testing and refactoring. For instance, if I update this paragraph definition, what pages do I need to look at to verify I didn't break something?

    1. 1. copy we are about to introduce the possability to move/copy paragraphs across different areas as well between pages
      2. reference this has to be solved on a higher level but having a referencing feature which makes such content transparent is a good idea. The template script would simply not know that the current content is referenced (by faking the hierarchy while rendering.
      3. undo has not much to do with the templating as such but this is foreseen as some of our mocks show.
      4. paragraph search would have needed that several times myself (felt normally back on groovy scripts), but which tools we finally provide in the first release is not yet decided. Once we have the basics done right we will launch the discussion

  22. what is the difference between a slot and a paragraph? If a slot can hold only one paragraph, where an area can hold many, somewhere the semantics of these words don't add up. Either you have an area, where all paragraphs are in slots, or you have areas where all content are paragraphs, and some are collections, some are not. The idea to call a single paragraph slot instead of singleton-paragraph is confusing for me.

    1. after some offline discussion we decided for the area types:

      • list
      • single

      warning: we have serious discussions wether we should replace the term 'paragraph' with something easier to understand: block, component, ...

      1. In training I never encountered a confusion about the term paragraph. I think its quite clear, cause it reflects a valid naming comparison to reality. Paragraph is a non technical word people know.

        The reall confusin term in Magnolia always was 'Template:

        - A page template (article etc, actually a definition), which defines its

        - template script (jsp or freemarker)

        - and a paragraph has a template script too

        A pages template and a paragraphs template is then quite clear I think.

        Personally I liked slot, bot no mattter, single is fine too.

        - Slot for a paragraph

        - Collection of pargraphs

        were my favorites

        1. FYI - In communication we always have to additionally qualify "Paragraph". Any non-Magnolian has no idea what we mean by "paragraph". But I agree that the whole templates naming scheme is really confusing.

          1. +1, a Magnolia "Paragraph" does not (necessarily) match what a paragraph means, and I've been suggesting renaming that concept for years (wink)

            1. Finally we decided for the name components. So Magnolia 5 will:

              • have a single registry of template definitions
                • configured in the templates node of the module or registered by code
                • they are structured in folders and by convention we structure them in pages and components
                • a template definition can be used to render any content (page, page components, data, ...)
              • an area configures the available components

              Now we have to experience a bit to see if we need more modifications.

              1. Chiming in late as usual, but won't that raise confusion with Java object components, ie info.magnolia.objectfactory.Components et al ?
                edit: as well as with templating-components ?

                1. Me and Samuel Schmitt really really dislike the naming 'component':

                  1. Component is already a technical view/expression. The 'proof' of that is, that we need to rename things as templates-components and the Components (Object Factory).

                  This will lead to the same naming confiusions, as we have/had with the term 'template': 'You need to implement a new compnent - No not a Java class component, a page component and its script...'

                  2. A page component is not that clear, that could be anything needed for a page, for exampke a page script you could call a component too.

                  Samuel suggested, and I think thats a good naming which has no corellation to anything existing yet is: Element. A page element.

                  Evey non technical person will understand this:

                  'Add to the page a element, which renders a link list'

                  'You need to implement in your project a new page element, which renders a calendar entry from Lotus'

                  When we write project documentations, usually you write something like 'Add this element to the page'.

                  1. I honestly don't see why anybody would have difficulties to have at least an idea what "page component" could mean. Yes there are other things called component, but frankly we are talking different audiences here. An author will not bother about what other technical elements hidden from him may be called component. But she will have a much easier time understanding that she can create page components than paragraphs, simply because a paragraph has a different meaning specifically for authors.

                    On the other side, any decent developer will have no problem understanding the difference between a page component and anything else.

                    1. 1. Delevolpers have now in training many confusing points about the naming 'template', if names are shared accross things, only experiance gets you a feeling for it. But that raises the entry barrier for new developers. We will produce the same confusion with the term components. When you give a training, you have to explain things. There it is irrelevant how good a developer is, but you will still need to always mention 'components, no not the one from the factory' and so on.

                      2. I think the term 'element' is as clear as component, but has not the mentioned draw backs. So why not using 'element' instead of components? And on the focus of not needing to rename anything existing (as templating-components and the ObjectFactory) it speaks for a different name too.

                2. I am aware of the fact that this conflicts with info.magnolia.objectfactory.Components but I think these are different enough to not confuse them with the page components. Especially now that we use IoC and the usage of this class will be rare.

                  The templating components will have to be renamed to templating tags, directives, markup, ... I had always to explain what this 'components' are and it was never quickly grasped so renaming them wont hurt.

                  1. Had a talk with Boris, and now I belive you two (smile)

                    Components is fine.

                  2. Ok if the "templating components" are already renamed, that's good. Indeed, the i.m.o.Components won't be used anymore, but the concept still exists (a module will still have to register... objects... components... manager-things and service-foobars...). Indeed very different concepts, and we might get away with a "prefix" to differentiate when needed (especially now that "templating component" is a free space (big grin))

              2. I like where this is going. With all the discussion, I'm a bit lost as to what the final decision on the naming is? In any case, I don't think it is a big deal - just choose some names, people will adapt. (smile)

                Making a single registry and aligning the definitions is a good idea, that will be less confusing.

                Making the definition work with any content is also an improvement.

                Please consider aligning the models in the same way. One of the most annoying side-effects of the current setup is that Template and Paragraph model-classes split into two trees below "RenderingModelImpl".

                This means that a model-class inheriting from STKTemplateModel (required for STK templates) cannot be used for a paragraph model, and vice-versa.

                A very common use-case is (at least, it is something I always need, I imagine other users will face the same trouble) to create a base model class, with some general functionality that will be needed in all the templates for a site. Just for example, if the site is a shop, you might want to create a BaseShopModel extends STKTemplateModel that provides the getShoppingCart() method in a central location.

                The problem with the current setup is that you have to make 2 base models:

                1. BaseShopTemplateModel extends STKTemplateModel
                2. BaseShopParagraphModel extends RenderingModelImpl

                and duplicate all the common code (or create a delegate object or something like that). That's really annoying.

                So it would be great if you could provide a single base model class, also for STK, that can be used for both paragraphs and templates (and areas).

                1. I am not sure if we can make it the same parent model (why should a paragraph have all the logic to assemble the navigation, ....) but we will do our best to make them not to different.

                  Anyhow, if you want to expose generic objects (like mgnl, stk, ...) one can register them in the freemarker configuration (server/rendering/freemarker/sharedVariables). We might even go as far as that any template definition can define such objects to uncouple this from the model class hierarchy.

  23. Another point:

    For the template variants, you mention that json and xml variants will be always available.

    Please make sure that these can be easily deactivated, it would be a definate No-No for us to permit our end-users to export arbitrary content as XML as JSON.

    It's a nice feature, but needs to be disabled by default on the public instance, IMHO.

  24. cms:render and other tags

    It would be great to have more flexibility here. You permit passing the content and the template (definition).

    • How about making the model class a parameter as well?
    • Or also the template-script (as opposed to the definition)?
    • It would be cool to be able to pass the "template-variant", ie to include content rendered with a different variant than the parent.
    • Or how about making all the fields of the definition "overridable" in the render tag?

    Also, I express again the usefulness of inline-templates: allow specifying the freemarker code inline in the render tag. It's a really useful feature, mainly for debugging, but also to render some content with an alternate template, without having to create a whole new definition...

    1. true, this would facilitate the reuse of content a lot. we could make it very similar to the area tag which is not dependent on a definition

  25. Can somebody tell me how to properly get the property value of an inherited (string) property?

    [#assign visual = cmsfn.inheritProperty(content, "visual")]
    Visual: ${visual!""}

    does not give me the value, neither does ${visual?string!""} nor ${visual.value!""}

    ${visual.string!""} gives me something what looks like the path to the property, but not the value: 

    Visual: property /mypage/visual

    ${visual.value!""} gives me:

    Visual: org.apache.jackrabbit.spi.commons.value.QValueValue@db251769

    How to get the value from the QValueValue object in a freemaker template? I already googled for it, but ${visual.value.string} nor ${visual.value.value} does not work.

     

    1. I hope you are aware that this is a proposal page for Magnolia 4.5 (2012&2111), we are now on Magnolia 5.4.

      Was pure luck I saw this entry.

      What you get back is a JCR Property:

       public Property inheritProperty(Node content, String relPath) throws RepositoryException {

      You need to get the value out of the JCR Property:

      String stringValue = nodeProperty.getString()
      Calendar dateValue = nodeProperty.getDate();
      Double doubleValue = nodeProperty.getDouble();
      Long longValue = nodeProperty.getLong();
      Binary forBinaryNodeData = nodeProperty.getBinary();
      //etc

      I tried it with a standard default page property called 'title, works fine:

      [#assign title = cmsfn.inheritProperty(content, "title")]
      Title: ${title.string!"nope"}
      1. Thanks, I meanwhile got it working via 

        ${visual.value.string!""}
        1. Strange, ${visual.string!""} should also work.
          Basically
          ${visual.value.string!""} calls Property.getValue().getString(), so just operating on the Value object first.

          The direct '.getString()' should also properly work, at least on my example it did.

          ${visual.string!""} gives me something what looks like the path to the property, but not the value: 

          Visual: property /mypage/visual

          The path I got when I printed directly the property: Title: ${title!"nope"}

           

          Most important is, it works for you (big grin)