Skip to content

Commit

Permalink
Fix #171 error duplicating element with a Neo field set not to manage…
Browse files Browse the repository at this point in the history
… blocks on a per-site basis
  • Loading branch information
ttempleton committed Jan 11, 2019
1 parent 02c7a63 commit 1efc479
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 91 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 2.1.3 - 2019-01-12
### Fixed
- Fixed error when duplicating an element with a Neo field set not to manage blocks on a per-site basis

## 2.1.2 - 2019-01-05
### Added
- Added the ability to copy multiple selected blocks from different levels, which can be pasted at the same level
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "spicyweb/craft-neo",
"description": "A Matrix-like field type that uses existing fields",
"version": "2.1.2",
"version": "2.1.3",
"type": "craft-plugin",
"keywords": [
"cms",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "craft-neo",
"version": "2.1.2",
"version": "2.1.3",
"description": "A Matrix-like field type that uses existing fields",
"main": "webpack.config.js",
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions src/elements/Block.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,9 @@ public function getSupportedSites(): array
{
$owner = $this->getOwner();

if ($owner)
if ($owner || $this->duplicateOf)
{
foreach (ElementHelper::supportedSitesForElement($owner) as $siteInfo)
foreach (ElementHelper::supportedSitesForElement($owner ?? $this->duplicateOf) as $siteInfo)
{
$siteIds[] = $siteInfo['siteId'];
}
Expand Down
171 changes: 84 additions & 87 deletions src/services/Fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,124 +179,121 @@ public function saveValue(Field $field, ElementInterface $owner, bool $isNew)
{
$dbService = Craft::$app->getDb();
$elementsService = Craft::$app->getElements();

$query = $owner->getFieldValue($field->handle);
$ownerSiteId = $field->localizeBlocks ? $owner->siteId : null;

// Is the owner being duplicated?
if ($owner->duplicateOf !== null)
{
$query = $owner->duplicateOf->getFieldValue($field->handle);

// If this is the first site the element is being duplicated for, or if the element is set to manage blocks
// on a per-site basis, then we need to duplicate them for the new element
$duplicateBlocks = !$owner->propagating || $field->localizeBlocks;
}
else
{
$query = $owner->getFieldValue($field->handle);

// If the element is brand new and propagating, and the field manages blocks on a per-site basis,
// then we will need to duplicate the blocks for this site
$duplicateBlocks = !$query->ownerId && $owner->propagating && $field->localizeBlocks;
}

$isSite = $query->siteId == $owner->siteId;
$isNewElement = !$query->ownerId;
$isDuplicatingElement = $query->ownerId && $query->ownerId != $owner->id;

if ($isSite)
{
$blocks = $query->getCachedResult();

if ($blocks === null)
// Skip if the element is propagating right now, and we don't need to duplicate the blocks
if ($owner->propagating && !$duplicateBlocks)
{
$query = clone $query;
$query->status = null;
$query->enabledForSite = false;

$blocks = $query->all();
return;
}

// Get the Neo blocks
$blocks = $query->getCachedResult() ?? (clone $query)->anyStatus()->all();

$transaction = $dbService->beginTransaction();
try
{
if (!$isNewElement)
// If we're duplicating an element, or the owner was a preexisting element,
// make sure that the blocks for this field/owner respect the field's translation setting
if ($owner->duplicateOf || $query->ownerId)
{
$this->_applyFieldTranslationSettings($query->ownerId, $query->siteId, $field);
$ownerId = $owner->duplicateOf ? $owner->duplicateOf->id : $query->ownerId;
$siteId = $owner->duplicateOf ? $owner->duplicateOf->siteId : $query->siteId;
$this->_applyFieldTranslationSettings($ownerId, $siteId, $field);
}

if ($isDuplicatingElement)
{
$blockCheckQuery = clone $query;
$blockCheckQuery->ownerId = $owner->id;

$hasBlocks = $blockCheckQuery->exists();
$blockIds = [];

if (!$hasBlocks)
foreach ($blocks as $block)
{
if ($duplicateBlocks)
{
$duplicatedBlocks = [];

foreach ($blocks as $block)
{
$duplicatedBlock = $elementsService->duplicateElement($block, [
'ownerId' => $owner->id,
'ownerSiteId' => $ownerSiteId,
]);

$duplicatedBlock->setCollapsed($block->getCollapsed());
$duplicatedBlock->cacheCollapsed();
$duplicatedBlocks[] = $duplicatedBlock;
}

$duplicatedblockStructure = new BlockStructure();
$duplicatedblockStructure->fieldId = $field->id;
$duplicatedblockStructure->ownerId = $owner->id;
$duplicatedblockStructure->ownerSiteId = $ownerSiteId;

Neo::$plugin->blocks->saveStructure($duplicatedblockStructure);
Neo::$plugin->blocks->buildStructure($duplicatedBlocks, $duplicatedblockStructure);
$collapsed = $block->getCollapsed();
$block = $elementsService->duplicateElement($block, [
'ownerId' => $owner->id,
'ownerSiteId' => $ownerSiteId,
'siteId' => $owner->siteId,
'propagating' => false,
]);
$block->setCollapsed($collapsed);
}
}
else
{
$blockIds = [];

foreach ($blocks as $block)
else
{
$block->ownerId = $owner->id;
$block->ownerSiteId = $ownerSiteId;
$block->propagating = $owner->propagating;

$elementsService->saveElement($block, false, !$owner->propagating);
$block->cacheCollapsed();

$blockIds[] = $block->id;
}

$deleteQuery = Block::find()
->anyStatus()
->ownerId($owner->id)
->fieldId($field->id)
->inReverse()
->where(['not', ['elements.id' => $blockIds] ]);
$block->cacheCollapsed();
$blockIds[] = $block->id;
}

if ($field->localizeBlocks)
{
$deleteQuery->ownerSiteId($owner->siteId);
}
else
{
$deleteQuery->siteId($owner->siteId);
}
// Now find any blocks that need to be deleted
// The blocks need to be returned in reverse order, as trying to delete them in regular order can
// cause a level-related SQL error
$deleteQuery = Block::find()
->anyStatus()
->ownerId($owner->id)
->fieldId($field->id)
->inReverse()
->where(['not', ['elements.id' => $blockIds] ]);

if ($field->localizeBlocks)
{
$deleteQuery->ownerSiteId($owner->siteId);
}
else
{
$deleteQuery->siteId($owner->siteId);
}

$deleteBlocks = $deleteQuery->all();
$deleteBlocks = $deleteQuery->all();

foreach ($deleteBlocks as $deleteBlock)
{
$deleteBlock->forgetCollapsed();
$elementsService->deleteElement($deleteBlock);
}
foreach ($deleteBlocks as $deleteBlock)
{
$deleteBlock->forgetCollapsed();
$elementsService->deleteElement($deleteBlock);
}

// Delete any existing block structures associated with this field/owner/site combination.
while (($blockStructure = Neo::$plugin->blocks->getStructure($field->id, $owner->id, $ownerSiteId)) !== null)
{
Neo::$plugin->blocks->deleteStructure($blockStructure);
}
// Delete any existing block structures associated with this field/owner/site combination
while (($blockStructure = Neo::$plugin->blocks->getStructure($field->id, $owner->id, $ownerSiteId)) !== null)
{
Neo::$plugin->blocks->deleteStructure($blockStructure);
}

// Now, if there are any blocks, save their structure.
if (!empty($blocks))
{
$blockStructure = new BlockStructure();
$blockStructure->fieldId = $field->id;
$blockStructure->ownerId = $owner->id;
$blockStructure->ownerSiteId = $ownerSiteId;
// Now, if there are blocks, save their structure
if (!empty($blocks))
{
$blockStructure = new BlockStructure();
$blockStructure->fieldId = $field->id;
$blockStructure->ownerId = $owner->id;
$blockStructure->ownerSiteId = $ownerSiteId;

Neo::$plugin->blocks->saveStructure($blockStructure);
Neo::$plugin->blocks->buildStructure($blocks, $blockStructure);
}
Neo::$plugin->blocks->saveStructure($blockStructure);
Neo::$plugin->blocks->buildStructure($blocks, $blockStructure);
}

$transaction->commit();
Expand Down

0 comments on commit 1efc479

Please sign in to comment.