Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Adjust rendering of backend module from fluid to fusion #61

Merged
merged 6 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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