Sitegeist.CriticalMass
Automatic creation of node-hierarchies
In Neos it is sometimes hard to handle high amounts of documents below a
single parent node. In such cases it is hard for editors to find a
specific document again. On top of that the performance and usability of
the current navigate-component will suffer if too many nodes are on a
single level., (*1)
To overcome this we suggest to use a hierarchical structure of
collection-nodes to create a node structure that is appropriate
for the current project and that helps editors to find documents.
This solution has the additional benefit of representing that structure
in the document-url aswell, that makes url-collisions much less likely., (*2)
Since the creation of such a node hierarchy is a repetitive task we
provide this package to help automating that., (*3)
A common use case would be to automatically create NewsCollection Nodes for Year and Month
and move any News Node into a matchig collection node., (*4)
Importing of nodes from CSV-Files
It is a repeated requirement to import data from table-structures into neos that usually requires the
implementation of custom import controllers. This package allows to configure the import into nodes or even
into node hierarchies., (*5)
NOTE: The import works together with the automatic node-hierarchy creation. So if you want to import into
a structure you can configure both options., (*6)
Exporting of nodes to CSV-Files
For exporting nodes into a table-structure the package allow the configuration of the expression to query for nodes and
expressions for each column of the imported table., (*7)
- Wilhelm Behncke - behncke@sitegeist.de
- Martin Ficzel - ficzel@sitegeist.de
The development and the public-releases of this package is generously sponsored
by our employer http://www.sitegeist.de., (*8)
Usage
Sitegeist:
CriticalMass:
#
# Automatic hierarchy creation
#
automaticNodeHierarchy:
# The configuration for the node type Sitegeist.CriticalMass:ExampleNode
'Sitegeist.CriticalMass:ExampleNode':
# optional: Eel-values that are evaluates first and afterwards are available in the context
# of the eel expressions below. During evaluation the `site`, `documentNode` and `node` values are present.
context:
year: "${q(node).property('startDate') ? Date.year(q(node).property('startDate')) : 'no-year'}"
month: "${q(node).property('startDate') ? Date.month(q(node).property('startDate')) : 'no-month'}"
# Detect the root-collection node that will contain the automatically created node hierarchy
root: "${q(node).parents().filter('[instanceof Sitegeist.CriticalMass:ExampleNodeCollection]').slice(-1, 1).get(0)}"
# optional: Automatically publish the created document hierarchy
autoPublishPath: true
# optional: Decide wether a node shall be handled or not
condition: "${q(node).property('startDate') ? true : false}"
# optional: The sorting of the nodes inside the target hierarchy
sortBy: '${q(a).property("title") < q(b).property("title")}'
# Define the levels of the node hierarchy that are created beneath the root node
path:
# level 1 year
-
# the type of the hierarchy-node
type: 'Sitegeist.CriticalMass:ExampleNodeCollection'
# eel-expression to find existing collection nodes
# the context contains the parent node as `parent` in addition to the default context values
node: "${q(parent).children('[uriPathSegment = '\' + year + ' '\]').get(0)}"
# optional: the sorting of the nodes on this level
# the eel expression gets two nodes `a` and `b` in the context
sortBy: '${q(a).property("title") < q(b).property("title")}'
# properties that are applied only on node creation and can be edited afterwards
properties:
title: "${year}"
uriPathSegment: "${year}"
# level 2 month
-
# the type and nodename of the hierarchy-node
type: 'Sitegeist.CriticalMass:ExampleNodeCollection'
# eel-expression to find existing collection nodes
# the context contains the parent node as `parent` in addition to the default context values
node: "${q(parent).children('[uriPathSegment = '\' + month + ' '\]').get(0)}"
# optional: the sorting of the nodes on this level
# the eel expression gets two nodes `a` and `b` in the context
sortBy: '${q(a).property("title") < q(b).property("title")}'
# properties that are applied only on node creation and can be edited afterwards
properties:
title: "${month}"
uriPathSegment: "${month}"
#
# Node-import
#
import:
# A single import preset for Sitegeist.CriticalMass:ExampleNode
#
# !!! All expressions in here are evaluated with the `site` and the current `row` in the context.
example-import:
# optional: Description for the preset
description: "Example-import description"
# optional Eel-values that are evaluates first and afterwards are available in the context
context:
base: "${q(site).find('[instanceof Sitegeist.CriticalMass:ExampleCollection]').get(0)}"
# optional: Configuration for importing previously imported nodes
update:
# Expression that returns the node that shall be updated
node: "${q(base).find('[instanceof Sitegeist.CriticalMass:ExampleNode][importIdentifier=\"' + row['ID'] + '\"]').get(0)}"
# optional: Configuration for creating new nodes if no update was configured or no preexisting node is found
create:
# optional: skip import under certain conditions
condition: "${row['ID'] ? true : false}"
# Expression that returns the node that shall be updated
parentNode: "${base}"
# The type of the node that shall be created
type: 'Sitegeist.CriticalMass:ExampleNode'
# optional: The properties that are set once for new imported nodes
properties:
'importIdentifier': "${row['ID']}"
# The properties that are updated during import AND update
properties:
'title': "${row['Title']}"
'subtitle': "${row['Subtitle']}"
'description': "${row['Description']}"
'date': "${Date.parse(row['Date'], 'y-m-d')}"
# optional: Import data into descendent-nodes
#
# !!! The expressions in here have the imported or updated node in the context as `ancestor`
#
# All the configuration from the main level can be used in here aswell. Descendent-nodes can
# even have their own descendent nodes.
descendants:
image:
update:
node: "${q(ancestor).children('images').children().get(0)}"
create:
condition: "${row['Image'] ? true : false}"
parentNode: "${q(ancestor).children('images').get(0)}"
nodeType: 'Sitegeist.CriticalMass:ExampleImage'
properties:
'title': "${row['Title']}"
'image': "${row['Image']}"
#
# Node-export
#
export:
# A single export preset for Sitegeist.CriticalMass:ExampleNode
#
# !!! All expressions in here are evaluated with the `site` and the current `row` in the context.
example-export:
# optional: Description for the preset
description: "Example-export description"
# Expression that returns the nodes that shall be exported
nodes: "${q(site).find('[instanceof Sitegeist.CriticalMass:ExampleNode]').get()}"
# The properties that are exported
#
# Each configuration key in here is evaluated as en expression with 'site' and 'node' in the context.
properties:
'ID': "${q(node).property('importIdentifier')}"
'Title': "${q(node).property('title')}"
'Subtitle': "${q(node).property('subtitle')}"
'Date': "${Date.format(q(node).property('date'), 'y-m-d')}"
'Image': "${q(node).children('images').children().first().property('image')}"
Commands
-
./flow csv:showpresets
- List the defined import and export presets
-
./flow csv:import <preset> <file>
- Import or update nodes from csv-file
-
./flow csv:export <preset> <file>
- Export nodes to csv-file
The import- and export-commands are expecting the field-names in the first line of the csv-file., (*9)
The import and export commands have an optional parameter --site-node
that can
be used to specify the site for the import. If this parameter is not given the default of
the current Neos-setup is used., (*10)
Limitations
The following issues and side effects are known at the moment:, (*11)
- Currently there is no way to notify the navigate component about a
needed reload. So after a node was moved behind the scene, the navigate
component will keep displaying the node on the current position until
the next reload.
- The automatically created nodes are in the user workspace and still
have to be published. If you want to avoid this use the option
autoPublishPath
.
Installation
Sitegeist.CriticalMass is available via packagist. Just add "sitegeist/criticalmass" : "~1.0"
to the require-dev section of the composer.json or run composer require --dev sitegeist/criticalmass
. We use semantic-versioning so every breaking change will increase the major-version number., (*12)
License
see LICENSE file, (*13)