/**
* Internal publish method, accepting language ID instead of language code and optionally
* new alias ID (used when swapping Locations).
*
* @see \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationSwapped()
*
* @param int $locationId
* @param int $parentLocationId
* @param string $name
* @param int $languageId
* @param bool $alwaysAvailable
* @param bool $updatePathIdentificationString legacy storage specific for updating ezcontentobject_tree.path_identification_string
* @param int $newId
*/
private function internalPublishUrlAliasForLocation($locationId, $parentLocationId, $name, $languageId, $alwaysAvailable = false, $updatePathIdentificationString = false, $newId = null)
{
$parentId = $this->getRealAliasId($parentLocationId);
$name = $this->slugConverter->convert($name, 'location_' . $locationId);
$uniqueCounter = $this->slugConverter->getUniqueCounterValue($name, $parentId == 0);
$languageMask = $languageId | (int) $alwaysAvailable;
$action = 'eznode:' . $locationId;
$cleanup = false;
// Exiting the loop with break;
while (true) {
$newText = '';
if ($locationId != self::CONTENT_REPOSITORY_ROOT_LOCATION_ID) {
$newText = $name . ($uniqueCounter > 1 ? $uniqueCounter : '');
}
$newTextMD5 = $this->getHash($newText);
// Try to load existing entry
$row = $this->gateway->loadRow($parentId, $newTextMD5);
// If nothing was returned insert new entry
if (empty($row)) {
// Check for existing active location entry on this level and reuse it's id
$existingLocationEntry = $this->gateway->loadAutogeneratedEntry($action, $parentId);
if (!empty($existingLocationEntry)) {
$cleanup = true;
$newId = $existingLocationEntry['id'];
}
$newId = $this->gateway->insertRow(array('id' => $newId, 'link' => $newId, 'parent' => $parentId, 'action' => $action, 'lang_mask' => $languageMask, 'text' => $newText, 'text_md5' => $newTextMD5));
break;
}
// Row exists, check if it is reusable. There are 3 cases when this is possible:
// 1. NOP entry
// 2. existing location or custom alias entry
// 3. history entry
if ($row['action'] == 'nop:' || $row['action'] == $action || $row['is_original'] == 0) {
// Check for existing location entry on this level, if it exists and it's id differs from reusable
// entry id then reusable entry should be updated with the existing location entry id.
// Note: existing location entry may be downgraded and relinked later, depending on its language.
$existingLocationEntry = $this->gateway->loadAutogeneratedEntry($action, $parentId);
if (!empty($existingLocationEntry)) {
// Always cleanup when active autogenerated entry exists on the same level
$cleanup = true;
$newId = $existingLocationEntry['id'];
if ($existingLocationEntry['id'] == $row['id']) {
// If we are reusing existing location entry merge existing language mask
$languageMask |= $row['lang_mask'] & ~1;
}
} elseif ($newId === null) {
// Use reused row ID only if publishing normally, else use given $newId
$newId = $row['id'];
}
$this->gateway->updateRow($parentId, $newTextMD5, array('action' => $action, 'action_type' => 'eznode', 'lang_mask' => $languageMask, 'text' => $newText, 'id' => $newId, 'link' => $newId, 'alias_redirects' => 1, 'is_original' => 1, 'is_alias' => 0));
break;
}
// If existing row is not reusable, increment $uniqueCounter and try again
$uniqueCounter += 1;
}
/* @var $newText */
if ($updatePathIdentificationString) {
$this->locationGateway->updatePathIdentificationString($locationId, $parentLocationId, $this->slugConverter->convert($newText, 'node_' . $locationId, 'urlalias_compat'));
}
/* @var $newId */
/* @var $newTextMD5 */
// Note: cleanup does not touch custom and global entries
if ($cleanup) {
$this->gateway->cleanupAfterPublish($action, $languageId, $newId, $parentId, $newTextMD5);
}
}