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: NodeTemplates Version 2.0 #53

Merged
merged 44 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
23ca154
WIP: TASK: Separate template creation from apply
mhsdesign Jun 3, 2023
5cf7cf5
WIP: TASK: Add PageTests and reintroduce uriPathSegment generation
mhsdesign Jun 3, 2023
f845b91
WIP: TASK: Explain stuff
mhsdesign Jun 3, 2023
71ff434
TASK: testNodeCreationWithDifferentPropertyTypes
mhsdesign Jun 3, 2023
02621c1
FEATURE: Catch exceptions early and allow to partially apply a nodeTe…
mhsdesign Jun 3, 2023
063d2fe
TASK: Add missing fixtures
mhsdesign Jun 3, 2023
e15a07f
TASK: Improve tests for error handling and fix bugs
mhsdesign Jun 3, 2023
9ecb1f0
TASK: Use `StopBuildingTemplatePartException` instead of $ignoreLastR…
mhsdesign Jun 3, 2023
861754e
TASK: Move logic of TemplateBuilder into TemplateFactory and make it …
mhsdesign Jun 3, 2023
74d5aa8
TASK: Cleanup
mhsdesign Jun 3, 2023
3d4eadb
TASK: Inline `builder::withMergedWithContext`
mhsdesign Jun 4, 2023
f4fe1fb
TASK: Refactor configuration null type handling
mhsdesign Jun 4, 2023
1500685
TASK: Improve Error messages with full configuration path
mhsdesign Jun 4, 2023
425d1d2
TASK: Rename CaughtException::withCause to withOrigin
mhsdesign Jun 4, 2023
1cb457f
TASK: Validate property values before applying
mhsdesign Jun 4, 2023
7473ab0
TASK: Log CaughtExceptions with system logger and store in throwable …
mhsdesign Jun 4, 2023
152b33f
FEATURE: Add ExceptionHandlingBehaviour
mhsdesign Jun 5, 2023
3f4ce2d
TASK: Adjust README.md
mhsdesign Jun 5, 2023
06b7a56
BUGFIX: Fix tests on system with multiple dimensions
mhsdesign Jun 5, 2023
f87667c
TASK: Treat Properties and References differently and validate both s…
mhsdesign Jun 5, 2023
32ec38e
FEATURE: Re-Implement `hidden`
mhsdesign Jun 5, 2023
b3c36b3
TASK: Improve Exception Message when template was not applied
mhsdesign Jun 5, 2023
7516e12
BUGFIX: PropertyType allow `array<string>`
mhsdesign Jun 5, 2023
f214a0c
Merge remote-tracking branch 'origin/master' into task/separateTempla…
mhsdesign Jun 5, 2023
c681ca6
TASK: Restructure Classes
mhsdesign Jun 5, 2023
529da21
TASK: Add type annotation
mhsdesign Jun 5, 2023
bc5dc74
TASK: Improve error messages and use tick quotes to not confuse with …
mhsdesign Jun 5, 2023
a181656
TASK: Rename Testing folder to Tests
mhsdesign Jun 5, 2023
8b4b972
TASK: Catch exceptions when type is null or invalid
mhsdesign Jun 5, 2023
89e9826
TASK: Make `exceptionHandlingStrategy` easier to configure
mhsdesign Jun 6, 2023
8f133c6
TASK: NodeTemplateDumper traverse all 'Neos.Neos:Node'
mhsdesign Jun 6, 2023
ef3927e
TASK: Add json snapshot of node dump to tests
mhsdesign Jun 6, 2023
0495934
TASK: Test evaluated template for all tests
mhsdesign Jun 6, 2023
3068f63
TASK: Revert validating property values to match select box and not a…
mhsdesign Jun 6, 2023
807cf69
TASK: Rename `hidden` to `disabled`
mhsdesign Jun 7, 2023
c78fcfb
TASK: Rename `hidden` to `disabled` nachtrag
mhsdesign Jun 7, 2023
d11d513
TASK: Restructure codebase
mhsdesign Jun 7, 2023
314536e
TASK: Extract logic into ExceptionHandler
mhsdesign Jun 7, 2023
cce5118
TASK: Update readme
mhsdesign Jun 7, 2023
4fa4f45
TASK: Remove functionality to hide/disable nodes for now
mhsdesign Jun 7, 2023
fa2865d
TASK: Catch error because of legacy internal property names
mhsdesign Jun 7, 2023
7cb8ff4
TASK: Catch error because invalid property type
mhsdesign Jun 7, 2023
4b64b03
TASK: Catch error because of invalid template option
mhsdesign Jun 7, 2023
e24eed0
TASK: Make Neos 7.3 compatible
mhsdesign Jun 7, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

declare(strict_types=1);

namespace Flowpack\NodeTemplates\Command;
namespace Flowpack\NodeTemplates\Application\Command;

use Flowpack\NodeTemplates\NodeTemplateDumper\NodeTemplateDumper;
use Flowpack\NodeTemplates\Domain\NodeTemplateDumper\NodeTemplateDumper;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\CommandController;
use Neos\Neos\Domain\Service\ContentContextFactory;
Expand Down
43 changes: 43 additions & 0 deletions Classes/Domain/DelegatingDocumentTitleNodeCreationHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);

namespace Flowpack\NodeTemplates\Domain;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Neos\Ui\NodeCreationHandler\DocumentTitleNodeCreationHandler;
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationHandlerInterface;

/**
* Augments the original neos ui document title node creation handler, which takes care of setting the "uriPathSegment" based of the title.
* This handler steps in when a node has a node template with the property `uriPathSegment`.
* In this case we will prevent the original handler from being called, as we handle setting the `uriPathSegment` ourselves and the original handler will just override our `uriPathSegment` again.
*
* @todo once we have sorting with https://github.com/neos/neos-ui/pull/3511 we can put our handler at the end instead.
*/
class DelegatingDocumentTitleNodeCreationHandler implements NodeCreationHandlerInterface
{
/**
* @Flow\Inject
* @var DocumentTitleNodeCreationHandler
*/
protected $originalDocumentTitleNodeCreationHandler;

/**
* @throws \Neos\Eel\Exception
* @throws \Neos\Neos\Exception
*/
public function handle(NodeInterface $node, array $data): void
{
$template = $node->getNodeType()->getOptions()['template'] ?? null;
if (
!$template
|| !isset($template['properties']['uriPathSegment'])
) {
$this->originalDocumentTitleNodeCreationHandler->handle($node, $data);
return;
}

// do nothing, as we handle this already when applying the template
}
}
71 changes: 71 additions & 0 deletions Classes/Domain/ExceptionHandling/CaughtException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

namespace Flowpack\NodeTemplates\Domain\ExceptionHandling;


use Neos\Flow\Annotations as Flow;

/**
* @Flow\Proxy(false)
*/
class CaughtException
{
private \Throwable $exception;

private ?string $origin;

private function __construct(\Throwable $exception, ?string $origin)
{
$this->exception = $exception;
$this->origin = $origin;
}

public static function fromException(\Throwable $exception): self
{
return new self($exception, null);
}

public function withOrigin(string $origin): self
{
return new self($this->exception, $origin);
}

public function getException(): \Throwable
{
return $this->exception;
}

public function getOrigin(): ?string
{
return $this->origin;
}

public function toMessage(): string
{
$messageLines = [];

if ($this->origin) {
$messageLines[] = $this->origin;
}

$level = 0;
$exception = $this->exception;
do {
$level++;
if ($level >= 8) {
$messageLines[] = '...Recursion';
break;
}

$reflexception = new \ReflectionClass($exception);
$shortExceptionName = $reflexception->getShortName();
if ($shortExceptionName === 'Exception') {
$secondPartOfPackageName = explode('\\', $reflexception->getNamespaceName())[1] ?? '';
$shortExceptionName = $secondPartOfPackageName . $shortExceptionName;
}
$messageLines[] = sprintf('%s(%s, %s)', $shortExceptionName, $exception->getMessage(), $exception->getCode());
} while ($exception = $exception->getPrevious());

return join(' | ', $messageLines);
}
}
45 changes: 45 additions & 0 deletions Classes/Domain/ExceptionHandling/CaughtExceptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Flowpack\NodeTemplates\Domain\ExceptionHandling;

use Flowpack\NodeTemplates\Domain\ExceptionHandling\CaughtException;
use Neos\Flow\Annotations as Flow;

/** @Flow\Proxy(false) */
class CaughtExceptions implements \IteratorAggregate
{
/** @var array<int, CaughtException> */
private array $exceptions = [];

private function __construct()
{
}

public static function create(): self
{
return new self();
}

public function hasExceptions(): bool
{
return $this->exceptions !== [];
}

public function add(CaughtException $exception): void
{
$this->exceptions[] = $exception;
}

public function first(): ?CaughtException
{
return $this->exceptions[0] ?? null;
}

/**
* @return \Traversable<int, CaughtException>|CaughtException[]
*/
public function getIterator()
{
yield from $this->exceptions;
}
}
107 changes: 107 additions & 0 deletions Classes/Domain/ExceptionHandling/ExceptionHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace Flowpack\NodeTemplates\Domain\ExceptionHandling;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Log\ThrowableStorageInterface;
use Neos\Flow\Log\Utility\LogEnvironment;
use Neos\Neos\Ui\Domain\Model\Feedback\Messages\Error;
use Neos\Neos\Ui\Domain\Model\FeedbackCollection;
use Psr\Log\LoggerInterface;

class ExceptionHandler
{
/**
* @var FeedbackCollection
* @Flow\Inject(lazy=false)
*/
protected $feedbackCollection;

/**
* @var LoggerInterface
* @Flow\Inject
*/
protected $logger;

/**
* @var ThrowableStorageInterface
* @Flow\Inject
*/
protected $throwableStorage;

/**
* @var ExceptionHandlingConfiguration
* @Flow\Inject
*/
protected $configuration;

public function handleAfterTemplateConfigurationProcessing(CaughtExceptions $caughtExceptions, NodeInterface $node): void
{
if (!$caughtExceptions->hasExceptions()) {
return;
}

if (!$this->configuration->shouldStopOnExceptionAfterTemplateConfigurationProcessing()) {
return;
}

$templateNotCreatedException = new TemplateNotCreatedException(
sprintf('Template for "%s" was not applied. Only %s was created.', $node->getNodeType()->getLabel(), (string)$node),
1686135532992,
$caughtExceptions->first()->getException(),
);

$this->logCaughtExceptions($caughtExceptions, $templateNotCreatedException);

throw $templateNotCreatedException;
}

public function handleAfterNodeCreation(CaughtExceptions $caughtExceptions, NodeInterface $node): void
{
if (!$caughtExceptions->hasExceptions()) {
return;
}

$templatePartiallyCreatedException = new TemplatePartiallyCreatedException(
sprintf('Template for "%s" only partially applied. Please check the newly created nodes beneath %s.', $node->getNodeType()->getLabel(), (string)$node),
1686135564160,
$caughtExceptions->first()->getException(),
);

$this->logCaughtExceptions($caughtExceptions, $templatePartiallyCreatedException);

throw $templatePartiallyCreatedException;
}

/**
* @param TemplateNotCreatedException|TemplatePartiallyCreatedException $templateCreationException
*/
private function logCaughtExceptions(CaughtExceptions $caughtExceptions, \DomainException $templateCreationException): void
{
$messages = [];
foreach ($caughtExceptions as $index => $caughtException) {
$messages[sprintf('CaughtException (%s)', $index)] = $caughtException->toMessage();
}

// log exception
$messageWithReference = $this->throwableStorage->logThrowable($templateCreationException, $messages);
$this->logger->warning($messageWithReference, LogEnvironment::fromMethodName(__METHOD__));

// neos ui logging
$nodeTemplateError = new Error();
$nodeTemplateError->setMessage($templateCreationException->getMessage());

$this->feedbackCollection->add(
$nodeTemplateError
);

foreach ($messages as $message) {
$error = new Error();
$error->setMessage($message);
$this->feedbackCollection->add(
$error
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Flowpack\NodeTemplates\Domain\ExceptionHandling;

use Neos\Flow\Annotations as Flow;

class ExceptionHandlingConfiguration
{
/**
* @Flow\InjectConfiguration(package="Flowpack.NodeTemplates", path="exceptionHandling")
*/
protected array $exceptionHandlingConfiguration;

public function shouldStopOnExceptionAfterTemplateConfigurationProcessing(): bool
{
return $this->exceptionHandlingConfiguration['templateConfigurationProcessing']['stopOnException'] ?? false;
}
}
11 changes: 11 additions & 0 deletions Classes/Domain/ExceptionHandling/TemplateNotCreatedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Flowpack\NodeTemplates\Domain\ExceptionHandling;

/**
* Thrown if the templateConfigurationProcessing was unsuccessful (due to an invalid EEL expression f.x),
* and the {@see ExceptionHandlingConfiguration} is configured not to continue
*/
class TemplateNotCreatedException extends \DomainException
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Flowpack\NodeTemplates\Domain\ExceptionHandling;

/**
* Thrown in the following cases:
* - the templateConfigurationProcessing was unsuccessful (due to an invalid EEL expression f.x)
* - the nodeCreation was unsuccessful (f.x. due to constrains from the cr)
*/
class TemplatePartiallyCreatedException extends \DomainException
{
}
Loading