Skip to content
Open
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
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "pimcore/studio-backend-bundle",
"version": "2025.4.5",
"license": "proprietary",
"type": "pimcore-bundle",
"description": "Pimcore Studio Backend Bundle",
Expand Down
2 changes: 2 additions & 0 deletions config/data_objects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ services:
Pimcore\Bundle\StudioBackendBundle\DataObject\Service\ReplaceServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\ReplaceService

Pimcore\Bundle\StudioBackendBundle\DataObject\Service\RelationNormalizationContext: ~

Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataService

Expand Down
17 changes: 15 additions & 2 deletions src/DataIndex/Grid/GridSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,21 @@
UserInterface $user
): AssetSearchResult|DataObjectSearchResult|DocumentSearchResult {
$type = $this->getStudioElementType($type);
/** @var AssetQueryInterface|DataObjectQueryInterface|DocumentQueryInterface $query */
$query = $this->getSearchQuery($type, $gridParameter, $user);

// When filtering by explicit IDs (relation context), skip workspace and path
// restrictions — the user already has a reference to these objects via the
// relation, so showing their field data does not expose new information.
$filter = $gridParameter->getFilters();
$byIds = $filter->getSimpleColumnFilterByType('system.ids') !== null;

if ($byIds) {
$query = $this->queryFactory->create($type);
/** @var AssetQueryInterface|DataObjectQueryInterface|DocumentQueryInterface $query */
$query = $this->filterService->applyFilters($query, $filter, $type);
} else {

Check notice on line 127 in src/DataIndex/Grid/GridSearch.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/DataIndex/Grid/GridSearch.php#L127

The method searchElementsForUser uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.
/** @var AssetQueryInterface|DataObjectQueryInterface|DocumentQueryInterface $query */
$query = $this->getSearchQuery($type, $gridParameter, $user);
}

return match($type) {
ElementTypes::TYPE_ASSET => $this->assetSearchService->searchAssets($query),
Expand Down
20 changes: 13 additions & 7 deletions src/DataObject/Service/DataService.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public function __construct(
private DataAdapterServiceInterface $dataAdapterService,
private InheritanceServiceInterface $inheritanceService,
private WorkflowDetailsServiceInterface $workflowDetailsService,
private RelationNormalizationContext $normalizationContext,
) {
}

Expand Down Expand Up @@ -286,13 +287,18 @@ private function removeEmptyValues(array $previewFields): array
*/
private function getDetailObjectData(Concrete $dataObject, array $fieldDefinitions): array
{
$data = [];
foreach ($fieldDefinitions as $key => $fieldDefinition) {
$data[$key] = $this->getDetailValue(
$dataObject,
$this->getValidFieldValue($dataObject, $key),
$fieldDefinition
);
$this->normalizationContext->setParent($dataObject);
try {
$data = [];
foreach ($fieldDefinitions as $key => $fieldDefinition) {
$data[$key] = $this->getDetailValue(
$dataObject,
$this->getValidFieldValue($dataObject, $key),
$fieldDefinition
);
}
} finally {
$this->normalizationContext->setParent(null);
}

return $data;
Expand Down
38 changes: 38 additions & 0 deletions src/DataObject/Service/RelationNormalizationContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);

/**
* This source file is available under the terms of the
* Pimcore Open Core License (POCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
* @license Pimcore Open Core License (POCL)
*/

namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Service;

use Pimcore\Model\DataObject\Concrete;

/**
* Holds the parent (owner) object currently being normalized so that nested
* relation adapters can check save/publish permissions against it without
* requiring the parent to be threaded through every normalize() signature.
*
* @internal
*/
class RelationNormalizationContext
{
private ?Concrete $parent = null;

Check notice on line 27 in src/DataObject/Service/RelationNormalizationContext.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/DataObject/Service/RelationNormalizationContext.php#L27

Expected 1 blank line(s) before first member var; 0 found

public function setParent(?Concrete $parent): void

Check notice on line 29 in src/DataObject/Service/RelationNormalizationContext.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/DataObject/Service/RelationNormalizationContext.php#L29

Expected 2 blank lines before function; 1 found
{
$this->parent = $parent;
}

Check notice on line 32 in src/DataObject/Service/RelationNormalizationContext.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/DataObject/Service/RelationNormalizationContext.php#L32

Expected 1 blank line before closing function brace; 0 found

Check notice on line 32 in src/DataObject/Service/RelationNormalizationContext.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/DataObject/Service/RelationNormalizationContext.php#L32

Expected 2 blank lines after function; 1 found

public function getParent(): ?Concrete
{
return $this->parent;
}
}
16 changes: 15 additions & 1 deletion src/Element/Schema/RelatedElementData.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#[Schema(
schema: 'RelatedElementData',
title: 'RelatedElementData',
required: ['id', 'type', 'subtype', 'fullPath', 'isPublished'],
required: ['id', 'type', 'subtype', 'fullPath', 'isPublished', 'hasAccess', 'canEdit'],
type: 'object'
)]
final readonly class RelatedElementData
Expand All @@ -38,6 +38,10 @@
private string $fullPath,
#[Property(description: 'Is the element published', type: 'boolean', example: true)]
private ?bool $isPublished = null,
#[Property(description: 'Whether the current user has view access to the element', type: 'boolean', example: true)]

Check warning on line 41 in src/Element/Schema/RelatedElementData.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Split this 123 characters long line (which is greater than 120 authorized).

See more on https://sonarcloud.io/project/issues?id=pimcore_studio-backend-bundle&issues=AZ6njQ0uvn79Adz8v4qi&open=AZ6njQ0uvn79Adz8v4qi&pullRequest=1872
private bool $hasAccess = true,

Check notice on line 42 in src/Element/Schema/RelatedElementData.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Element/Schema/RelatedElementData.php#L42

Incorrect spacing between argument "$hasAccess" and equals sign; expected 0 but found 1
#[Property(description: 'Whether the current user has save or publish permission on the element', type: 'boolean', example: true)]

Check warning on line 43 in src/Element/Schema/RelatedElementData.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Split this 138 characters long line (which is greater than 120 authorized).

See more on https://sonarcloud.io/project/issues?id=pimcore_studio-backend-bundle&issues=AZ6njQ0uvn79Adz8v4qj&open=AZ6njQ0uvn79Adz8v4qj&pullRequest=1872
private bool $canEdit = true,

Check notice on line 44 in src/Element/Schema/RelatedElementData.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Element/Schema/RelatedElementData.php#L44

Incorrect spacing between argument "$canEdit" and equals sign; expected 0 but found 1
) {
}

Expand Down Expand Up @@ -65,4 +69,14 @@
{
return $this->isPublished;
}

public function getHasAccess(): bool
{
return $this->hasAccess;
}

public function getCanEdit(): bool
{
return $this->canEdit;
}
}
35 changes: 31 additions & 4 deletions src/Element/Service/ElementDataService.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,58 @@

namespace Pimcore\Bundle\StudioBackendBundle\Element\Service;

use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\RelationNormalizationContext;
use Pimcore\Bundle\StudioBackendBundle\Element\Schema\RelatedElementData;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\UserNotFoundException;
use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Util\Trait\ElementProviderTrait;
use Pimcore\Model\DataObject\Concrete;
use Pimcore\Model\Document;
use Pimcore\Model\Element\ElementInterface;
use Pimcore\Model\User;

/**
* @internal
*/
final readonly class ElementDataService implements ElementDataServiceInterface
readonly class ElementDataService implements ElementDataServiceInterface
{
use ElementProviderTrait;

public function __construct(

Check notice on line 33 in src/Element/Service/ElementDataService.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Element/Service/ElementDataService.php#L33

Expected 2 blank lines before function; 1 found
protected SecurityServiceInterface $securityService,
protected RelationNormalizationContext $normalizationContext,
) {
}

public function getRelatedElementData(ElementInterface $element): RelatedElementData
{
$hasAccess = true;
$canEdit = true;
try {
$user = $this->securityService->getCurrentUser();
/** @var User $user */
$hasAccess = $element->isAllowed('view', $user);
$parent = $this->normalizationContext->getParent();
if ($parent !== null) {
$canEdit = $parent->isAllowed('save', $user) || $parent->isAllowed('publish', $user);
}
} catch (UserNotFoundException) {
$hasAccess = false;
$canEdit = false;
}

return new RelatedElementData(
$element->getId(),
$this->getElementType($element, true),
$this->getSubType($element),
$element->getRealFullPath(),
$this->getPublished($element)
$this->getPublished($element),
$hasAccess,
$canEdit,
);
}

private function getSubType(ElementInterface $element): string
protected function getSubType(ElementInterface $element): string
{
if ($element instanceof Concrete) {
return $element->getClassName();
Expand All @@ -46,7 +73,7 @@
return $element->getType();
}

private function getPublished(ElementInterface $element): ?bool
protected function getPublished(ElementInterface $element): ?bool
{
if ($element instanceof Concrete || $element instanceof Document) {
return $element->getPublished();
Expand Down
2 changes: 1 addition & 1 deletion src/Util/Trait/ElementProviderTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private function getElementClass(ElementInterface $element): string
/**
* @throws InvalidElementTypeException
*/
private function getElementType(ElementInterface $element, bool $getCoreType = false): string
protected function getElementType(ElementInterface $element, bool $getCoreType = false): string
{
return match (true) {
$element instanceof Asset => ElementTypes::TYPE_ASSET,
Expand Down
Loading