Skip to content

Commit

Permalink
Merge pull request #61 from sitegeist/feature/fusionRendering
Browse files Browse the repository at this point in the history
Feature: Adjust rendering of backend module from fluid to fusion
  • Loading branch information
mficzel authored Jan 16, 2023
2 parents d59a56a + 15725b7 commit fbc1528
Show file tree
Hide file tree
Showing 24 changed files with 594 additions and 487 deletions.
110 changes: 69 additions & 41 deletions Classes/Controller/ModuleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\View\ViewInterface;
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Fusion\View\FusionView;
use Sitegeist\Taxonomy\Service\DimensionService;
use Sitegeist\Taxonomy\Service\TaxonomyService;
use Neos\ContentRepository\Domain\Service\ContextFactoryInterface;
Expand All @@ -35,6 +36,15 @@
*/
class ModuleController extends ActionController
{
/**
* @var string
*/
protected $defaultViewObjectName = FusionView::class;

/**
* @var FusionView
*/
protected $view;

/**
* @Flow\Inject
Expand All @@ -60,6 +70,12 @@ class ModuleController extends ActionController
*/
protected $persistenceManager;

/**
* @var array
* @Flow\InjectConfiguration(path="backendModule.additionalFusionIncludePathes")
*/
protected $additionalFusionIncludePathes;

/**
* @var string
* @Flow\InjectConfiguration(package="Neos.ContentRepository", path="contentDimensions")
Expand Down Expand Up @@ -91,6 +107,11 @@ class ModuleController extends ActionController
*/
public function initializeView(ViewInterface $view)
{
$fusionPathes = ['resource://Sitegeist.Taxonomy/Private/Fusion/Backend'];
if ($this->additionalFusionIncludePathes && is_array($this->additionalFusionIncludePathes)) {
$fusionPathes = Arrays::arrayMergeRecursiveOverrule($fusionPathes, $this->additionalFusionIncludePathes);
}
$this->view->setFusionPathPatterns($fusionPathes);
$this->view->assign('contentDimensionOptions', $this->getContentDimensionOptions());
}

Expand Down Expand Up @@ -150,6 +171,7 @@ public function changeContextAction($targetAction, $targetProperty, NodeInterfac
$newContextProperties['targetDimensions'][$dimensionName] = $presetName;
}
$modifiedContext = $this->contextFactory->create(array_merge($contextProperties, $newContextProperties));

$nodeInModifiedContext = $modifiedContext->getNodeByIdentifier($contextNode->getIdentifier());

$this->redirect($targetAction, null, null, [$targetProperty => $nodeInModifiedContext]);
Expand Down Expand Up @@ -277,24 +299,27 @@ public function newVocabularyAction(NodeInterface $taxonomyRoot)
* Create a new vocabulary
*
* @param NodeInterface $taxonomyRoot
* @param string $title
* @param string $description
* @param array $properties
* @return void
*/
public function createVocabularyAction(NodeInterface $taxonomyRoot, $title, $description = '')
public function createVocabularyAction(NodeInterface $taxonomyRoot, array $properties)
{
$vocabularyNodeType = $this->nodeTypeManager->getNodeType($this->taxonomyService->getVocabularyNodeType());
$vocabularyProperties = $vocabularyNodeType->getProperties();

$nodeTemplate = new NodeTemplate();
$nodeTemplate->setNodeType($vocabularyNodeType);
$nodeTemplate->setName(CrUtitlity::renderValidNodeName($title));
$nodeTemplate->setProperty('title', $title);
$nodeTemplate->setProperty('description', $description);
$nodeTemplate->setName(CrUtitlity::renderValidNodeName($properties['title']));
foreach($properties as $name => $value) {
if (array_key_exists($name, $vocabularyProperties)) {
$nodeTemplate->setProperty($name, $value);
}
}

$vocabulary = $taxonomyRoot->createNodeFromTemplate($nodeTemplate);

$this->addFlashMessage(
sprintf('Created vocabulary %s at path %s', $title, $vocabulary->getPath())
sprintf('Created vocabulary %s at path %s', $properties['title'], $vocabulary->getLabel())
);
$this->redirect('index', null, null, ['root' => $taxonomyRoot]);
}
Expand All @@ -317,26 +342,24 @@ public function editVocabularyAction(NodeInterface $vocabulary)
* Apply changes to the given vocabulary
*
* @param NodeInterface $vocabulary
* @param string $title
* @param string $description
* @param array $properties
* @return void
*/
public function updateVocabularyAction(NodeInterface $vocabulary, $title, $description = '')
public function updateVocabularyAction(NodeInterface $vocabulary, array $properties)
{
$taxonomyRoot = $this->taxonomyService->getRoot($vocabulary->getContext());
$previousTitle = $vocabulary->getProperty('title');
$previousDescription = $vocabulary->getProperty('description');

if ($previousTitle !== $title) {
$vocabulary->setProperty('title', $title);
}

if ($previousDescription !== $description) {
$vocabulary->setProperty('description', $description);
$vocabularyProperties = $vocabulary->getNodeType()->getProperties();
foreach($properties as $name => $value) {
if (array_key_exists($name, $vocabularyProperties)) {
$previous = $vocabulary->getProperty($name);
if ($previous !== $value) {
$vocabulary->setProperty($name, $value);
}
}
}

$this->addFlashMessage(
sprintf('Updated vocabulary %s', $title)
sprintf('Updated vocabulary %s', $vocabulary->getLabel())
);
$this->redirect('index', null, null, ['root' => $taxonomyRoot]);
}
Expand Down Expand Up @@ -381,22 +404,28 @@ public function newTaxonomyAction(NodeInterface $parent)
* Create a new taxonomy
*
* @param NodeInterface $parent
* @param string $title
* @param string $description
* @param array $properties
* @return void
*/
public function createTaxonomyAction(NodeInterface $parent, $title, $description = '')
public function createTaxonomyAction(NodeInterface $parent, array $properties)
{
$taxonomyNodeType = $this->nodeTypeManager->getNodeType($this->taxonomyService->getTaxonomyNodeType());
$taxomonyProperties = $taxonomyNodeType->getProperties();

$nodeTemplate = new NodeTemplate();
$nodeTemplate->setNodeType($this->nodeTypeManager->getNodeType($this->taxonomyService->getTaxonomyNodeType()));
$nodeTemplate->setName(CrUtitlity::renderValidNodeName($title));
$nodeTemplate->setProperty('title', $title);
$nodeTemplate->setProperty('description', $description);
$nodeTemplate->setNodeType($taxonomyNodeType);
$nodeTemplate->setName(CrUtitlity::renderValidNodeName($properties['title']));

foreach($properties as $name => $value) {
if (array_key_exists($name, $taxomonyProperties)) {
$nodeTemplate->setProperty($name, $value);
}
}

$taxonomy = $parent->createNodeFromTemplate($nodeTemplate);

$this->addFlashMessage(
sprintf('Created taxonomy %s at path %s', $title, $taxonomy->getPath())
sprintf('Created taxonomy %s at path %s', $taxonomy->getLabel(), $taxonomy->getPath())
);

$flowQuery = new FlowQuery([$taxonomy]);
Expand Down Expand Up @@ -430,28 +459,25 @@ public function editTaxonomyAction(NodeInterface $taxonomy)

$this->view->assign('taxonomy', $taxonomy);
$this->view->assign('defaultTaxonomy', $this->getNodeInDefaultDimensions($taxonomy));

}

/**
* Apply changes to the given taxonomy
*
* @param NodeInterface $taxonomy
* @param string $title
* @param string $description
* @param array $properties
* @return void
*/
public function updateTaxonomyAction(NodeInterface $taxonomy, $title, $description = '')
public function updateTaxonomyAction(NodeInterface $taxonomy, array $properties)
{
$previousTitle = $taxonomy->getProperty('title');
$previousDescription = $taxonomy->getProperty('description');

if ($previousTitle !== $title) {
$taxonomy->setProperty('title', $title);
}

if ($previousDescription !== $description) {
$taxonomy->setProperty('description', $description);
$taxonomyProperties = $taxonomy->getNodeType()->getProperties();
foreach($properties as $name => $value) {
if (array_key_exists($name, $taxonomyProperties)) {
$previous = $taxonomy->getProperty($name);
if ($previous !== $value) {
$taxonomy->setProperty($name, $value);
}
}
}

$this->addFlashMessage(
Expand Down Expand Up @@ -491,4 +517,6 @@ public function deleteTaxonomyAction(NodeInterface $taxonomy)

$this->redirect('vocabulary', null, null, ['vocabulary' => $vocabulary]);
}


}
8 changes: 8 additions & 0 deletions Configuration/Settings.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
Sitegeist:
Taxonomy:
backendModule:
# fusion files or folders that are to be included in the backend module
additionalFusionIncludePathes: []
# names of additional prototypes to be rendered in vocabulary forms
additionalVocabularyFieldPrototypes: []
# names of additional prototypes to be rendered in taxonomy forms
additionalTaxonomyFieldPrototypes: []

contentRepository:
rootNodeName: 'taxonomies'
rootNodeType: 'Sitegeist.Taxonomy:Root'
Expand Down
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ multiple sites and the taxonomy documents can be defined without interfering wit

It also provides a separate backend module for managing vocabularies and taxonomies.

## Installation

Sitegeist.Taxonomy is available via packagist `composer require sitegeist/taxonomy`.
We use semantic-versioning, so every breaking change will increase the major version number.

## Storing vocabularies and taxonomies in the ContentRepository

Expand Down Expand Up @@ -113,12 +117,16 @@ of taxonomies:

Reading and referencing taxonomies from other nodes is currently not limited.

## Installation
## Extensibility

Sitegeist.Taxonomy is available via packagist. `"sitegeist/taxonomy" : "^1.0"` to the require section of the composer.json
or run `composer require sitegeist/taxonomy`.
Packages can add additional fields to the forms of taxonomies and vocabularies. To do this
the following steps are required.

We use semantic-versioning, so every breaking change will increase the major version number.
1. Extend the NodeTypes `Sitegeist.Taxonomy:Taxonomy` or `Sitegeist.Taxonomy:Vocabulary` in your package.
2. Add tha path to your additional `Root.fusion` to the Setting in path `Sitegeist.Taxonomy.backendModule.additionalFusionIncludePathes`.
3. In the fusion code define each field as prototype that accepts the props `name` plus `taxon` & `defaultTaxon` resp. `vocabulary` & `defaultVocabulary`.
4. Register addtional prototypesNames by adding them to the Settings `Sitegeist.Taxonomy.backendModule.additionalVocabularyFieldPrototypes` or
`Sitegeist.Taxonomy.backendModule.additionalTaxonomyFieldPrototypes`

## Contribution

Expand Down
67 changes: 67 additions & 0 deletions Resources/Private/Fusion/Backend/Form/Taxonomy.fusion
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
prototype(Sitegeist.Taxonomy:Form.Taxonomy) < prototype(Neos.Fusion:Component) {

i18nMain = ${Translation.value('').package("Sitegeist.Taxonomy").source('Main')}
i18nVocabulary = ${Translation.value('').package("Sitegeist.Taxonomy").source('NodeTypes/Vocabulary')}
i18nTaxonomy = ${Translation.value('').package("Sitegeist.Taxonomy").source('NodeTypes/Taxonomy')}

targetAction = null
taxonomy = null
defaultTaxonomy = null
parent = null
vocabulary = null

additionalFieldPrototypeNames = ${Configuration.setting('Sitegeist.Taxonomy.backendModule.additionalTaxonomyFieldPrototypes')}

renderer = afx`
<Neos.Fusion.Form:Form form.target.action={props.targetAction} form.data.properties={props.taxonomy.properties}>

<Neos.Fusion.Form:Hidden field.name="taxonomy" field.value={props.taxonomy.contextPath} @if={props.taxonomy}/>
<Neos.Fusion.Form:Hidden field.name="parent" field.value={props.parent.contextPath} @if={props.parent}/>

<fieldset>
<div class="neos-control-group">
<label class="neos-control-label" for="title">
{props.i18nTaxonomy.id('properties.title')}
<span @if={props.defaultTaxonomy}>: {props.defaultTaxonomy.properties.title}</span>
</label>
<Neos.Fusion.Form:Textarea attributes.class="neos-span6" field.name="properties[title]" />
</div>

<div class="neos-control-group">
<label class="neos-control-label" for="description">
{props.i18nTaxonomy.id('properties.description')}
<span @if={props.defaultTaxonomy}>: {props.defaultTaxonomy.properties.description}</span>
</label>
<Neos.Fusion.Form:Textarea attributes.class="neos-span6 form-inline" field.name="properties[description]"/>
</div>

<Neos.Fusion:Fragment @if={props.additionalFieldPrototypeNames}>
<Neos.Fusion:Loop items={props.additionalFieldPrototypeNames} itemKey="key" itemName="prototypeName">
<div class="neos-control-group">
<Neos.Fusion:Renderer
type={prototypeName}

element.name={key}
element.taxonomy={props.taxonomy}
element.defaultTaxonomy={props.defaultTaxonomy}
/>
</div>
</Neos.Fusion:Loop>
</Neos.Fusion:Fragment>

<div class="neos-control-group">
<Neos.Fusion:Link.Action
class="neos-button"
href.action="vocabulary"
href.arguments.vocabulary={props.vocabulary}
>
{props.i18nMain.id('generic.cancel')}
</Neos.Fusion:Link.Action>
&nbsp;
<Neos.Fusion.Form:Button attributes.class="neos-button neos-button-primary">{props.i18nMain.id('generic.save') + ''}</Neos.Fusion.Form:Button>
</div>
</fieldset>
</Neos.Fusion.Form:Form>
`

}
64 changes: 64 additions & 0 deletions Resources/Private/Fusion/Backend/Form/Vocabulary.fusion
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
prototype(Sitegeist.Taxonomy:Form.Vocabulary) < prototype(Neos.Fusion:Component) {

i18nMain = ${Translation.value('').package("Sitegeist.Taxonomy").source('Main')}
i18nVocabulary = ${Translation.value('').package("Sitegeist.Taxonomy").source('NodeTypes/Vocabulary')}

targetAction = null
vocabulary = null
defaultVocabulary = null
taxonomyRoot = null

additionalFieldPrototypeNames = ${Configuration.setting('Sitegeist.Taxonomy.backendModule.additionalVocabularyFieldPrototypes')}

renderer = afx`
<Neos.Fusion.Form:Form form.target.action={props.targetAction} form.data.properties={props.vocabulary ? props.vocabulary.properties : null}>

<Neos.Fusion.Form:Hidden field.name="vocabulary" field.value={props.vocabulary.contextPath} @if={props.vocabulary}/>
<Neos.Fusion.Form:Hidden field.name="taxonomyRoot" field.value={props.taxonomyRoot.contextPath} @if={props.taxonomyRoot}/>

<fieldset>
<div class="neos-control-group">
<label class="neos-control-label" for="title">
{props.i18nVocabulary.id('properties.title')}
<span @if={props.defaultVocabulary}>: {props.defaultVocabulary.properties.title}</span>
</label>
<Neos.Fusion.Form:Textarea attributes.class="neos-span6" field.name="properties[title]" />
</div>

<div class="neos-control-group">
<label class="neos-control-label" for="description">
{props.i18nVocabulary.id('properties.description')}
<span @if={props.defaultVocabulary}>: {props.defaultVocabulary.properties.description}</span>
</label>
<Neos.Fusion.Form:Textarea attributes.class="neos-span6 form-inline" field.name="properties[description]"/>
</div>

<Neos.Fusion:Fragment @if={props.additionalFieldPrototypeNames}>
<Neos.Fusion:Loop items={props.additionalFieldPrototypeNames} itemKey="key" itemName="prototypeName">
<div class="neos-control-group">
<Neos.Fusion:Renderer
type={prototypeName}
element.name={key}
element.vocabulary={props.vocabulary}
element.defaultVocabulary={props.defaultVocabulary}
/>
</div>
</Neos.Fusion:Loop>
</Neos.Fusion:Fragment>

<div class="neos-control-group">
<Neos.Fusion:Link.Action
class="neos-button"
href.action="index"
href.arguments.root={taxonomyRoot}
>
{props.i18nMain.id('generic.cancel')}
</Neos.Fusion:Link.Action>
&nbsp;
<Neos.Fusion.Form:Button attributes.class="neos-button neos-button-primary">{props.i18nMain.id('generic.save') + ''}</Neos.Fusion.Form:Button>
</div>
</fieldset>
</Neos.Fusion.Form:Form>
`

}
Loading

0 comments on commit fbc1528

Please sign in to comment.