10
10
using Umbraco . Cms . Core . Serialization ;
11
11
using Umbraco . Cms . Core . Services ;
12
12
using Umbraco . Cms . Core . Strings ;
13
+ using Umbraco . Extensions ;
13
14
14
15
namespace Umbraco . Cms . Core . PropertyEditors ;
15
16
@@ -291,6 +292,11 @@ private void MapBlockItemDataFromEditor(List<BlockItemData> items)
291
292
bool canUpdateInvariantData ,
292
293
HashSet < string > allowedCultures )
293
294
{
295
+ if ( canUpdateInvariantData is false && targetValue is null )
296
+ {
297
+ return sourceValue ;
298
+ }
299
+
294
300
BlockEditorData < TValue , TLayout > ? source = BlockEditorValues . DeserializeAndClean ( sourceValue ) ;
295
301
BlockEditorData < TValue , TLayout > ? target = BlockEditorValues . DeserializeAndClean ( targetValue ) ;
296
302
@@ -310,48 +316,110 @@ private void MapBlockItemDataFromEditor(List<BlockItemData> items)
310
316
bool canUpdateInvariantData ,
311
317
HashSet < string > allowedCultures )
312
318
{
313
- source = UpdateSourceInvariantData ( source , target , canUpdateInvariantData ) ;
319
+ var mergedInvariant = UpdateSourceInvariantData ( source , target , canUpdateInvariantData ) ;
314
320
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 )
316
323
{
317
324
return null ;
318
325
}
319
326
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 )
325
330
{
326
- target = new BlockEditorData < TValue , TLayout > ( [ ] , CreateWithLayout ( source . Layout ) ) ;
331
+ source = new BlockEditorData < TValue , TLayout > ( [ ] , new TValue ( ) ) ;
327
332
}
328
333
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 ;
334
336
335
337
// remove all the blocks that are no longer part of the layout
336
338
target . BlockValue . ContentData . RemoveAll ( contentBlock =>
337
339
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 ) ;
338
342
339
343
target . BlockValue . SettingsData . RemoveAll ( settingsBlock =>
340
344
target . Layout ! . Any ( layoutItem => layoutItem . ReferencesSetting ( settingsBlock . Key ) ) is false ) ;
341
345
342
346
CleanupVariantValues ( source . BlockValue . ContentData , target . BlockValue . ContentData , canUpdateInvariantData , allowedCultures ) ;
343
347
CleanupVariantValues ( source . BlockValue . SettingsData , target . BlockValue . SettingsData , canUpdateInvariantData , allowedCultures ) ;
344
348
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
+
345
375
return target . BlockValue ;
346
376
}
347
377
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
+
348
416
private void CleanupVariantValues (
349
417
List < BlockItemData > sourceBlockItems ,
350
418
List < BlockItemData > targetBlockItems ,
351
419
bool canUpdateInvariantData ,
352
420
HashSet < string > allowedCultures )
353
421
{
354
- // merge the source values into the target values for culture
422
+ // merge the source values into the target values per culture
355
423
foreach ( BlockItemData targetBlockItem in targetBlockItems )
356
424
{
357
425
BlockItemData ? sourceBlockItem = sourceBlockItems . FirstOrDefault ( i => i . Key == targetBlockItem . Key ) ;
0 commit comments