diff --git a/eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php b/eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php index f7056fb01f7..3cc186db47b 100644 --- a/eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php +++ b/eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php @@ -960,4 +960,73 @@ public function testLookUpThrowsInvalidArgumentException() $loadedAlias = $urlAliasService->lookUp(str_repeat('/1', 99), 'ger-DE'); /* END: Use Case */ } + + /** + * Test for the lookUp() method after renaming parent which is a part of the lookup path. + * + * @see https://jira.ez.no/browse/EZP-28046 + * @covers \eZ\Publish\API\Repository\URLAliasService::lookUp + * @covers \eZ\Publish\API\Repository\URLAliasService::listLocationAliases + */ + public function testLookupOnRenamedParent() + { + $urlAliasService = $this->getRepository()->getURLAliasService(); + $locationService = $this->getRepository()->getLocationService(); + $contentTypeService = $this->getRepository()->getContentTypeService(); + $contentService = $this->getRepository()->getContentService(); + + // 1. Create new container object (e.g. Folder "My Folder"). + $folderContentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $folderCreateStruct = $contentService->newContentCreateStruct($folderContentType, 'eng-GB'); + $folderCreateStruct->setField('name', 'My-Folder'); + + $folderDraft = $contentService->createContent($folderCreateStruct, [ + $locationService->newLocationCreateStruct(2), + ]); + + $folder = $contentService->publishVersion($folderDraft->versionInfo); + + // 2. Create new object inside this container (e.g. article "My Article"). + $folderLocation = $locationService->loadLocation($folder->contentInfo->mainLocationId); + + $articleContentType = $contentTypeService->loadContentTypeByIdentifier('article'); + $articleCreateStruct = $contentService->newContentCreateStruct($articleContentType, 'eng-GB'); + $articleCreateStruct->setField('title', 'My Article'); + $articleCreateStruct->setField( + 'intro', + <<< DOCBOOK + +
+ Cache invalidation in eZ +
+DOCBOOK + ); + $article = $contentService->publishVersion( + $contentService->createContent($articleCreateStruct, [ + $locationService->newLocationCreateStruct($folderLocation->id), + ])->versionInfo + ); + $articleLocation = $locationService->loadLocation($article->contentInfo->mainLocationId); + + // 3. Navigate to both of them + $urlAliasService->lookup('/My-Folder'); + $urlAliasService->listLocationAliases($folderLocation, false); + $urlAliasService->lookup('/My-Folder/My-Article'); + $urlAliasService->listLocationAliases($articleLocation, false); + + // 4. Rename "My Folder" to "My Folder Modified". + $folderDraft = $contentService->createContentDraft($folder->contentInfo); + $folderUpdateStruct = $contentService->newContentUpdateStruct(); + $folderUpdateStruct->setField('name', 'My Folder Modified'); + + $contentService->publishVersion( + $contentService->updateContent($folderDraft->versionInfo, $folderUpdateStruct)->versionInfo + ); + + // 5. Navigate to "Article" + $urlAliasService->lookup('/My-Folder/My-Article'); + $aliases = $urlAliasService->listLocationAliases($articleLocation, false); + + $this->assertEquals('/My-Folder-Modified/My-Article', $aliases[0]->path); + } } diff --git a/eZ/Publish/Core/Limitation/ObjectStateLimitationType.php b/eZ/Publish/Core/Limitation/ObjectStateLimitationType.php index 148b700bfa1..8473eca5895 100644 --- a/eZ/Publish/Core/Limitation/ObjectStateLimitationType.php +++ b/eZ/Publish/Core/Limitation/ObjectStateLimitationType.php @@ -158,8 +158,11 @@ public function evaluate(APILimitationValue $value, APIUserReference $currentUse ); } - if ($this->isStateGroupUsedForLimitation($stateGroup->id, $limitationValues)) { - $objectStateIdsToVerify[] = $defaultStateId; + foreach ($states as $state) { + // check using loose types as limitation values are strings and id's can be int + if (in_array($state->id, $limitationValues)) { + $objectStateIdsToVerify[] = $defaultStateId; + } } } } else { diff --git a/eZ/Publish/Core/Persistence/Cache/ObjectStateHandler.php b/eZ/Publish/Core/Persistence/Cache/ObjectStateHandler.php index 9b7a9a0b4d6..06402a82dd0 100644 --- a/eZ/Publish/Core/Persistence/Cache/ObjectStateHandler.php +++ b/eZ/Publish/Core/Persistence/Cache/ObjectStateHandler.php @@ -60,26 +60,22 @@ public function loadGroupByIdentifier($identifier) */ public function loadAllGroups($offset = 0, $limit = -1) { + // Method caches all state groups in cache only uses offset / limit to slice the cached result $cache = $this->cache->getItem('objectstategroup', 'all'); - $groupIds = $cache->get(); + $stateGroups = $cache->get(); if ($cache->isMiss()) { $this->logger->logCall(__METHOD__, array('offset' => $offset, 'limit' => $limit)); $stateGroups = $this->persistenceHandler->objectStateHandler()->loadAllGroups(0, -1); - - $groupIds = array(); - foreach ($stateGroups as $objectStateGroup) { - $groupCache = $this->cache->getItem('objectstategroup', $objectStateGroup->id); - $groupCache->set($objectStateGroup)->save(); - $groupIds[] = $objectStateGroup->id; - } - - $cache->set($groupIds)->save(); + $cache->set($stateGroups)->save(); $stateGroups = array_slice($stateGroups, $offset, $limit > -1 ?: null); } else { - $groupIds = array_slice($groupIds, $offset, $limit > -1 ?: null); - $stateGroups = array(); - foreach ($groupIds as $groupId) { - $stateGroups[] = $this->loadGroup($groupId); + $stateGroups = array_slice($stateGroups, $offset, $limit > -1 ?: null); + // BC for updates to 6.7LTS installs where cache contains ID's and not objects + // @todo Remove in later branches + foreach ($stateGroups as $key => $stateGroup) { + if (is_numeric($stateGroup)) { + $stateGroups[$key] = $this->loadGroup($stateGroup); + } } } @@ -92,22 +88,18 @@ public function loadAllGroups($offset = 0, $limit = -1) public function loadObjectStates($groupId) { $cache = $this->cache->getItem('objectstate', 'byGroup', $groupId); - $objectStateIds = $cache->get(); + $objectStates = $cache->get(); if ($cache->isMiss()) { $this->logger->logCall(__METHOD__, array('groupId' => $groupId)); - $objectStates = $this->persistenceHandler->objectStateHandler()->loadObjectStates($groupId); - - $objectStateIds = array(); - foreach ($objectStates as $objectState) { - $objectStateIds[] = $objectState->id; - } - - $cache->set($objectStateIds)->save(); + $cache->set($objectStates)->save(); } else { - $objectStates = array(); - foreach ($objectStateIds as $stateId) { - $objectStates[] = $this->load($stateId); + // BC for updates to 6.7LTS installs where cache contains ID's and not objects + // @todo Remove in later branches + foreach ($objectStates as $key => $state) { + if (is_numeric($state)) { + $objectStates[$key] = $this->load($state); + } } } diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/ObjectStateHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/ObjectStateHandlerTest.php index 771210cfe4b..c042bf36018 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/ObjectStateHandlerTest.php +++ b/eZ/Publish/Core/Persistence/Cache/Tests/ObjectStateHandlerTest.php @@ -217,12 +217,6 @@ public function generateObjectGroupsArray() public function testLoadAllGroups($offset = 0, $limit = -1) { $testGroups = $this->generateObjectGroupsArray(); - $testGroupIds = array_map( - function ($group) { - return $group->id; - }, - $testGroups - ); $cacheItemMock = $this->getMock('Stash\Interfaces\ItemInterface'); $this->cacheMock @@ -253,36 +247,10 @@ function ($group) { ->with(0, -1) ->will($this->returnValue($testGroups)); - foreach ($testGroups as $group) { - $this->cacheMock - ->expects($this->at($group->id)) - ->method('getItem') - ->with('objectstategroup', $group->id) - ->will( - $this->returnCallback( - function ($cachekey, $i) use ($group) { - $cacheItemMock = $this->getMock('Stash\Interfaces\ItemInterface'); - $cacheItemMock - ->expects($this->once()) - ->method('set') - ->with($group) - ->will($this->returnValue($cacheItemMock)); - - $cacheItemMock - ->expects($this->once()) - ->method('save') - ->with(); - - return $cacheItemMock; - } - ) - ); - } - $cacheItemMock ->expects($this->once()) ->method('set') - ->with($testGroupIds) + ->with($testGroups) ->will($this->returnValue($cacheItemMock)); $cacheItemMock @@ -303,12 +271,6 @@ function ($cachekey, $i) use ($group) { public function testLoadAllGroupsCached($offset = 0, $limit = -1) { $testGroups = $this->generateObjectGroupsArray($offset, $limit); - $testGroupIds = array_map( - function ($group) { - return $group->id; - }, - $testGroups - ); $cacheItemMock = $this->getMock('Stash\Interfaces\ItemInterface'); $this->cacheMock @@ -319,7 +281,7 @@ function ($group) { $cacheItemMock ->expects($this->once()) ->method('get') - ->will($this->returnValue($testGroupIds)); + ->will($this->returnValue($testGroups)); $cacheItemMock ->expects($this->once()) ->method('isMiss') @@ -327,27 +289,6 @@ function ($group) { $expectedGroups = array_slice($testGroups, $offset, $limit > -1 ?: null); - // loadGroup() - foreach ($expectedGroups as $i => $group) { - $this->cacheMock - ->expects($this->at($i + 1)) - ->method('getItem') - ->with('objectstategroup', $group->id) - ->will( - $this->returnCallback( - function ($cachekey, $i) use ($group) { - $cacheItemMock = $this->getMock('Stash\Interfaces\ItemInterface'); - $cacheItemMock - ->expects($this->once()) - ->method('get') - ->will($this->returnValue($group)); - - return $cacheItemMock; - } - ) - ); - } - $handler = $this->persistenceCacheHandler->objectStateHandler(); $groups = $handler->loadAllGroups($offset, $limit); $this->assertEquals($groups, $expectedGroups); @@ -397,12 +338,6 @@ public function testLoadObjectStates() ) ), ); - $testStateIds = array_map( - function ($state) { - return $state->id; - }, - $testStates - ); $cacheItemMock = $this->getMock('Stash\Interfaces\ItemInterface'); $this->cacheMock @@ -436,7 +371,7 @@ function ($state) { $cacheItemMock ->expects($this->once()) ->method('set') - ->with($testStateIds) + ->with($testStates) ->will($this->returnValue($cacheItemMock)); $cacheItemMock @@ -477,12 +412,6 @@ public function testLoadObjectStatesCached() ) ), ); - $testStateIds = array_map( - function ($state) { - return $state->id; - }, - $testStates - ); $cacheItemMock = $this->getMock('Stash\Interfaces\ItemInterface'); $this->cacheMock @@ -493,33 +422,12 @@ function ($state) { $cacheItemMock ->expects($this->once()) ->method('get') - ->will($this->returnValue($testStateIds)); + ->will($this->returnValue($testStates)); $cacheItemMock ->expects($this->once()) ->method('isMiss') ->will($this->returnValue(false)); - // load() - foreach ($testStates as $i => $state) { - $this->cacheMock - ->expects($this->at($i + 1)) - ->method('getItem') - ->with('objectstate', $state->id) - ->will( - $this->returnCallback( - function ($cachekey, $i) use ($state) { - $cacheItemMock = $this->getMock('Stash\Interfaces\ItemInterface'); - $cacheItemMock - ->expects($this->once()) - ->method('get') - ->will($this->returnValue($state)); - - return $cacheItemMock; - } - ) - ); - } - $handler = $this->persistenceCacheHandler->objectStateHandler(); $states = $handler->loadObjectStates(1); $this->assertEquals($states, $testStates); diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/UrlAliasHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/UrlAliasHandlerTest.php index 2175e4ef49a..87a5dde0dd2 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/UrlAliasHandlerTest.php +++ b/eZ/Publish/Core/Persistence/Cache/Tests/UrlAliasHandlerTest.php @@ -82,7 +82,6 @@ public function testPublishUrlAliasForLocationWithoutCachedLocation() $this->loggerMock->expects($this->once())->method('logCall'); $innerHandler = $this->getMock('eZ\\Publish\\SPI\\Persistence\\Content\\UrlAlias\\Handler'); - $cacheItem = $this->getMock('Stash\Interfaces\ItemInterface'); $this->persistenceHandlerMock ->expects($this->once()) @@ -95,77 +94,11 @@ public function testPublishUrlAliasForLocationWithoutCachedLocation() ->with(44, 2, 'name', 'eng-GB', true) ->will($this->returnValue(new UrlAlias(array('id' => 55)))); - $cacheItem - ->expects($this->once()) - ->method('isMiss') - ->willReturn(true); - $cacheItem - ->expects($this->never()) - ->method('clear'); - $this->cacheMock ->expects($this->once()) ->method('clear') ->with('urlAlias') ->will($this->returnValue(null)); - $this->cacheMock - ->expects($this->once()) - ->method('getItem') - ->with('urlAlias', 'location', 44) - ->willReturn($cacheItem); - - $handler = $this->persistenceCacheHandler->urlAliasHandler(); - $handler->publishUrlAliasForLocation(44, 2, 'name', 'eng-GB', true); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\UrlAliasHandler::publishUrlAliasForLocation - */ - public function testPublishUrlAliasForLocationWithCachedLocation() - { - $this->loggerMock->expects($this->once())->method('logCall'); - - $innerHandler = $this->getMock('eZ\\Publish\\SPI\\Persistence\\Content\\UrlAlias\\Handler'); - $cacheItem = $this->getMock('Stash\Interfaces\ItemInterface'); - - $this->persistenceHandlerMock - ->expects($this->once()) - ->method('urlAliasHandler') - ->will($this->returnValue($innerHandler)); - - $innerHandler - ->expects($this->once()) - ->method('publishUrlAliasForLocation') - ->with(44, 2, 'name', 'eng-GB', true) - ->will($this->returnValue(new UrlAlias(array('id' => 55)))); - - $cacheItem - ->expects($this->once()) - ->method('isMiss') - ->willReturn(false); - $cacheItem - ->expects($this->once()) - ->method('clear'); - $cacheItem - ->expects($this->once()) - ->method('get') - ->willReturn([44]); - - $this->cacheMock - ->expects($this->once()) - ->method('getItem') - ->with('urlAlias', 'location', 44) - ->willReturn($cacheItem); - $this->cacheMock - ->expects($this->at(1)) - ->method('clear') - ->with('urlAlias', 44) - ->will($this->returnValue(null)); - $this->cacheMock - ->expects($this->at(2)) - ->method('clear') - ->with('urlAlias', 'url') - ->will($this->returnValue(null)); $handler = $this->persistenceCacheHandler->urlAliasHandler(); $handler->publishUrlAliasForLocation(44, 2, 'name', 'eng-GB', true); diff --git a/eZ/Publish/Core/Persistence/Cache/UrlAliasHandler.php b/eZ/Publish/Core/Persistence/Cache/UrlAliasHandler.php index d8d86853067..61921f09778 100644 --- a/eZ/Publish/Core/Persistence/Cache/UrlAliasHandler.php +++ b/eZ/Publish/Core/Persistence/Cache/UrlAliasHandler.php @@ -44,7 +44,10 @@ public function publishUrlAliasForLocation( 'alwaysAvailable' => $alwaysAvailable, ) ); - $this->clearLocation($locationId); + + // we need to clear all because we are unable to clear cache for children + // locations only based on the locationId + $this->cache->clear('urlAlias'); $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation( $locationId, diff --git a/eZ/Publish/Core/Repository/Helper/RelationProcessor.php b/eZ/Publish/Core/Repository/Helper/RelationProcessor.php index 032f691be60..5256a8eefdd 100644 --- a/eZ/Publish/Core/Repository/Helper/RelationProcessor.php +++ b/eZ/Publish/Core/Repository/Helper/RelationProcessor.php @@ -108,8 +108,10 @@ public function processFieldRelations( $mappedRelations = array(); foreach ($existingRelations as $relation) { if ($relation->type === Relation::FIELD) { - $fieldDefinitionId = $contentType->getFieldDefinition($relation->sourceFieldDefinitionIdentifier)->id; - $mappedRelations[$relation->type][$fieldDefinitionId][$relation->destinationContentInfo->id] = $relation; + $fieldDefinition = $contentType->getFieldDefinition($relation->sourceFieldDefinitionIdentifier); + if ($fieldDefinition !== null) { + $mappedRelations[$relation->type][$fieldDefinition->id][$relation->destinationContentInfo->id] = $relation; + } } // Using bitwise AND as Legacy Stack stores COMMON, LINK and EMBED relation types // in the same entry using bitmask diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/RelationProcessorTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/RelationProcessorTest.php index 0c1e9c9276b..81b9b7f97ab 100644 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/RelationProcessorTest.php +++ b/eZ/Publish/Core/Repository/Tests/Service/Mock/RelationProcessorTest.php @@ -8,6 +8,7 @@ */ namespace eZ\Publish\Core\Repository\Tests\Service\Mock; +use eZ\Publish\API\Repository\Values\ContentType\ContentType; use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; use eZ\Publish\API\Repository\Values\Content\Relation; @@ -502,6 +503,28 @@ public function testProcessFieldRelationsRemovesRelations() ); } + /** + * Test for the processFieldRelations() method. + * + * @covers \eZ\Publish\Core\Repository\Helper\RelationProcessor::processFieldRelations + */ + public function testProcessFieldRelationsWhenRelationFieldNoLongerExists() + { + $existingRelations = [ + $this->getStubbedRelation(2, Relation::FIELD, 43, 17), + ]; + + $contentTypeMock = $this->getMockForAbstractClass(ContentType::class); + $contentTypeMock + ->expects($this->at(0)) + ->method('getFieldDefinition') + ->with($this->equalTo('identifier43')) + ->will($this->returnValue(null)); + + $relationProcessor = $this->getPartlyMockedRelationProcessor(); + $relationProcessor->processFieldRelations([], 24, 2, $contentTypeMock, $existingRelations); + } + protected function getStubbedRelation($id, $type, $fieldDefinitionId, $contentId) { return new \eZ\Publish\Core\Repository\Values\Content\Relation(