diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index b46781f76d..33131bbb52 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -1375,7 +1375,7 @@ namespace Umbraco.Core.Services { DeleteBlueprintsOfTypes(new[] {contentTypeId}, userId); } - + /// /// Saves a single object /// diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index c575255eb2..6d0ca051a1 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -16,10 +16,10 @@ namespace Umbraco.Core.Services /// explicitly. These methods will replace the normal ones in IContentService in v8 and this will be removed. /// public interface IContentServiceOperations - { - //TODO: Remove this class in v8 - - //TODO: There's probably more that needs to be added like the EmptyRecycleBin, etc... + { + //TODO: Remove this class in v8 + + //TODO: There's probably more that needs to be added like the EmptyRecycleBin, etc... /// /// Saves a single object @@ -99,7 +99,7 @@ namespace Umbraco.Core.Services IEnumerable GetBlueprintsForContentTypes(params int[] documentTypeIds); IContent GetBlueprintById(int id); IContent GetBlueprintById(Guid id); - void SaveBlueprint(IContent content, int userId = 0); + void SaveBlueprint(IContent content, int userId = 0); void DeleteBlueprint(IContent content, int userId = 0); IContent CreateContentFromBlueprint(IContent blueprint, string name, int userId = 0); void DeleteBlueprintsOfType(int contentTypeId, int userId = 0); @@ -179,7 +179,7 @@ namespace Umbraco.Core.Services /// Alias of the /// Optional id of the user creating the content /// - IContent CreateContent(string name, Guid parentId, string contentTypeAlias, int userId = 0); + IContent CreateContent(string name, Guid parentId, string contentTypeAlias, int userId = 0); /// /// Creates an object using the alias of the @@ -644,7 +644,7 @@ namespace Umbraco.Core.Services /// /// /// True if sorting succeeded, otherwise False - bool Sort(IEnumerable items, int userId = 0, bool raiseEvents = true); + bool Sort(IEnumerable items, int userId = 0, bool raiseEvents = true); /// /// Sorts a collection of objects by updating the SortOrder according @@ -704,4 +704,4 @@ namespace Umbraco.Core.Services /// IContent CreateContentWithIdentity(string name, int parentId, string contentTypeAlias, int userId = 0); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index be78638a9e..64e3318b4c 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -95,8 +95,8 @@ namespace Umbraco.Web.Editors public IEnumerable PostSaveUserGroupPermissions(UserGroupPermissionsSave saveModel) { if (saveModel.ContentId <= 0) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - - //TODO: Should non-admins be alowed to set granular permissions? + + //TODO: Should non-admins be alowed to set granular permissions? var content = Services.ContentService.GetById(saveModel.ContentId); if (content == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); @@ -159,8 +159,8 @@ namespace Umbraco.Web.Editors if (contentId <= 0) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); var content = Services.ContentService.GetById(contentId); if (content == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - - //TODO: Should non-admins be able to see detailed permissions? + + //TODO: Should non-admins be able to see detailed permissions? var allUserGroups = Services.UserService.GetAllUserGroups(); @@ -379,7 +379,7 @@ namespace Umbraco.Web.Editors /// /// Gets the children for the content id passed in /// - /// + /// [FilterAllowedOutgoingContent(typeof(IEnumerable>), "Items")] public PagedResult> GetChildren( int id, @@ -409,9 +409,13 @@ namespace Umbraco.Web.Editors return new PagedResult>(0, 0, 0); } + // Note that we're excluding mapping of complex properties here to ensure that getting a larger amount of + // children for listviews and other similar cases, will not make everything halt when it tries to deserialize a + // complex property such as Nested Content. var pagedResult = new PagedResult>(totalChildren, pageNumber, pageSize); - pagedResult.Items = children - .Select(Mapper.Map>); + pagedResult.Items = children.Select(content => + Mapper.Map>(content, + opts => { opts.Items["ExcludeComplexProperties"] = true; })); return pagedResult; } @@ -644,7 +648,7 @@ namespace Umbraco.Web.Editors ShowMessageForPublishStatus(publishStatus.Result, display); break; } - + //If the item is new and the operation was cancelled, we need to return a different // status code so the UI can handle it since it won't be able to redirect since there // is no Id to redirect to! diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index c54574a08a..36b9edc3f9 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -195,7 +195,7 @@ namespace Umbraco.Web.Models.Mapping { var parent = _contentService.Value.GetById(source.ParentId); path = parent == null ? "-1" : parent.Path; - } + } var permissions = svc.GetPermissionsForPath( //TODO: This is certainly not ideal usage here - perhaps the best way to deal with this in the future is diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs index e7104f2939..89be6859d1 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Logging; @@ -13,11 +14,13 @@ namespace Umbraco.Web.Models.Mapping /// Creates a base generic ContentPropertyBasic from a Property /// /// - internal class ContentPropertyBasicConverter : TypeConverter + internal class ContentPropertyBasicConverter : ITypeConverter where T : ContentPropertyBasic, new() { protected IDataTypeService DataTypeService { get; private set; } + private static readonly List ComplexPropertyTypeAliases = new List {"Umbraco.NestedContent"}; + public ContentPropertyBasicConverter(IDataTypeService dataTypeService) { DataTypeService = dataTypeService; @@ -26,28 +29,45 @@ namespace Umbraco.Web.Models.Mapping /// /// Assigns the PropertyEditor, Id, Alias and Value to the property /// - /// /// - protected override T ConvertCore(Property property) + public T Convert(ResolutionContext context) { + var property = context.SourceValue as Property; + if (property == null) + throw new InvalidOperationException("Source value is not a property."); + var editor = PropertyEditorResolver.Current.GetByAlias(property.PropertyType.PropertyEditorAlias); if (editor == null) { LogHelper.Error>( "No property editor found, converting to a Label", - new NullReferenceException("The property editor with alias " + property.PropertyType.PropertyEditorAlias + " does not exist")); + new NullReferenceException("The property editor with alias " + + property.PropertyType.PropertyEditorAlias + " does not exist")); editor = PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias); } - var result = new T - { - Id = property.Id, - Value = editor.ValueEditor.ConvertDbToEditor(property, property.PropertyType, DataTypeService), - Alias = property.Alias, - PropertyEditor = editor, - Editor = editor.Alias - }; + var result = new T + { + Id = property.Id, + Alias = property.Alias, + PropertyEditor = editor, + Editor = editor.Alias + }; + + // Complex properties such as Nested Content do not need to be mapped for simpler things like list views, + // where they will not make sense to use anyways. To avoid having to do unnecessary mapping on large + // collections of items in list views - we allow excluding mapping of certain properties. + var excludeComplexProperties = false; + if (context.Options.Items.ContainsKey("ExcludeComplexProperties")) + { + excludeComplexProperties = System.Convert.ToBoolean(context.Options.Items["ExcludeComplexProperties"]); + } + if (excludeComplexProperties == false || ComplexPropertyTypeAliases.Contains(property.PropertyType.PropertyEditorAlias) == false) + { + result.Value = editor.ValueEditor.ConvertDbToEditor(property, property.PropertyType, DataTypeService); + } + return result; } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs index 8267e46e25..632268ab2f 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs @@ -1,7 +1,5 @@ using System; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Configuration; +using AutoMapper; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; @@ -21,27 +19,31 @@ namespace Umbraco.Web.Models.Mapping _textService = textService; } - protected override ContentPropertyDisplay ConvertCore(Property originalProp) + public new ContentPropertyDisplay Convert(ResolutionContext context) { - var display = base.ConvertCore(originalProp); + var display = base.Convert(context); + + var originalProperty = context.SourceValue as Property; + if (originalProperty == null) + throw new InvalidOperationException("Source value is not a property."); var dataTypeService = DataTypeService; - var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(originalProp.PropertyType.DataTypeDefinitionId); + var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(originalProperty.PropertyType.DataTypeDefinitionId); //configure the editor for display with the pre-values var valEditor = display.PropertyEditor.ValueEditor; valEditor.ConfigureForDisplay(preVals); //set the display properties after mapping - display.Alias = originalProp.Alias; - display.Description = originalProp.PropertyType.Description; - display.Label = originalProp.PropertyType.Name; + display.Alias = originalProperty.Alias; + display.Description = originalProperty.PropertyType.Description; + display.Label = originalProperty.PropertyType.Name; display.HideLabel = valEditor.HideLabel; - + //add the validation information - display.Validation.Mandatory = originalProp.PropertyType.Mandatory; - display.Validation.Pattern = originalProp.PropertyType.ValidationRegExp; - + display.Validation.Mandatory = originalProperty.PropertyType.Mandatory; + display.Validation.Pattern = originalProperty.PropertyType.ValidationRegExp; + if (display.PropertyEditor == null) { //display.Config = PreValueCollection.AsDictionary(preVals); diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs index f3f9fbe9d5..80b8c28309 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs @@ -1,7 +1,6 @@ using System; -using Umbraco.Core; +using AutoMapper; using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; @@ -17,9 +16,13 @@ namespace Umbraco.Web.Models.Mapping { } - protected override ContentPropertyDto ConvertCore(Property originalProperty) + public new ContentPropertyDto Convert(ResolutionContext context) { - var propertyDto = base.ConvertCore(originalProperty); + var propertyDto = base.Convert(context); + + var originalProperty = context.SourceValue as Property; + if (originalProperty == null) + throw new InvalidOperationException("Source value is not a property."); var dataTypeService = DataTypeService; @@ -27,7 +30,7 @@ namespace Umbraco.Web.Models.Mapping propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp; propertyDto.Description = originalProperty.PropertyType.Description; propertyDto.Label = originalProperty.PropertyType.Name; - + //TODO: We should be able to look both of these up at the same time! propertyDto.DataType = dataTypeService.GetDataTypeDefinitionById(originalProperty.PropertyType.DataTypeDefinitionId); propertyDto.PreValues = dataTypeService.GetPreValuesCollectionByDataTypeId(originalProperty.PropertyType.DataTypeDefinitionId); diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs index 5e29bcf125..f401832020 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs @@ -1,9 +1,7 @@ -using System; -using AutoMapper; +using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Mapping; -using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping