Skip to content

Commit

Permalink
Merge branch '7.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Longosz committed Jan 16, 2019
2 parents ee65948 + 8991503 commit 6a50503
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 11 deletions.
69 changes: 69 additions & 0 deletions eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php
Expand Up @@ -1362,6 +1362,75 @@ function (Connection $connection) use ($folderLocation) {
$this->assertUrlIsCurrent('/Updated-Again-Name/Updated-Nested-folder', $nestedFolderLocation->id);
}

/**
* Test edge case when updated and archived entry gets moved to another subtree.
*
* @see https://jira.ez.no/browse/EZP-30004
*
* @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
* @throws \Exception
*/
public function testRefreshSystemUrlAliasesForMovedLocation()
{
$repository = $this->getRepository();
$urlAliasService = $repository->getURLAliasService();
$locationService = $repository->getLocationService();

$folderNames = ['eng-GB' => 'folder'];
$folder = $this->createFolder($folderNames, 2);
$nestedFolder = $this->createFolder($folderNames, $folder->contentInfo->mainLocationId);

$nestedFolder = $this->updateContentField(
$nestedFolder->contentInfo,
'name',
['eng-GB' => 'folder2']
);

$nestedFolderLocation = $locationService->loadLocation(
$nestedFolder->contentInfo->mainLocationId
);
$rootLocation = $locationService->loadLocation(2);

$locationService->moveSubtree($nestedFolderLocation, $rootLocation);
// reload nested Location to get proper parent information
$nestedFolderLocation = $locationService->loadLocation($nestedFolderLocation->id);

// corrupt database by breaking link to the original URL alias
$this->performRawDatabaseOperation(
function (Connection $connection) use ($nestedFolderLocation) {
$queryBuilder = $connection->createQueryBuilder();
$expr = $queryBuilder->expr();
$queryBuilder
->update('ezurlalias_ml')
->set('link', $queryBuilder->createPositionalParameter(666, \PDO::PARAM_INT))
->where(
$expr->eq(
'action',
$queryBuilder->createPositionalParameter(
"eznode:{$nestedFolderLocation->id}"
)
)
)
->andWhere(
$expr->eq(
'is_original',
$queryBuilder->createPositionalParameter(0, \PDO::PARAM_INT)
)
)
->andWhere(
$expr->eq('text', $queryBuilder->createPositionalParameter('folder'))
)
;

return $queryBuilder->execute();
}
);

$urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation);
}

/**
* Lookup given URL and check if it is archived and points to the given Location Id.
*
Expand Down
Expand Up @@ -9,15 +9,16 @@
namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use eZ\Publish\Core\Base\Exceptions\BadStateException;
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
use eZ\Publish\Core\Persistence\Database\Query;
use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator;
use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Language;
use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway;
use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Language;
use RuntimeException;

/**
Expand Down Expand Up @@ -1291,7 +1292,7 @@ private function loadLocationEntriesMatchingMultipleLanguages($locationId, array
*
* @throws \Doctrine\DBAL\DBALException
*/
public function deleteUrlAliasesWithoutLocation()
public function deleteUrlAliasesWithoutLocation(): int
{
$dbPlatform = $this->connection->getDatabasePlatform();

Expand Down Expand Up @@ -1333,7 +1334,7 @@ public function deleteUrlAliasesWithoutLocation()
*
* @return int Number of affected rows.
*/
public function deleteUrlAliasesWithoutParent()
public function deleteUrlAliasesWithoutParent(): int
{
$existingAliasesQuery = $this->getAllUrlAliasesQuery();

Expand Down Expand Up @@ -1405,7 +1406,7 @@ public function repairBrokenUrlAliasesForLocation(int $locationId)
$updateQueryBuilder
->update('ezurlalias_ml')
->set('link', ':linkId')
->set('parent', ':parentId')
->set('parent', ':newParentId')
->where(
$expr->eq('action', ':action')
)
Expand Down Expand Up @@ -1438,11 +1439,21 @@ public function repairBrokenUrlAliasesForLocation(int $locationId)

$updateQueryBuilder
->setParameter(':linkId', $originalUrlAlias['link'], ParameterType::INTEGER)
->setParameter(':parentId', $originalUrlAlias['parent'], ParameterType::INTEGER)
// attempt to fix missing parent case
->setParameter(
':newParentId',
$urlAliasData['existing_parent'] ?? $originalUrlAlias['parent'],
ParameterType::INTEGER
)
->setParameter(':oldParentId', $urlAliasData['parent'], ParameterType::INTEGER)
->setParameter(':textMD5', $urlAliasData['text_md5']);

$updateQueryBuilder->execute();
try {
$updateQueryBuilder->execute();
} catch (UniqueConstraintViolationException $e) {
// edge case: if such row already exists, there's no way to restore history
$this->deleteRow($urlAliasData['parent'], $urlAliasData['text_md5']);
}
}
}

Expand Down Expand Up @@ -1475,7 +1486,7 @@ function ($urlAliasData) {
*
* @return string Query
*/
private function getAllUrlAliasesQuery()
private function getAllUrlAliasesQuery(): string
{
$existingAliasesQueryBuilder = $this->connection->createQueryBuilder();
$innerQueryBuilder = $this->connection->createQueryBuilder();
Expand All @@ -1497,7 +1508,7 @@ private function getAllUrlAliasesQuery()
*
* @return string
*/
private function getIntegerType(AbstractPlatform $databasePlatform)
private function getIntegerType(AbstractPlatform $databasePlatform): string
{
switch ($databasePlatform->getName()) {
case 'mysql':
Expand All @@ -1518,15 +1529,56 @@ protected function getUrlAliasesForLocation(int $locationId): array
{
$queryBuilder = $this->connection->createQueryBuilder();
$queryBuilder
->select('id', 'is_original', 'lang_mask', 'link', 'parent', 'text_md5')
->from($this->table)
->select(
't1.id',
't1.is_original',
't1.lang_mask',
't1.link',
't1.parent',
// show existing parent only if its row exists, special case for root parent
'CASE t1.parent WHEN 0 THEN 0 ELSE t2.id END AS existing_parent',
't1.text_md5'
)
->from($this->table, 't1')
// selecting t2.id above will result in null if parent is broken
->leftJoin('t1', $this->table, 't2', $queryBuilder->expr()->eq('t1.parent', 't2.id'))
->where(
$queryBuilder->expr()->eq(
'action',
't1.action',
$queryBuilder->createPositionalParameter("eznode:{$locationId}")
)
);

return $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE);
}

/**
* Delete URL alias row by its primary composite key.
*
* @param int $parentId
* @param string $textMD5
*
* @return int number of affected rows
*/
private function deleteRow(int $parentId, string $textMD5): int
{
$queryBuilder = $this->connection->createQueryBuilder();
$expr = $queryBuilder->expr();
$queryBuilder
->delete($this->table)
->where(
$expr->andX(
$expr->eq(
'parent',
$queryBuilder->createPositionalParameter($parentId, ParameterType::INTEGER)
),
$expr->eq(
'text_md5',
$queryBuilder->createPositionalParameter($textMD5)
)
)
);

return $queryBuilder->execute();
}
}

0 comments on commit 6a50503

Please sign in to comment.