Skip to content

Commit c38faec

Browse files
committed
V15/fix/sub variant block deletion (#18802)
* Fix * Editors with access should be able to clear a blocklist value * Writeup around block element level variation * Dissallow values to be removed a limited language user does not have permissions to * Remove commented out code * improved comments * Improve expose list for limited language access sub variant block lists
1 parent 1e52176 commit c38faec

File tree

3 files changed

+757
-33
lines changed

3 files changed

+757
-33
lines changed

src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using Umbraco.Cms.Core.Serialization;
1111
using Umbraco.Cms.Core.Services;
1212
using Umbraco.Cms.Core.Strings;
13+
using Umbraco.Extensions;
1314

1415
namespace Umbraco.Cms.Core.PropertyEditors;
1516

@@ -291,6 +292,11 @@ private void MapBlockItemDataFromEditor(List<BlockItemData> items)
291292
bool canUpdateInvariantData,
292293
HashSet<string> allowedCultures)
293294
{
295+
if (canUpdateInvariantData is false && targetValue is null)
296+
{
297+
return sourceValue;
298+
}
299+
294300
BlockEditorData<TValue, TLayout>? source = BlockEditorValues.DeserializeAndClean(sourceValue);
295301
BlockEditorData<TValue, TLayout>? target = BlockEditorValues.DeserializeAndClean(targetValue);
296302

@@ -310,48 +316,110 @@ private void MapBlockItemDataFromEditor(List<BlockItemData> items)
310316
bool canUpdateInvariantData,
311317
HashSet<string> allowedCultures)
312318
{
313-
source = UpdateSourceInvariantData(source, target, canUpdateInvariantData);
319+
var mergedInvariant = UpdateSourceInvariantData(source, target, canUpdateInvariantData);
314320

315-
if (source is null && target is null)
321+
// if the structure (invariant) is not defined after merger, the target content does not matter
322+
if (mergedInvariant is null)
316323
{
317324
return null;
318325
}
319326

320-
if (source is null && target?.Layout is not null)
321-
{
322-
source = new BlockEditorData<TValue, TLayout>([], CreateWithLayout(target.Layout));
323-
}
324-
else if (target is null && source?.Layout is not null)
327+
// since we merged the invariant data (layout) before we get to this point
328+
// we just need an empty valid object to run comparisons at this point
329+
if (source is null)
325330
{
326-
target = new BlockEditorData<TValue, TLayout>([], CreateWithLayout(source.Layout));
331+
source = new BlockEditorData<TValue, TLayout>([], new TValue());
327332
}
328333

329-
// at this point the layout should have been merged or fallback created
330-
if (source is null || target is null)
331-
{
332-
throw new ArgumentException("invalid sourceValue or targetValue");
333-
}
334+
// update the target with the merged invariant
335+
target!.BlockValue.Layout = mergedInvariant.BlockValue.Layout;
334336

335337
// remove all the blocks that are no longer part of the layout
336338
target.BlockValue.ContentData.RemoveAll(contentBlock =>
337339
target.Layout!.Any(layoutItem => layoutItem.ReferencesContent(contentBlock.Key)) is false);
340+
// remove any exposes that no longer have content assigned to them
341+
target.BlockValue.Expose.RemoveAll(expose => target.BlockValue.ContentData.Any(data => data.Key == expose.ContentKey) is false);
338342

339343
target.BlockValue.SettingsData.RemoveAll(settingsBlock =>
340344
target.Layout!.Any(layoutItem => layoutItem.ReferencesSetting(settingsBlock.Key)) is false);
341345

342346
CleanupVariantValues(source.BlockValue.ContentData, target.BlockValue.ContentData, canUpdateInvariantData, allowedCultures);
343347
CleanupVariantValues(source.BlockValue.SettingsData, target.BlockValue.SettingsData, canUpdateInvariantData, allowedCultures);
344348

349+
// every source block value for a culture that is not allowed to be edited should be present on the target
350+
RestoreMissingValues(
351+
source.BlockValue.ContentData,
352+
target.BlockValue.ContentData,
353+
mergedInvariant.Layout!,
354+
(layoutItem, itemData) => layoutItem.ContentKey == itemData.Key,
355+
canUpdateInvariantData,
356+
allowedCultures);
357+
RestoreMissingValues(
358+
source.BlockValue.SettingsData,
359+
target.BlockValue.SettingsData,
360+
mergedInvariant.Layout!,
361+
(layoutItem, itemData) => layoutItem.SettingsKey == itemData.Key,
362+
canUpdateInvariantData,
363+
allowedCultures);
364+
365+
// update the expose list from source for any blocks that were restored
366+
var missingSourceExposes =
367+
source.BlockValue.Expose.Where(sourceExpose =>
368+
target.BlockValue.Expose.Any(targetExpose => targetExpose.ContentKey == sourceExpose.ContentKey) is false
369+
&& target.BlockValue.ContentData.Any(data => data.Key == sourceExpose.ContentKey)).ToList();
370+
foreach (BlockItemVariation missingSourceExpose in missingSourceExposes)
371+
{
372+
target.BlockValue.Expose.Add(missingSourceExpose);
373+
}
374+
345375
return target.BlockValue;
346376
}
347377

378+
private void RestoreMissingValues(
379+
List<BlockItemData> sourceBlockItemData,
380+
List<BlockItemData> targetBlockItemData,
381+
IEnumerable<TLayout> mergedLayout,
382+
Func<TLayout, BlockItemData, bool> relevantBlockItemMatcher,
383+
bool canUpdateInvariantData,
384+
HashSet<string> allowedCultures)
385+
{
386+
IEnumerable<BlockItemData> blockItemsToCheck = sourceBlockItemData.Where(itemData =>
387+
mergedLayout.Any(layoutItem => relevantBlockItemMatcher(layoutItem, itemData)));
388+
foreach (BlockItemData blockItemData in blockItemsToCheck)
389+
{
390+
var relevantValues = blockItemData.Values.Where(value =>
391+
(value.Culture is null && canUpdateInvariantData is false)
392+
|| (value.Culture is not null && allowedCultures.Contains(value.Culture) is false)).ToList();
393+
if (relevantValues.Count < 1)
394+
{
395+
continue;
396+
}
397+
398+
BlockItemData targetBlockData =
399+
targetBlockItemData.FirstOrDefault(itemData => itemData.Key == blockItemData.Key)
400+
?? new BlockItemData(blockItemData.Key, blockItemData.ContentTypeKey, blockItemData.ContentTypeAlias);
401+
foreach (BlockPropertyValue missingValue in relevantValues.Where(value => targetBlockData.Values.Any(targetValue =>
402+
targetValue.Alias == value.Alias
403+
&& targetValue.Culture == value.Culture
404+
&& targetValue.Segment == value.Segment) is false))
405+
{
406+
targetBlockData.Values.Add(missingValue);
407+
}
408+
409+
if (targetBlockItemData.Any(existingBlockItemData => existingBlockItemData.Key == targetBlockData.Key) is false)
410+
{
411+
targetBlockItemData.Add(blockItemData);
412+
}
413+
}
414+
}
415+
348416
private void CleanupVariantValues(
349417
List<BlockItemData> sourceBlockItems,
350418
List<BlockItemData> targetBlockItems,
351419
bool canUpdateInvariantData,
352420
HashSet<string> allowedCultures)
353421
{
354-
// merge the source values into the target values for culture
422+
// merge the source values into the target values per culture
355423
foreach (BlockItemData targetBlockItem in targetBlockItems)
356424
{
357425
BlockItemData? sourceBlockItem = sourceBlockItems.FirstOrDefault(i => i.Key == targetBlockItem.Key);

0 commit comments

Comments
 (0)