Skip to content
Draft
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
166 changes: 164 additions & 2 deletions Classes/Controller/MediaController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,196 @@
* source code.
*/

use Flowpack\Media\Ui\GraphQL\Context\AssetSourceContext;
use Flowpack\Media\Ui\GraphQL\Types\AssetId;
use Flowpack\Media\Ui\GraphQL\Types\AssetIdentity;
use Flowpack\Media\Ui\GraphQL\Types\AssetSourceId;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\I18n\Translator;
use Neos\Flow\Mvc\Exception\StopActionException;
use Neos\Fusion\View\FusionView;
use Neos\Media\Domain\Model\Asset;
use Neos\Media\Domain\Service\AssetService;
use Neos\MetaData\Domain\Dto\MetaDataAssetReference;
use Neos\MetaData\Domain\Dto\MetaDataDimensionSpacePoint;
use Neos\MetaData\Domain\Dto\MetaDataPropertyDefinitions;
use Neos\MetaData\Domain\Dto\MetaDataPropertyName;
use Neos\MetaData\Domain\Dto\MetaDataPropertyValues;
use Neos\MetaData\MetaDataManager;
use Neos\Neos\Controller\Module\AbstractModuleController;

#[Flow\Scope('singleton')]
class MediaController extends AbstractModuleController
{
#[Flow\Inject]
protected AssetSourceContext $assetSourceContext;

#[Flow\Inject]
protected AssetService $assetService;

#[Flow\Inject]
protected Translator $translator;

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

/**
* @var string
* @inheritdoc
*/
protected $defaultViewObjectName = FusionView::class;

/**
* @var array
* @inheritdoc
*/
protected $viewFormatToObjectNameMap = [
'html' => FusionView::class,
];

protected ?MetaDataManager $metaDataManager;

Check failure on line 64 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

Property Flowpack\Media\Ui\Controller\MediaController::$metaDataManager has unknown class Neos\MetaData\MetaDataManager as its type.

public function initializeAction()
{
$this->metaDataManager = $this->objectManager->has(MetaDataManager::class)

Check failure on line 68 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

Class Neos\MetaData\MetaDataManager not found.
? $this->objectManager->get(MetaDataManager::class)

Check failure on line 69 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

Class Neos\MetaData\MetaDataManager not found.
: null;
parent::initializeAction();
}

/**
* Renders the media ui application
*/
public function indexAction(): void
{
}

public function editMetadataAction(
AssetId $assetId,
AssetSourceId $assetSourceId,
?string $metaDataDimensionSpacePointHash = null,
): void {
if ($this->metaDataManager === null) {
return;
}
$metaDataPropertyDefinitions = $this->metaDataManager->getPropertyDefinitions();

Check failure on line 89 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

Call to method getPropertyDefinitions() on an unknown class Neos\MetaData\MetaDataManager.
$dimensionSpacePoints = $this->metaDataManager->getDimensionSpacePointConfiguration();

Check failure on line 90 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

Call to method getDimensionSpacePointConfiguration() on an unknown class Neos\MetaData\MetaDataManager.
if ($metaDataDimensionSpacePointHash !== null) {
$dimensionSpacePoint = $this->getDimensionSpacePointFromHash($metaDataDimensionSpacePointHash);
}
if ($metaDataDimensionSpacePointHash === null || $dimensionSpacePoint === null) {
$dimensionSpacePoint = $dimensionSpacePoints->getIterator()->current();
}
$asset = $this->assetSourceContext->getAsset($assetId, $assetSourceId);
if ($asset === null) {
return;
}

$assetReference = MetaDataAssetReference::create($assetSourceId->value, $assetId->value);

Check failure on line 102 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

Call to static method create() on an unknown class Neos\MetaData\Domain\Dto\MetaDataAssetReference.
$propertyValues = $this->metaDataManager->getMetaDataPropertyValues(

Check failure on line 103 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

Call to method getMetaDataPropertyValues() on an unknown class Neos\MetaData\MetaDataManager.
$assetReference,
$dimensionSpacePoint
);
// Values of the parent dimension if exists, to show the fallback value before overriding
$propertyValuesOfParentWithFallback = $this->metaDataManager->getMetaDataPropertyValuesOfParentWithFallback(

Check failure on line 108 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

Call to method getMetaDataPropertyValuesOfParentWithFallback() on an unknown class Neos\MetaData\MetaDataManager.
$assetReference,
$dimensionSpacePoint
);

$propertyDefinitions = $this->mapPropertyDefinitions(
$metaDataPropertyDefinitions, $propertyValues, $propertyValuesOfParentWithFallback
);

$assetIdentity = AssetIdentity::create($assetId, $assetSourceId);

$hasOnlyEmptyDsp = false;
if ($dimensionSpacePoints->count() === 1) {
/** @var MetaDataDimensionSpacePoint $dimensionSpacePoint */
$dimensionSpacePoint = $dimensionSpacePoints->getIterator()->current();

Check failure on line 122 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

PHPDoc tag `@var` for variable $dimensionSpacePoint contains unknown class Neos\MetaData\Domain\Dto\MetaDataDimensionSpacePoint.
$hasOnlyEmptyDsp = $dimensionSpacePoint->equals(MetaDataDimensionSpacePoint::fromCoordinates([]));

Check failure on line 123 in Classes/Controller/MediaController.php

View workflow job for this annotation

GitHub Actions / codestyle (8.3)

Call to method equals() on an unknown class Neos\MetaData\Domain\Dto\MetaDataDimensionSpacePoint.
}

$this->view->assignMultiple([
'formSchema' => $propertyDefinitions,
'asset' => $asset,
'assetIdentity' => $assetIdentity,
'assetDsps' => !$hasOnlyEmptyDsp ? $dimensionSpacePoints : [],
'currentAssetDsp' => $metaDataDimensionSpacePointHash ?: $dimensionSpacePoint?->hash,
]);
}

/**
* @return array {type: string, editor: string|null, label: string, value: string|null}[]
*/
protected function mapPropertyDefinitions(
?MetaDataPropertyDefinitions $metaDataPropertyDefinitions,
MetaDataPropertyValues $propertyValues,
MetaDataPropertyValues $propertyValuesOfParentWithFallback,
): array {
if (!isset($metaDataPropertyDefinitions) || iterator_count(
$metaDataPropertyDefinitions->getIterator()
) === 0) {
return [];
}

$config = [];
foreach ($metaDataPropertyDefinitions as $propertyDefinition) {
$propertyName = $propertyDefinition->name->value;
$config[$propertyName] = [
'type' => $propertyDefinition->type->name,
'editor' => $propertyDefinition->ui->editorDefinition->editorType === 'Neos.Neos/Inspector/Editors/TextAreaEditor' ? 'textarea' : null,
'label' => $propertyDefinition->ui->label,
];

foreach ($propertyValues as $propertyValueName => $propertyValue) {
if ($propertyValueName->equals($propertyName)) {
$config[$propertyName]['value'] = $propertyValue;
}
}

foreach ($propertyValuesOfParentWithFallback as $propertyValueName => $propertyValue) {
if ($propertyValueName->equals($propertyName)) {
$config[$propertyName]['parentFallbackValue'] = $propertyValue;
}
}
}
return $config;
}

/**
* @param string[] $postData
* @throws StopActionException
*/
public function updateMetadataAction(
Asset $asset,
string $metaDataDimensionSpacePointHash,
array $postData,
): void {
$assetIdentity = AssetIdentity::create(
AssetId::fromString($this->persistenceManager->getIdentifierByObject($asset)),
new AssetSourceId($asset->getAssetSourceIdentifier())
);

$metaDataDimensionSpacePoint = $this->getDimensionSpacePointFromHash($metaDataDimensionSpacePointHash);

foreach ($postData as $propertyName => $propertyValue) {
$this->metaDataManager->setMetaDataPropertyValue(
MetaDataAssetReference::create($assetIdentity->assetSourceId->value, $assetIdentity->assetId->value),
MetaDataPropertyName::fromString($propertyName),
$propertyValue,
$metaDataDimensionSpacePoint,
);
}
$this->assetService->emitAssetUpdated($asset);
$this->redirect('index');
}

private function getDimensionSpacePointFromHash(string $dimensionSpacePointHash): ?MetaDataDimensionSpacePoint
{
$dimensionSpacePoints = $this->metaDataManager->getDimensionSpacePointConfiguration();
return current(array_filter(
iterator_to_array($dimensionSpacePoints),
fn($spacePoint) => $spacePoint->hash === $dimensionSpacePointHash
)) ?? null;
}
}
24 changes: 16 additions & 8 deletions Classes/GraphQL/Middleware/GraphQLMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public function __construct(
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
// only handle POST and OPTIONS requests to $this->url
if (!\in_array($request->getMethod(), ['POST', 'OPTIONS'],
true) || $request->getUri()->getPath() !== $this->uriPath) {
if (!in_array($request->getMethod(), ['POST', 'OPTIONS'], true)
|| $request->getUri()->getPath() !== $this->uriPath) {
return $handler->handle($request);
}
if ($this->simulateControllerObjectName !== null) {
Expand Down Expand Up @@ -145,8 +145,10 @@ private function addCorsHeaders(ResponseInterface $response): ResponseInterface
return $response
->withHeader('Access-Control-Allow-Origin', $this->corsOrigin)
->withHeader('Access-Control-Allow-Methods', 'POST,OPTIONS')
->withHeader('Access-Control-Allow-Headers',
'Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range');
->withHeader(
'Access-Control-Allow-Headers',
'Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'
);
}

private function handleGraphQLErrors(array $errors, callable $formatter): array
Expand Down Expand Up @@ -218,14 +220,20 @@ private function getSchema(Resolver $resolver): Schema
try {
$documentNode = Parser::parse($schema);
} catch (SyntaxError $e) {
throw new \RuntimeException(sprintf('Failed to parse GraphQL Schema: %s', $e->getMessage()), 1652975280,
$e);
throw new \RuntimeException(
sprintf('Failed to parse GraphQL Schema: %s', $e->getMessage()), 1652975280,
$e
);
}
try {
$this->schemaCache->set($cacheKey, AST::toArray($documentNode));
} catch (Exception $e) {
throw new \RuntimeException(sprintf('Failed to store parsed GraphQL Scheme in cache: %s',
$e->getMessage()), 1652975323, $e);
throw new \RuntimeException(
sprintf(
'Failed to store parsed GraphQL Scheme in cache: %s',
$e->getMessage()
), 1652975323, $e
);
}
}
return BuildSchema::build($documentNode, $resolver->typeConfigDecorator(...));
Expand Down
2 changes: 1 addition & 1 deletion Classes/GraphQL/Types/AssetId.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#[StringBased]
final class AssetId implements \JsonSerializable
{
private function __construct(public readonly string $value)
public function __construct(public readonly string $value)
{
}

Expand Down
2 changes: 1 addition & 1 deletion Classes/GraphQL/Types/AssetSourceId.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#[StringBased]
final class AssetSourceId implements \JsonSerializable
{
private function __construct(public readonly string $value)
public function __construct(public readonly string $value)
{
}

Expand Down
2 changes: 1 addition & 1 deletion Classes/GraphQL/Types/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ final class Image
private function __construct(
public readonly int $width,
public readonly int $height,
public readonly Url $url,
public readonly string $url,
public readonly string $alt,
) {
}
Expand Down
8 changes: 8 additions & 0 deletions Configuration/Routes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- name: 'Edit asset metadata'
uriPattern: 'neos/media-ui/{@action}'
defaults:
'@package': 'Flowpack.Media.Ui'
'@controller': 'Media'
'@format': 'html'
'@action': 'editMetadata'
appendExceedingArguments: true
1 change: 1 addition & 0 deletions Configuration/Views.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
fusionPathPatterns:
- 'resource://Neos.Fusion/Private/Fusion'
- 'resource://Flowpack.Media.Ui/Private/Fusion'
- 'resource://Neos.Fusion.Form/Private/Fusion'
21 changes: 21 additions & 0 deletions Resources/Private/Fusion/Components/DimensionSwitcherForm.fusion
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
prototype(Flowpack.Media.Ui:EditMetadata.DimensionSwitcherForm) < prototype(Neos.Fusion:Component) {
assetDsps = null
currentAssetDsp = null

renderer = afx`
<Neos.Fusion.Form:Form form.target.action="editMetadata" attributes.id="changeDspHashForm">
<h1 style="margin-bottom: .5rem">Select Dimension</h1>
<Neos.Fusion.Form:Hidden field.name="assetId" field.value={props.assetIdentity.assetId.value} />
<Neos.Fusion.Form:Hidden field.name="assetSourceId" field.value={props.assetIdentity.assetSourceId.value} />
<Neos.Fusion.Form:Select field.name="metaDataDimensionSpacePointHash" field.value={props.currentAssetDsp}>
<Neos.Fusion:Loop items={props.assetDsps}>
<Neos.Fusion.Form:Select.Option option.value={item.hash} attributes.selected={item.hash == props.currentAssetDsp}>
<Neos.Fusion:Loop items={item.coordinates} itemKey="dimensionName" itemName="dimensionValue" @glue=", ">
{dimensionName}: {dimensionValue}
</Neos.Fusion:Loop>
</Neos.Fusion.Form:Select.Option>
</Neos.Fusion:Loop>
</Neos.Fusion.Form:Select>
</Neos.Fusion.Form:Form>
`
}
55 changes: 55 additions & 0 deletions Resources/Private/Fusion/Components/FieldRenderer.fusion
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
prototype(Flowpack.Media.Ui:EditMetadata.FieldRenderer) < prototype(Neos.Fusion:Component) {
fieldName = ''
fieldConfig = null

@private.field = Neos.Fusion:Case {
boolean {
condition = ${props.fieldConfig.type == 'boolean'}
renderer = Neos.Fusion.Form:Checkbox {
field.name = ${'postData[' + props.fieldName + ']'}
field.value = ${props.fieldConfig.value ? props.fieldConfig.value : 1}
}
}
number {
condition = ${props.fieldConfig.type == 'number'}
renderer = Neos.Fusion.Form:Input {
attributes.type = 'number'
field.name = ${'postData[' + props.fieldName + ']'}
field.value = ${props.fieldConfig.value}
}
}
textArea {
condition = ${props.fieldConfig.type == 'string' && props.fieldConfig.editor == 'textarea'}
renderer = Neos.Fusion.Form:Textarea {
attributes.rows = 8
field.name = ${'postData[' + props.fieldName + ']'}
content = ${props.fieldConfig.value}
}
}
default {
condition = ${true}
renderer = Neos.Fusion.Form:Input {
field.name = ${'postData[' + props.fieldName + ']'}
field.value = ${props.fieldConfig.value}
}
}
}

prototype(Neos.Fusion.Form:LabelRenderer) {
class = 'neos-control-label'
}

renderer = afx`
<Neos.Fusion.Form:FieldContainer class="neos-control-group neos-span12"
label={props.fieldConfig.type != 'boolean' ? props.fieldConfig.label : ''}>
<div class="neos-controls neos-controls-row">
<label class="neos-checkbox neos-inline" @if.isBoolean={props.fieldConfig.type == 'boolean'}>
{private.field}
<span></span> {props.fieldConfig.label}
</label>
<Neos.Fusion:Fragment @if.isNotBoolean={props.fieldConfig.type != 'boolean'}>{private.field}</Neos.Fusion:Fragment>
<small @if.hasFallback={props.fieldConfig.parentFallbackValue}>Fallback: {props.fieldConfig.parentFallbackValue}</small>
</div>
</Neos.Fusion.Form:FieldContainer>
`
}
Loading