Created new field in cmsContentType and related models for storing the configuration for a container doc type; implemented configurable page size

This commit is contained in:
AndyButland
2014-07-27 08:53:32 +02:00
parent c2816e759b
commit 599872a310
20 changed files with 191 additions and 22 deletions

View File

@@ -29,6 +29,7 @@ namespace Umbraco.Core.Models
private int _creatorId;
private bool _allowedAsRoot;
private bool _isContainer;
private string _containerConfig;
private bool _trashed;
private PropertyGroupCollection _propertyGroups;
private PropertyTypeCollection _propertyTypes;
@@ -72,6 +73,7 @@ namespace Umbraco.Core.Models
private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, int>(x => x.CreatorId);
private static readonly PropertyInfo AllowedAsRootSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, bool>(x => x.AllowedAsRoot);
private static readonly PropertyInfo IsContainerSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, bool>(x => x.IsContainer);
private static readonly PropertyInfo ContainerConfigSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, string>(x => x.ContainerConfig);
private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, bool>(x => x.Trashed);
private static readonly PropertyInfo AllowedContentTypesSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, IEnumerable<ContentTypeSort>>(x => x.AllowedContentTypes);
private static readonly PropertyInfo PropertyGroupCollectionSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, PropertyGroupCollection>(x => x.PropertyGroups);
@@ -303,6 +305,23 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Gets or Sets a JSON string defining the configuration for this ContentType if set as a container
/// </summary>
[DataMember]
public virtual string ContainerConfig
{
get { return _containerConfig; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_containerConfig = value;
return _containerConfig;
}, _containerConfig, ContainerConfigSelector);
}
}
/// <summary>
/// Boolean indicating whether this ContentType is Trashed or not.
/// If ContentType is Trashed it will be located in the Recyclebin.

View File

@@ -43,6 +43,11 @@ namespace Umbraco.Core.Models
/// </remarks>
bool IsContainer { get; set; }
/// <summary>
/// Gets or Sets a JSON string defining the configuration for this ContentType if set as a container
/// </summary>
string ContainerConfig { get; set; }
/// <summary>
/// Gets or Sets a list of integer Ids of the ContentTypes allowed under the ContentType
/// </summary>

View File

@@ -39,6 +39,10 @@ namespace Umbraco.Core.Models.Rdbms
[Constraint(Default = "0")]
public bool IsContainer { get; set; }
[Column("containerConfig")]
[NullSetting(NullSetting = NullSettings.Null)]
public string ContainerConfig { get; set; }
[Column("allowAtRoot")]
[Constraint(Default = "0")]
public bool AllowAtRoot { get; set; }

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
@@ -62,6 +63,10 @@ namespace Umbraco.Core.Models.Rdbms
[Column("isContainer")]
public bool IsContainer { get; set; }
[Column("containerConfig")]
[NullSetting(NullSetting = NullSettings.Null)]
public string ContainerConfig { get; set; }
[Column("allowAtRoot")]
public bool AllowAtRoot { get; set; }

View File

@@ -38,6 +38,7 @@ namespace Umbraco.Core.Persistence.Factories
CreatorId = dto.ContentTypeDto.NodeDto.UserId.Value,
AllowedAsRoot = dto.ContentTypeDto.AllowAtRoot,
IsContainer = dto.ContentTypeDto.IsContainer,
ContainerConfig = dto.ContentTypeDto.ContainerConfig,
Trashed = dto.ContentTypeDto.NodeDto.Trashed,
DefaultTemplateId = dto.TemplateNodeId
};
@@ -74,6 +75,7 @@ namespace Umbraco.Core.Persistence.Factories
NodeId = entity.Id,
AllowAtRoot = entity.AllowedAsRoot,
IsContainer = entity.IsContainer,
ContainerConfig = entity.ContainerConfig,
NodeDto = BuildNodeDto(entity)
};
return contentTypeDto;

View File

@@ -37,6 +37,7 @@ namespace Umbraco.Core.Persistence.Factories
CreatorId = dto.NodeDto.UserId.Value,
AllowedAsRoot = dto.AllowAtRoot,
IsContainer = dto.IsContainer,
ContainerConfig = dto.ContainerConfig,
Trashed = dto.NodeDto.Trashed
};
//on initial construction we don't want to have dirty properties tracked
@@ -56,6 +57,7 @@ namespace Umbraco.Core.Persistence.Factories
NodeId = entity.Id,
AllowAtRoot = entity.AllowedAsRoot,
IsContainer = entity.IsContainer,
ContainerConfig = entity.ContainerConfig,
NodeDto = BuildNodeDto(entity)
};
return contentTypeDto;

View File

@@ -32,6 +32,7 @@ namespace Umbraco.Core.Persistence.Factories
NodeId = entity.Id,
AllowAtRoot = entity.AllowedAsRoot,
IsContainer = entity.IsContainer,
ContainerConfig = entity.ContainerConfig,
NodeDto = BuildNodeDto(entity)
};
return contentTypeDto;

View File

@@ -23,6 +23,7 @@ namespace Umbraco.Core.Persistence.Factories
Icon = dto.Icon,
Id = dto.NodeId,
IsContainer = dto.IsContainer,
ContainerConfig = dto.ContainerConfig,
Key = dto.UniqueId.Value,
Level = dto.Level,
Name = dto.Text,

View File

@@ -47,6 +47,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap<ContentType, ContentTypeDto>(src => src.Description, dto => dto.Description);
CacheMap<ContentType, ContentTypeDto>(src => src.Icon, dto => dto.Icon);
CacheMap<ContentType, ContentTypeDto>(src => src.IsContainer, dto => dto.IsContainer);
CacheMap<ContentType, ContentTypeDto>(src => src.ContainerConfig, dto => dto.ContainerConfig);
CacheMap<ContentType, ContentTypeDto>(src => src.Thumbnail, dto => dto.Thumbnail);
}
}

View File

@@ -47,6 +47,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap<MediaType, ContentTypeDto>(src => src.Description, dto => dto.Description);
CacheMap<MediaType, ContentTypeDto>(src => src.Icon, dto => dto.Icon);
CacheMap<MediaType, ContentTypeDto>(src => src.IsContainer, dto => dto.IsContainer);
CacheMap<MediaType, ContentTypeDto>(src => src.ContainerConfig, dto => dto.ContainerConfig);
CacheMap<MediaType, ContentTypeDto>(src => src.Thumbnail, dto => dto.Thumbnail);
}
}

View File

@@ -48,6 +48,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap<MemberType, ContentTypeDto>(src => src.Description, dto => dto.Description);
CacheMap<MemberType, ContentTypeDto>(src => src.Icon, dto => dto.Icon);
CacheMap<MemberType, ContentTypeDto>(src => src.IsContainer, dto => dto.IsContainer);
CacheMap<MemberType, ContentTypeDto>(src => src.ContainerConfig, dto => dto.ContainerConfig);
CacheMap<MemberType, ContentTypeDto>(src => src.Thumbnail, dto => dto.Thumbnail);
}
}

View File

@@ -305,7 +305,8 @@ namespace Umbraco.Core.Services
new XElement("Thumbnail", contentType.Thumbnail),
new XElement("Description", contentType.Description),
new XElement("AllowAtRoot", contentType.AllowedAsRoot.ToString()),
new XElement("IsListView", contentType.IsContainer.ToString()));
new XElement("IsListView", contentType.IsContainer.ToString()),
new XElement("ContainerConfig", contentType.ContainerConfig.ToString()));
var masterContentType = contentType.CompositionAliases().FirstOrDefault();
if (masterContentType != null)

View File

@@ -423,8 +423,17 @@ namespace Umbraco.Core.Services
//NOTE IsListView is a new property in the package xml so we need to verify it exists before using it.
var isListView = infoElement.Element("IsListView");
if (isListView != null)
{
contentType.IsContainer = isListView.Value.InvariantEquals("true");
//NOTE ContainerConfig is a new property in the package xml so we need to verify it exists before using it.
var containerConfig = infoElement.Element("ContainerConfig");
if (containerConfig != null)
{
contentType.ContainerConfig = containerConfig.Value;
}
}
UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement);
UpdateContentTypesTabs(contentType, documentType.Element("Tabs"));
UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties"));

View File

@@ -68,6 +68,38 @@ function contentTypeResource($q, $http, umbRequestHelper) {
"GetAllowedChildren",
[{ contentId: contentId }])),
'Failed to retrieve data for content id ' + contentId);
},
/**
* @ngdoc method
* @name umbraco.resources.contentTypeResource#getContainerConfig
* @methodOf umbraco.resources.contentTypeResource
*
* @description
* Returns a JSON structure for configuration of the container content type
*
* ##usage
* <pre>
* contentTypeResource.getContainerConfig(1234)
* .then(function(config) {
* $scope.options = {
* pageSize: config.pageSize,
* };
* });
* </pre>
* @param {Int} contentId id of the content item to retrive the container config for
* @returns {Promise} resourcePromise object.
*
*/
getContainerConfig: function (contentId) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentTypeApiBaseUrl",
"GetContainerConfig",
[{ contentId: contentId }])),
'Failed to retrieve container config data for content id ' + contentId);
}
};

View File

@@ -35,6 +35,38 @@ function mediaTypeResource($q, $http, umbRequestHelper) {
"GetAllowedChildren",
[{ contentId: mediaId }])),
'Failed to retrieve allowed types for media id ' + mediaId);
},
/**
* @ngdoc method
* @name umbraco.resources.mediaTypeResource#getContainerConfig
* @methodOf umbraco.resources.mediaTypeResource
*
* @description
* Returns a JSON structure for configuration of the container content type
*
* ##usage
* <pre>
* mediaTypeResource.getContainerConfig(1234)
* .then(function(config) {
* $scope.options = {
* pageSize: config.pageSize,
* };
* });
* </pre>
* @param {Int} contentId id of the content item to retrive the container config for
* @returns {Promise} resourcePromise object.
*
*/
getContainerConfig: function (contentId) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl",
"GetContainerConfig",
[{ contentId: contentId }])),
'Failed to retrieve container config data for media id ' + contentId);
}
};

View File

@@ -29,14 +29,19 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
items: []
};
$scope.options = {
pageSize: 10,
pageNumber: 1,
filter: '',
orderBy: 'UpdateDate',
orderDirection: "desc"
};
// Retrieve the container configuration for the content type and set options before presenting initial view
contentTypeResource.getContainerConfig($routeParams.id)
.then(function (config) {
$scope.options = {
pageSize: config.pageSize,
pageNumber: 1,
filter: '',
orderBy: 'UpdateDate',
orderDirection: "desc"
};
$scope.initView();
});
$scope.next = function () {
if ($scope.options.pageNumber < $scope.listViewResultSet.totalPages) {
@@ -54,14 +59,12 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
$scope.options.orderBy = field;
if ($scope.options.orderDirection === "desc") {
$scope.options.orderDirection = "asc";
} else {
$scope.options.orderDirection = "desc";
}
$scope.reloadView($scope.contentId);
};
@@ -72,6 +75,17 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
}
};
$scope.initView = function () {
if ($routeParams.id) {
$scope.pagination = new Array(10);
$scope.listViewAllowedTypes = contentTypeResource.getAllowedTypes($routeParams.id);
$scope.reloadView($routeParams.id);
$scope.contentId = $routeParams.id;
$scope.isTrashed = $routeParams.id === "-20" || $routeParams.id === "-21";
}
};
/*Loads the search results, based on parameters set in prev,next,sort and so on*/
/*Pagination is done by an array of objects, due angularJS's funky way of monitoring state
with simple values */
@@ -80,7 +94,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
contentResource.getChildren(id, $scope.options).then(function (data) {
$scope.listViewResultSet = data;
$scope.pagination = [];
$scope.pagination = [];
for (var i = $scope.listViewResultSet.totalPages - 1; i >= 0; i--) {
$scope.pagination[i] = { index: i, name: i + 1 };
@@ -236,16 +250,6 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
}
};
if ($routeParams.id) {
$scope.pagination = new Array(10);
$scope.listViewAllowedTypes = contentTypeResource.getAllowedTypes($routeParams.id);
$scope.reloadView($routeParams.id);
$scope.contentId = $routeParams.id;
$scope.isTrashed = $routeParams.id === "-20" || $routeParams.id === "-21";
}
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.ListViewController", listViewController);

View File

@@ -12,6 +12,7 @@ using Umbraco.Web.WebApi;
using System.Linq;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
using Newtonsoft.Json;
namespace Umbraco.Web.Editors
{
@@ -86,6 +87,21 @@ namespace Umbraco.Web.Editors
return basics;
}
/// <summary>
/// Returns the container configuration JSON structure for the content item id passed in
/// </summary>
/// <param name="contentId"></param>
public ContentTypeContainerConfiguration GetContainerConfig(int contentId)
{
var contentItem = Services.ContentService.GetById(contentId);
if (contentItem == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return JsonConvert.DeserializeObject<ContentTypeContainerConfiguration>(contentItem.ContentType.ContainerConfig);
}
// TODO: This should really be centralized and used anywhere globalization applies.
internal string TranslateItem(string text)
{

View File

@@ -61,11 +61,24 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return contentItem.ContentType.AllowedContentTypes
.Select(x => Services.ContentTypeService.GetMediaType((int) x.Id.Value))
.Select(Mapper.Map<IMediaType, ContentTypeBasic>);
}
/// <summary>
/// Returns the container configuration JSON structure for the content item id passed in
/// </summary>
/// <param name="contentId"></param>
public JsonNetResult GetContainerConfig(int contentId)
{
var contentItem = Services.ContentService.GetById(contentId);
if (contentItem == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return JsonConvert.DeserializeObject<ContentTypeContainerConfiguration>(contentItem.ContentType.ContainerConfig);
}
}
}

View File

@@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
namespace Umbraco.Web.Models.ContentEditing
{
/// <summary>
/// A model representing the configuration for a content type defined as a list container
/// </summary>
[DataContract(Name = "config", Namespace = "")]
public class ContentTypeContainerConfiguration
{
/// <summary>
/// The page size for the list
/// </summary>
[DataMember(Name = "pageSize", IsRequired = true)]
[Required]
public int PageSize { get; set; }
}
}

View File

@@ -300,6 +300,7 @@
<Compile Include="Editors\EntityControllerConfigurationAttribute.cs" />
<Compile Include="Editors\ImagesController.cs" />
<Compile Include="Models\ContentEditing\ContentBaseItemSave.cs" />
<Compile Include="Models\ContentEditing\ContentTypeContainerConfiguration.cs" />
<Compile Include="Models\ContentEditing\MediaItemSave.cs" />
<Compile Include="Models\DetachedContent.cs" />
<Compile Include="Models\ImageCropAnchor.cs" />