1- using Microsoft . Extensions . Logging ;
1+ using Microsoft . Extensions . DependencyInjection ;
2+ using Microsoft . Extensions . Logging ;
3+ using Umbraco . Cms . Core . DependencyInjection ;
24using Umbraco . Cms . Core . Models ;
35using Umbraco . Cms . Core . Models . ContentEditing ;
6+ using Umbraco . Cms . Core . Models . Membership ;
47using Umbraco . Cms . Core . PropertyEditors ;
58using Umbraco . Cms . Core . Scoping ;
69using Umbraco . Cms . Core . Services . OperationStatus ;
10+ using Umbraco . Extensions ;
711
812namespace Umbraco . Cms . Core . Services ;
913
@@ -12,7 +16,11 @@ internal sealed class ContentEditingService
1216{
1317 private readonly ITemplateService _templateService ;
1418 private readonly ILogger < ContentEditingService > _logger ;
19+ private readonly IUserService _userService ;
20+ private readonly ILocalizationService _localizationService ;
21+ private readonly ILanguageService _languageService ;
1522
23+ [ Obsolete ( "Use non-obsolete constructor. This will be removed in Umbraco 16." ) ]
1624 public ContentEditingService (
1725 IContentService contentService ,
1826 IContentTypeService contentTypeService ,
@@ -24,10 +32,46 @@ public ContentEditingService(
2432 IUserIdKeyResolver userIdKeyResolver ,
2533 ITreeEntitySortingService treeEntitySortingService ,
2634 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+ }
53+
54+ public ContentEditingService (
55+ IContentService contentService ,
56+ IContentTypeService contentTypeService ,
57+ PropertyEditorCollection propertyEditorCollection ,
58+ IDataTypeService dataTypeService ,
59+ ITemplateService templateService ,
60+ ILogger < ContentEditingService > logger ,
61+ ICoreScopeProvider scopeProvider ,
62+ IUserIdKeyResolver userIdKeyResolver ,
63+ ITreeEntitySortingService treeEntitySortingService ,
64+ IContentValidationService contentValidationService ,
65+ IUserService userService ,
66+ ILocalizationService localizationService ,
67+ ILanguageService languageService )
2768 : base ( contentService , contentTypeService , propertyEditorCollection , dataTypeService , logger , scopeProvider , userIdKeyResolver , contentValidationService , treeEntitySortingService )
2869 {
2970 _templateService = templateService ;
3071 _logger = logger ;
72+ _userService = userService ;
73+ _localizationService = localizationService ;
74+ _languageService = languageService ;
3175 }
3276
3377 public async Task < IContent ? > GetAsync ( Guid key )
@@ -65,7 +109,7 @@ public async Task<Attempt<ContentCreateResult, ContentEditingOperationStatus>> C
65109 ContentEditingOperationStatus validationStatus = result . Status ;
66110 ContentValidationResult validationResult = result . Result . ValidationResult ;
67111
68- IContent content = result . Result . Content ! ;
112+ IContent content = await EnsureOnlyAllowedFieldsAreUpdated ( result . Result . Content ! , userKey ) ;
69113 ContentEditingOperationStatus updateTemplateStatus = await UpdateTemplateAsync ( content , createModel . TemplateKey ) ;
70114 if ( updateTemplateStatus != ContentEditingOperationStatus . Success )
71115 {
@@ -78,6 +122,53 @@ public async Task<Attempt<ContentCreateResult, ContentEditingOperationStatus>> C
78122 : Attempt . FailWithStatus ( saveStatus , new ContentCreateResult { Content = content } ) ;
79123 }
80124
125+ /// <summary>
126+ /// A temporary method that ensures the data is sent in is overridden by the original data, in cases where the user do not have permissions to change the data.
127+ /// </summary>
128+ private async Task < IContent > EnsureOnlyAllowedFieldsAreUpdated ( IContent contentWithPotentialUnallowedChanges , Guid userKey )
129+ {
130+ if ( contentWithPotentialUnallowedChanges . ContentType . VariesByCulture ( ) is false )
131+ {
132+ return contentWithPotentialUnallowedChanges ;
133+ }
134+
135+ IContent ? existingContent = await GetAsync ( contentWithPotentialUnallowedChanges . Key ) ;
136+
137+ IUser ? user = await _userService . GetAsync ( userKey ) ;
138+
139+ if ( user is null )
140+ {
141+ return contentWithPotentialUnallowedChanges ;
142+ }
143+
144+ var allowedLanguageIds = user . CalculateAllowedLanguageIds ( _localizationService ) ! ;
145+
146+ var allowedCultures = ( await _languageService . GetIsoCodesByIdsAsync ( allowedLanguageIds ) ) . ToHashSet ( ) ;
147+
148+ foreach ( var culture in contentWithPotentialUnallowedChanges . EditedCultures ?? contentWithPotentialUnallowedChanges . PublishedCultures )
149+ {
150+ if ( allowedCultures . Contains ( culture ) )
151+ {
152+ continue ;
153+ }
154+
155+
156+ // else override the updates values with the original values.
157+ foreach ( IProperty property in contentWithPotentialUnallowedChanges . Properties )
158+ {
159+ if ( property . PropertyType . VariesByCulture ( ) is false )
160+ {
161+ continue ;
162+ }
163+
164+ var value = existingContent ? . Properties . First ( x=> x . Alias == property . Alias ) . GetValue ( culture , null , false ) ;
165+ property . SetValue ( value , culture , null ) ;
166+ }
167+ }
168+
169+ return contentWithPotentialUnallowedChanges ;
170+ }
171+
81172 public async Task < Attempt < ContentUpdateResult , ContentEditingOperationStatus > > UpdateAsync ( Guid key , ContentUpdateModel updateModel , Guid userKey )
82173 {
83174 IContent ? content = ContentService . GetById ( key ) ;
@@ -102,6 +193,8 @@ public async Task<Attempt<ContentUpdateResult, ContentEditingOperationStatus>> U
102193 ContentEditingOperationStatus validationStatus = result . Status ;
103194 ContentValidationResult validationResult = result . Result . ValidationResult ;
104195
196+ content = await EnsureOnlyAllowedFieldsAreUpdated ( content , userKey ) ;
197+
105198 ContentEditingOperationStatus updateTemplateStatus = await UpdateTemplateAsync ( content , updateModel . TemplateKey ) ;
106199 if ( updateTemplateStatus != ContentEditingOperationStatus . Success )
107200 {
0 commit comments