Skip to content
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"ibexa/user": "~4.6.0@dev",
"ibexa/fieldtype-richtext": "~4.6.0@dev",
"ibexa/rest": "~4.6.0@dev",
"ibexa/phpstan": "~4.6.x-dev",
"ibexa/polyfill-php82": "^1.0",
"ibexa/search": "~4.6.x-dev",
"ibexa/twig-components": "~4.6.x-dev",
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ includes:
- phpstan-baseline.neon.php
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-symfony/extension.neon
- vendor/ibexa/phpstan/extension.neon

parameters:
level: 8
Expand Down
8 changes: 6 additions & 2 deletions src/lib/Menu/ContentRightSidebarBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Ibexa\AdminUi\Siteaccess\SiteaccessResolverInterface;
use Ibexa\AdminUi\Specification\ContentType\ContentTypeIsUser;
use Ibexa\AdminUi\Specification\ContentType\ContentTypeIsUserGroup;
use Ibexa\AdminUi\Specification\Location\IsInContextualTreeRootIds;
use Ibexa\AdminUi\Specification\Location\IsRoot;
use Ibexa\AdminUi\Specification\Location\IsWithinCopySubtreeLimit;
use Ibexa\AdminUi\UniversalDiscovery\ConfigResolver;
Expand Down Expand Up @@ -293,7 +294,10 @@ public function createStructure(array $options): ItemInterface
);
}

if (!$contentIsUser && 1 !== $location->depth && $canTrashLocation) {
$isAtRootLevel = (new IsRoot())->isSatisfiedBy($location);
$isInContextualRootIds = (new IsInContextualTreeRootIds($this->configResolver))->isSatisfiedBy($location);

if (!$contentIsUser && !$isAtRootLevel && !$isInContextualRootIds && $canTrashLocation) {
$menu->addChild(
$this->createMenuItem(
self::ITEM__SEND_TO_TRASH,
Expand All @@ -305,7 +309,7 @@ public function createStructure(array $options): ItemInterface
);
}

if (1 === $location->depth) {
if ($isAtRootLevel || $isInContextualRootIds) {
$menu[self::ITEM__MOVE]->setAttribute('disabled', 'disabled');
}

Expand Down
30 changes: 30 additions & 0 deletions src/lib/Specification/Location/IsContentStructureRoot.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\Specification\Location;

use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Contracts\Core\Specification\AbstractSpecification;

final class IsContentStructureRoot extends AbstractSpecification
{
private ConfigResolverInterface $configResolver;

public function __construct(ConfigResolverInterface $configResolver)
{
$this->configResolver = $configResolver;
}

/**
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $item
*/
public function isSatisfiedBy($item): bool
{
return $item->getId() === (int)$this->configResolver->getParameter('location_ids.content_structure');
}
}
34 changes: 34 additions & 0 deletions src/lib/Specification/Location/IsInContextualTreeRootIds.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\Specification\Location;

use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Contracts\Core\Specification\AbstractSpecification;

final class IsInContextualTreeRootIds extends AbstractSpecification
{
private ConfigResolverInterface $configResolver;

public function __construct(ConfigResolverInterface $configResolver)
{
$this->configResolver = $configResolver;
}

/**
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $item
*/
public function isSatisfiedBy($item): bool
{
$contextualRootIds = $this->configResolver->getParameter(
'content_tree_module.contextual_tree_root_location_ids'
);

return in_array($item->getId(), $contextualRootIds, true);
}
}
4 changes: 1 addition & 3 deletions src/lib/Specification/Location/IsRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ class IsRoot extends AbstractSpecification
{
/**
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $item
*
* @return bool
*/
public function isSatisfiedBy($item): bool
{
return 1 === $item->depth;
return 1 === $item->getDepth();
}
}

Expand Down
66 changes: 66 additions & 0 deletions tests/lib/Specification/Location/IsContentStructureRootTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\AdminUi\Specification\Location;

use Ibexa\AdminUi\Specification\Location\IsContentStructureRoot;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use PHPUnit\Framework\TestCase;

final class IsContentStructureRootTest extends TestCase
{
/**
* @covers \Ibexa\AdminUi\Specification\Location\IsContentStructureRoot::isSatisfiedBy
*/
public function testReturnsTrueWhenLocationDepthMatchesRoot(): void
{
$id = 1;

$specification = new IsContentStructureRoot(
$this->createConfigResolverReturning($id)
);

self::assertTrue(
$specification->isSatisfiedBy($this->createLocationWithId($id))
);
}

/**
* @covers \Ibexa\AdminUi\Specification\Location\IsContentStructureRoot::isSatisfiedBy
*/
public function testReturnsFalseWhenLocationDepthDoesNotMatchRoot(): void
{
$specification = new IsContentStructureRoot(
$this->createConfigResolverReturning(1)
);

self::assertFalse(
$specification->isSatisfiedBy($this->createLocationWithId(3))
);
}

private function createLocationWithId(int $id): Location
{
$location = $this->createMock(Location::class);
$location->method('getId')->willReturn($id);

return $location;
}

private function createConfigResolverReturning(int $id): ConfigResolverInterface
{
$configResolver = $this->createMock(ConfigResolverInterface::class);
$configResolver
->method('getParameter')
->with('location_ids.content_structure')
->willReturn($id);

return $configResolver;
}
}
66 changes: 66 additions & 0 deletions tests/lib/Specification/Location/IsInContextualTreeRootIdsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\AdminUi\Specification\Location;

use Ibexa\AdminUi\Specification\Location\IsInContextualTreeRootIds;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use PHPUnit\Framework\TestCase;

final class IsInContextualTreeRootIdsTest extends TestCase
{
private const CONTEXTUAL_ROOT_IDS = [2, 5, 43, 55, 56, 67];

/**
* @covers \Ibexa\AdminUi\Specification\Location\IsInContextualTreeRootIds::isSatisfiedBy
*/
public function testReturnsTrueWhenLocationIdIsInContextualRootList(): void
{
$specification = new IsInContextualTreeRootIds(
$this->createConfigResolverReturning()
);

self::assertTrue(
$specification->isSatisfiedBy($this->createLocationWithId(43))
);
}

/**
* @covers \Ibexa\AdminUi\Specification\Location\IsInContextualTreeRootIds::isSatisfiedBy
*/
public function testReturnsFalseWhenLocationIdIsNotInContextualRootList(): void
{
$specification = new IsInContextualTreeRootIds(
$this->createConfigResolverReturning()
);

self::assertFalse(
$specification->isSatisfiedBy($this->createLocationWithId(999))
);
}

private function createLocationWithId(int $id): Location
{
$location = $this->createMock(Location::class);
$location->method('getId')->willReturn($id);

return $location;
}

private function createConfigResolverReturning(): ConfigResolverInterface
{
$configResolver = $this->createMock(ConfigResolverInterface::class);
$configResolver
->method('getParameter')
->with('content_tree_module.contextual_tree_root_location_ids')
->willReturn(self::CONTEXTUAL_ROOT_IDS);

return $configResolver;
}
}
48 changes: 48 additions & 0 deletions tests/lib/Specification/Location/IsRootTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\AdminUi\Specification\Location;

use Ibexa\AdminUi\Specification\Location\IsRoot;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use PHPUnit\Framework\TestCase;

final class IsRootTest extends TestCase
{
/**
* @covers \Ibexa\AdminUi\Specification\Location\IsRoot::isSatisfiedBy
*/
public function testReturnsTrueWhenLocationDepthIsOne(): void
{
$specification = new IsRoot();

$location = $this->createLocationWithDepth(1);

self::assertTrue($specification->isSatisfiedBy($location));
}

/**
* @covers \Ibexa\AdminUi\Specification\Location\IsRoot::isSatisfiedBy
*/
public function testReturnsFalseWhenLocationDepthIsNotOne(): void
{
$specification = new IsRoot();

$location = $this->createLocationWithDepth(2);

self::assertFalse($specification->isSatisfiedBy($location));
}

private function createLocationWithDepth(int $depth): Location
{
$location = $this->createMock(Location::class);
$location->method('getDepth')->willReturn($depth);

return $location;
}
}
14 changes: 4 additions & 10 deletions tests/lib/Validator/Constraint/LocationIsNotRootValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@ protected function setUp(): void

public function testValid()
{
$location = $this
->getMockBuilder(Location::class)
->setMethodsExcept(['__get'])
->setConstructorArgs([['depth' => 5]])
->getMock();
$location = $this->createMock(Location::class);
$location->method('getDepth')->willReturn(5);

$this->executionContext
->expects($this->never())
Expand All @@ -46,11 +43,8 @@ public function testValid()

public function testInvalid()
{
$location = $this
->getMockBuilder(Location::class)
->setMethodsExcept(['__get'])
->setConstructorArgs([['depth' => 1]])
->getMock();
$location = $this->createMock(Location::class);
$location->method('getDepth')->willReturn(1);

$this->executionContext
->expects($this->once())
Expand Down
Loading