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.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 642e4fb13b..1ba454ac65 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -57,7 +57,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { saveModel), 'Failed to save permissions'); }, - + getRecycleBin: function () { return umbRequestHelper.resourcePromise( @@ -424,7 +424,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { $http.get( umbRequestHelper.getApiUrl( "contentApiBaseUrl", - "GetEmpty", + "GetEmpty", [{ blueprintId: blueprintId }, { parentId: parentId}])), 'Failed to retrieve blueprint for id ' + blueprintId); }, @@ -488,6 +488,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { getChildren: function (parentId, options) { var defaults = { + includeProperties: [], pageSize: 0, pageNumber: 0, filter: '', @@ -531,6 +532,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { "GetChildren", { id: parentId, + includeProperties: _.pluck(options.includeProperties, 'alias').join(","), pageNumber: options.pageNumber, pageSize: options.pageSize, orderBy: options.orderBy, @@ -580,7 +582,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { "contentApiBaseUrl", "GetDetailedPermissions", { contentId: contentId })), 'Failed to retrieve permissions for content item ' + contentId); - }, + }, getPermissions: function (nodeIds) { return umbRequestHelper.resourcePromise( @@ -741,11 +743,11 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { createBlueprintFromContent: function (contentId, name) { return umbRequestHelper.resourcePromise( - $http.post( + $http.post( umbRequestHelper.getApiUrl("contentApiBaseUrl", "CreateBlueprintFromContent", { contentId: contentId, name: name - }) - ), + }) + ), "Failed to create blueprint from content with id " + contentId ); } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index be78638a9e..26f938638c 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, @@ -389,7 +389,25 @@ namespace Umbraco.Web.Editors Direction orderDirection = Direction.Ascending, bool orderBySystemField = true, string filter = "") - { + { + return GetChildren(id, null, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter); + } + + /// + /// Gets the children for the content id passed in + /// + /// + [FilterAllowedOutgoingContent(typeof(IEnumerable>), "Items")] + public PagedResult> GetChildren( + int id, + string includeProperties, + int pageNumber = 0, //TODO: This should be '1' as it's not the index + int pageSize = 0, + string orderBy = "SortOrder", + Direction orderDirection = Direction.Ascending, + bool orderBySystemField = true, + string filter = "") + { long totalChildren; IContent[] children; if (pageNumber > 0 && pageSize > 0) @@ -410,8 +428,16 @@ namespace Umbraco.Web.Editors } var pagedResult = new PagedResult>(totalChildren, pageNumber, pageSize); - pagedResult.Items = children - .Select(Mapper.Map>); + pagedResult.Items = children.Select(content => + Mapper.Map>(content, + opts => + { + // if there's a list of property aliases to map - we will make sure to store this in the mapping context. + if (String.IsNullOrWhiteSpace(includeProperties) == false) + { + opts.Items["IncludeProperties"] = includeProperties.Split(new[] { ", ", "," }, StringSplitOptions.RemoveEmptyEntries); + } + })); return pagedResult; } @@ -644,7 +670,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..ccf22a8c34 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Logging; @@ -13,11 +15,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 +30,45 @@ namespace Umbraco.Web.Models.Mapping /// /// Assigns the PropertyEditor, Id, Alias and Value to the property /// - /// /// - protected override T ConvertCore(Property property) + public virtual 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 + }; + + // if there's a set of property aliases specified, we will check if the current property's value should be mapped. + // if it isn't one of the ones specified in 'includeProperties', we will just return the result without mapping the Value. + if (context.Options.Items.ContainsKey("IncludeProperties")) + { + var includeProperties = context.Options.Items["IncludeProperties"] as IEnumerable; + if (includeProperties != null && includeProperties.Contains(property.Alias) == false) + { + return result; + } + } + + // if no 'IncludeProperties' were specified or this property is set to be included - we will map the value and return. + 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..86c79d9c59 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; @@ -20,28 +18,31 @@ namespace Umbraco.Web.Models.Mapping { _textService = textService; } - - protected override ContentPropertyDisplay ConvertCore(Property originalProp) + public override 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..61fd4fbe00 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 override 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