Skip to content

Commit

Permalink
!!! FEATURE: NodeTemplates Version 2.0 #53
Browse files Browse the repository at this point in the history
!!! FEATURE: NodeTemplates Version 2.0
  • Loading branch information
mhsdesign authored Jun 7, 2023
2 parents 92d5735 + e24eed0 commit a3684ba
Show file tree
Hide file tree
Showing 58 changed files with 3,199 additions and 700 deletions.
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

0 comments on commit a3684ba

Please sign in to comment.