Skip to content

Commit 2c04e37

Browse files
kjacZeegaan
andauthored
Support limited language access at block level (#17322)
* Support limited language access at block level * Account for AllowEditInvariantFromNonDefault when updating properties (#17333) * Remove obsolete ctor * Add explanatory comment * Set AllowEditInvariantFromNonDefault to true on tests * Refactor to account for merge and default language * Merge invariant values on top of the already merged values * Add integration test to prove invariant merging --------- Co-authored-by: kjac <[email protected]> --------- Co-authored-by: Nikolaj Geisle <[email protected]>
1 parent 30d4ec4 commit 2c04e37

File tree

5 files changed

+551
-40
lines changed

5 files changed

+551
-40
lines changed

src/Umbraco.Core/Services/ContentEditingService.cs

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using Microsoft.Extensions.DependencyInjection;
2-
using Microsoft.Extensions.Logging;
3-
using Umbraco.Cms.Core.DependencyInjection;
1+
using Microsoft.Extensions.Logging;
2+
using Microsoft.Extensions.Options;
3+
using Umbraco.Cms.Core.Configuration.Models;
44
using Umbraco.Cms.Core.Models;
55
using Umbraco.Cms.Core.Models.ContentEditing;
66
using Umbraco.Cms.Core.Models.Membership;
@@ -14,42 +14,13 @@ namespace Umbraco.Cms.Core.Services;
1414
internal sealed class ContentEditingService
1515
: ContentEditingServiceWithSortingBase<IContent, IContentType, IContentService, IContentTypeService>, IContentEditingService
1616
{
17+
private readonly PropertyEditorCollection _propertyEditorCollection;
1718
private readonly ITemplateService _templateService;
1819
private readonly ILogger<ContentEditingService> _logger;
1920
private readonly IUserService _userService;
2021
private readonly ILocalizationService _localizationService;
2122
private readonly ILanguageService _languageService;
22-
23-
[Obsolete("Use non-obsolete constructor. This will be removed in Umbraco 16.")]
24-
public ContentEditingService(
25-
IContentService contentService,
26-
IContentTypeService contentTypeService,
27-
PropertyEditorCollection propertyEditorCollection,
28-
IDataTypeService dataTypeService,
29-
ITemplateService templateService,
30-
ILogger<ContentEditingService> logger,
31-
ICoreScopeProvider scopeProvider,
32-
IUserIdKeyResolver userIdKeyResolver,
33-
ITreeEntitySortingService treeEntitySortingService,
34-
IContentValidationService contentValidationService)
35-
: this(
36-
contentService,
37-
contentTypeService,
38-
propertyEditorCollection,
39-
dataTypeService,
40-
templateService,
41-
logger,
42-
scopeProvider,
43-
userIdKeyResolver,
44-
treeEntitySortingService,
45-
contentValidationService,
46-
StaticServiceProvider.Instance.GetRequiredService<IUserService>(),
47-
StaticServiceProvider.Instance.GetRequiredService<ILocalizationService>(),
48-
StaticServiceProvider.Instance.GetRequiredService<ILanguageService>()
49-
)
50-
{
51-
52-
}
23+
private readonly ContentSettings _contentSettings;
5324

5425
public ContentEditingService(
5526
IContentService contentService,
@@ -64,14 +35,17 @@ public ContentEditingService(
6435
IContentValidationService contentValidationService,
6536
IUserService userService,
6637
ILocalizationService localizationService,
67-
ILanguageService languageService)
38+
ILanguageService languageService,
39+
IOptions<ContentSettings> contentSettings)
6840
: base(contentService, contentTypeService, propertyEditorCollection, dataTypeService, logger, scopeProvider, userIdKeyResolver, contentValidationService, treeEntitySortingService)
6941
{
42+
_propertyEditorCollection = propertyEditorCollection;
7043
_templateService = templateService;
7144
_logger = logger;
7245
_userService = userService;
7346
_localizationService = localizationService;
7447
_languageService = languageService;
48+
_contentSettings = contentSettings.Value;
7549
}
7650

7751
public async Task<IContent?> GetAsync(Guid key)
@@ -154,28 +128,53 @@ private async Task<IContent> EnsureOnlyAllowedFieldsAreUpdated(IContent contentW
154128

155129
var allowedCultures = (await _languageService.GetIsoCodesByIdsAsync(allowedLanguageIds)).ToHashSet();
156130

131+
ILanguage? defaultLanguage = await _languageService.GetDefaultLanguageAsync();
132+
157133
foreach (var culture in contentWithPotentialUnallowedChanges.EditedCultures ?? contentWithPotentialUnallowedChanges.PublishedCultures)
158134
{
159135
if (allowedCultures.Contains(culture))
160136
{
161137
continue;
162138
}
163139

164-
165140
// else override the updates values with the original values.
166141
foreach (IProperty property in contentWithPotentialUnallowedChanges.Properties)
167142
{
168-
if (property.PropertyType.VariesByCulture() is false)
143+
// if the property varies by culture, simply overwrite the edited property value with the current property value
144+
if (property.PropertyType.VariesByCulture())
169145
{
146+
var currentValue = existingContent?.Properties.First(x => x.Alias == property.Alias).GetValue(culture, null, false);
147+
property.SetValue(currentValue, culture, null);
170148
continue;
171149
}
172150

173-
var value = existingContent?.Properties.First(x=>x.Alias == property.Alias).GetValue(culture, null, false);
174-
property.SetValue(value, culture, null);
151+
// if the property does not vary by culture and the data editor supports variance within invariant property values,
152+
// we need perform a merge between the edited property value and the current property value
153+
if (_propertyEditorCollection.TryGet(property.PropertyType.PropertyEditorAlias, out IDataEditor? dataEditor) && dataEditor.CanMergePartialPropertyValues(property.PropertyType))
154+
{
155+
var currentValue = existingContent?.Properties.First(x => x.Alias == property.Alias).GetValue(null, null, false);
156+
var editedValue = contentWithPotentialUnallowedChanges.Properties.First(x => x.Alias == property.Alias).GetValue(null, null, false);
157+
var mergedValue = dataEditor.MergePartialPropertyValueForCulture(currentValue, editedValue, culture);
158+
159+
// If we are not allowed to edit invariant properties, overwrite the edited property value with the current property value.
160+
if (_contentSettings.AllowEditInvariantFromNonDefault is false && culture == defaultLanguage?.IsoCode)
161+
{
162+
mergedValue = dataEditor.MergePartialPropertyValueForCulture(currentValue, mergedValue, null);
163+
}
164+
165+
property.SetValue(mergedValue, null, null);
166+
}
167+
168+
// If property does not support merging, we still need to overwrite if we are not allowed to edit invariant properties.
169+
else if (_contentSettings.AllowEditInvariantFromNonDefault is false && culture == defaultLanguage?.IsoCode)
170+
{
171+
var currentValue = existingContent?.Properties.First(x => x.Alias == property.Alias).GetValue(null, null, false);
172+
property.SetValue(currentValue, null, null);
173+
}
175174
}
176175
}
177176

178-
return contentWithPotentialUnallowedChanges;
177+
return contentWithPotentialUnallowedChanges;
179178
}
180179

181180
public async Task<Attempt<ContentUpdateResult, ContentEditingOperationStatus>> UpdateAsync(Guid key, ContentUpdateModel updateModel, Guid userKey)

tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/PropertyEditors/BlockEditorElementVariationTestBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public abstract class BlockEditorElementVariationTestBase : UmbracoIntegrationTe
3737

3838
protected PropertyEditorCollection PropertyEditorCollection => GetRequiredService<PropertyEditorCollection>();
3939

40+
protected IContentEditingService ContentEditingService => GetRequiredService<IContentEditingService>();
41+
4042
private IUmbracoContextAccessor UmbracoContextAccessor => GetRequiredService<IUmbracoContextAccessor>();
4143

4244
private IUmbracoContextFactory UmbracoContextFactory => GetRequiredService<IUmbracoContextFactory>();

0 commit comments

Comments
 (0)