Document and document type read API (#13853)
* Basic structure for document and document type read API * Handle unpublished, non-variant content * Expose content type key on ContentTypeSort * Add the remaining properties to document type (minus list view info, still pending) * Obsolete more ILocalizationService usage * Add URLs and template data to document view model * Clean up + add proprety type appearance * update submodule commit * front-end commit * latest front-end commit * latest commit * latest front-end commit * Rename content property to content value in view model layer * Add contextual JSON serialization as default JSON serializer * Add FIXME to content type sort + rearrange constructor parameters * Fix broken remark tag * Whitelist breakage for ContentTypeSort * Add variance info to property type output * Update src/Umbraco.Cms.Api.Management/Controllers/Document/ByKeyDocumentController.cs Co-authored-by: Bjarke Berg <mail@bergmania.dk> * Update src/Umbraco.Cms.Api.Management/Controllers/DocumentType/ByKeyDocumentTypeController.cs Co-authored-by: Bjarke Berg <mail@bergmania.dk> * Update src/Umbraco.Cms.Api.Management/Factories/ContentUrlFactory.cs Co-authored-by: Bjarke Berg <mail@bergmania.dk> * Add a few FIXME comments about async entity retrieval --------- Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com> Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
|
||||
public class ByKeyDocumentController : DocumentControllerBase
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IDocumentViewModelFactory _documentViewModelFactory;
|
||||
|
||||
public ByKeyDocumentController(IContentService contentService, IDocumentViewModelFactory documentViewModelFactory)
|
||||
{
|
||||
_contentService = contentService;
|
||||
_documentViewModelFactory = documentViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpGet("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(DocumentViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ByKey(Guid key)
|
||||
{
|
||||
// FIXME: create and use an async get method here.
|
||||
IContent? content = _contentService.GetById(key);
|
||||
if (content == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
DocumentViewModel model = await _documentViewModelFactory.CreateViewModelAsync(content);
|
||||
return Ok(model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Document;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute(Constants.UdiEntityType.Document)]
|
||||
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))]
|
||||
public abstract class DocumentControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.DocumentType;
|
||||
|
||||
public class ByKeyDocumentTypeController : DocumentTypeControllerBase
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public ByKeyDocumentTypeController(IContentTypeService contentTypeService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_contentTypeService = contentTypeService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
[HttpGet("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(DocumentTypeViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ByKey(Guid key)
|
||||
{
|
||||
// FIXME: create and use an async get method here.
|
||||
IContentType? contentType = _contentTypeService.Get(key);
|
||||
if (contentType == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
DocumentTypeViewModel model = _umbracoMapper.Map<DocumentTypeViewModel>(contentType)!;
|
||||
return await Task.FromResult(Ok(model));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Management.Routing;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.DocumentType;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute(Constants.UdiEntityType.DocumentType)]
|
||||
[ApiExplorerSettings(GroupName = "Document Type")]
|
||||
public abstract class DocumentTypeControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.Mapping.Document;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class DocumentBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddDocuments(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddTransient<IDocumentViewModelFactory, DocumentViewModelFactory>();
|
||||
builder.Services.AddTransient<IContentUrlFactory, ContentUrlFactory>();
|
||||
|
||||
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>().Add<DocumentMapDefinition>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Umbraco.Cms.Api.Management.Mapping.DocumentType;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.DependencyInjection;
|
||||
|
||||
internal static class DocumentTypeBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddDocumentTypes(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>().Add<DocumentTypeMapDefinition>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public class ContentUrlFactory : IContentUrlFactory
|
||||
{
|
||||
private readonly IPublishedRouter _publishedRouter;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly ILanguageService _languageService;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly UriUtility _uriUtility;
|
||||
private readonly IPublishedUrlProvider _publishedUrlProvider;
|
||||
|
||||
public ContentUrlFactory(
|
||||
IPublishedRouter publishedRouter,
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
ILanguageService languageService,
|
||||
ILocalizedTextService localizedTextService,
|
||||
IContentService contentService,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
ILoggerFactory loggerFactory,
|
||||
UriUtility uriUtility,
|
||||
IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
_publishedRouter = publishedRouter;
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_languageService = languageService;
|
||||
_localizedTextService = localizedTextService;
|
||||
_contentService = contentService;
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
_loggerFactory = loggerFactory;
|
||||
_uriUtility = uriUtility;
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ContentUrlInfo>> GetUrlsAsync(IContent content)
|
||||
{
|
||||
IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
|
||||
|
||||
UrlInfo[] urlInfos = (await content.GetContentUrlsAsync(
|
||||
_publishedRouter,
|
||||
umbracoContext,
|
||||
_languageService,
|
||||
_localizedTextService,
|
||||
_contentService,
|
||||
_variationContextAccessor,
|
||||
_loggerFactory.CreateLogger<IContent>(),
|
||||
_uriUtility,
|
||||
_publishedUrlProvider)).ToArray();
|
||||
|
||||
return urlInfos
|
||||
.Where(urlInfo => urlInfo.IsUrl)
|
||||
.Select(urlInfo => new ContentUrlInfo { Culture = urlInfo.Culture, Url = urlInfo.Text })
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public class DocumentViewModelFactory : IDocumentViewModelFactory
|
||||
{
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
private readonly IContentUrlFactory _contentUrlFactory;
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
public DocumentViewModelFactory(
|
||||
IUmbracoMapper umbracoMapper,
|
||||
IContentUrlFactory contentUrlFactory,
|
||||
IFileService fileService)
|
||||
{
|
||||
_umbracoMapper = umbracoMapper;
|
||||
_contentUrlFactory = contentUrlFactory;
|
||||
_fileService = fileService;
|
||||
}
|
||||
|
||||
public async Task<DocumentViewModel> CreateViewModelAsync(IContent content)
|
||||
{
|
||||
DocumentViewModel viewModel = _umbracoMapper.Map<DocumentViewModel>(content)!;
|
||||
|
||||
viewModel.Urls = await _contentUrlFactory.GetUrlsAsync(content);
|
||||
|
||||
viewModel.TemplateKey = content.TemplateId.HasValue
|
||||
? _fileService.GetTemplate(content.TemplateId.Value)?.Key
|
||||
: null;
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public interface IContentUrlFactory
|
||||
{
|
||||
Task<IEnumerable<ContentUrlInfo>> GetUrlsAsync(IContent content);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Factories;
|
||||
|
||||
public interface IDocumentViewModelFactory
|
||||
{
|
||||
Task<DocumentViewModel> CreateViewModelAsync(IContent content);
|
||||
}
|
||||
@@ -25,6 +25,8 @@ public class ManagementApiComposer : IComposer
|
||||
.AddUpgrader()
|
||||
.AddSearchManagement()
|
||||
.AddTrees()
|
||||
.AddDocuments()
|
||||
.AddDocumentTypes()
|
||||
.AddLanguages()
|
||||
.AddDictionary()
|
||||
.AddFileUpload()
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Mapping.Content;
|
||||
|
||||
public abstract class ContentMapDefinition<TContent, TValueViewModel, TVariantViewModel>
|
||||
where TContent : IContentBase
|
||||
where TValueViewModel : ValueViewModelBase, new()
|
||||
where TVariantViewModel : VariantViewModelBase, new()
|
||||
{
|
||||
private readonly PropertyEditorCollection _propertyEditorCollection;
|
||||
|
||||
protected ContentMapDefinition(PropertyEditorCollection propertyEditorCollection) => _propertyEditorCollection = propertyEditorCollection;
|
||||
|
||||
protected delegate void ValueViewModelMapping(IDataEditor propertyEditor, TValueViewModel variantViewModel);
|
||||
|
||||
protected delegate void VariantViewModelMapping(string? culture, string? segment, TVariantViewModel variantViewModel);
|
||||
|
||||
protected IEnumerable<TValueViewModel> MapValueViewModels(TContent source, ValueViewModelMapping? additionalPropertyMapping = null) =>
|
||||
source
|
||||
.Properties
|
||||
.SelectMany(property => property
|
||||
.Values
|
||||
.Select(propertyValue =>
|
||||
{
|
||||
IDataEditor? propertyEditor = _propertyEditorCollection[property.PropertyType.PropertyEditorAlias];
|
||||
if (propertyEditor == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var variantViewModel = new TValueViewModel
|
||||
{
|
||||
Culture = propertyValue.Culture,
|
||||
Segment = propertyValue.Segment,
|
||||
Alias = property.Alias,
|
||||
Value = propertyEditor.GetValueEditor().ToEditor(property, propertyValue.Culture, propertyValue.Segment)
|
||||
};
|
||||
additionalPropertyMapping?.Invoke(propertyEditor, variantViewModel);
|
||||
return variantViewModel;
|
||||
}))
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
protected IEnumerable<TVariantViewModel> MapVariantViewModels(TContent source, VariantViewModelMapping? additionalVariantMapping = null)
|
||||
{
|
||||
IPropertyValue[] propertyValues = source.Properties.SelectMany(propertyCollection => propertyCollection.Values).ToArray();
|
||||
var cultures = source.AvailableCultures.DefaultIfEmpty(null).ToArray();
|
||||
var segments = propertyValues.Select(property => property.Segment).Distinct().DefaultIfEmpty(null).ToArray();
|
||||
|
||||
return cultures
|
||||
.SelectMany(culture => segments.Select(segment =>
|
||||
{
|
||||
var variantViewModel = new TVariantViewModel
|
||||
{
|
||||
Culture = culture,
|
||||
Segment = segment,
|
||||
Name = source.GetCultureName(culture) ?? string.Empty,
|
||||
CreateDate = source.CreateDate, // apparently there is no culture specific creation date
|
||||
UpdateDate = culture == null
|
||||
? source.UpdateDate
|
||||
: source.GetUpdateDate(culture) ?? source.UpdateDate,
|
||||
};
|
||||
additionalVariantMapping?.Invoke(culture, segment, variantViewModel);
|
||||
return variantViewModel;
|
||||
}))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Mapping.ContentType;
|
||||
|
||||
public abstract class ContentTypeMapDefinition<TContentType, TPropertyTypeViewModel, TPropertyTypeContainerViewModel>
|
||||
where TContentType : IContentTypeBase
|
||||
where TPropertyTypeViewModel : PropertyTypeViewModelBase, new()
|
||||
where TPropertyTypeContainerViewModel : PropertyTypeContainerViewModelBase, new()
|
||||
{
|
||||
protected IEnumerable<TPropertyTypeViewModel> MapPropertyTypes(TContentType source)
|
||||
{
|
||||
// create a mapping table between properties and their associated groups
|
||||
var groupKeysByPropertyKeys = source
|
||||
.PropertyGroups
|
||||
.SelectMany(propertyGroup => (propertyGroup.PropertyTypes?.ToArray() ?? Array.Empty<PropertyType>())
|
||||
.Select(propertyType => new { GroupKey = propertyGroup.Key, PropertyTypeKey = propertyType.Key }))
|
||||
.ToDictionary(map => map.PropertyTypeKey, map => map.GroupKey);
|
||||
|
||||
return source.PropertyTypes.Select(propertyType =>
|
||||
new TPropertyTypeViewModel
|
||||
{
|
||||
Key = propertyType.Key,
|
||||
ContainerKey = groupKeysByPropertyKeys.ContainsKey(propertyType.Key)
|
||||
? groupKeysByPropertyKeys[propertyType.Key]
|
||||
: null,
|
||||
Name = propertyType.Name,
|
||||
Alias = propertyType.Alias,
|
||||
Description = propertyType.Description,
|
||||
DataTypeKey = propertyType.DataTypeKey,
|
||||
VariesByCulture = propertyType.VariesByCulture(),
|
||||
VariesBySegment = propertyType.VariesBySegment(),
|
||||
Validation = new PropertyTypeValidation
|
||||
{
|
||||
Mandatory = propertyType.Mandatory,
|
||||
MandatoryMessage = propertyType.MandatoryMessage,
|
||||
RegEx = propertyType.ValidationRegExp,
|
||||
RegExMessage = propertyType.ValidationRegExpMessage
|
||||
},
|
||||
Appearance = new PropertyTypeAppearance
|
||||
{
|
||||
LabelOnTop = propertyType.LabelOnTop
|
||||
}
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
protected IEnumerable<TPropertyTypeContainerViewModel> MapPropertyTypeContainers(TContentType source)
|
||||
{
|
||||
// create a mapping table between property group aliases and keys
|
||||
var groupKeysByGroupAliases = source
|
||||
.PropertyGroups
|
||||
.ToDictionary(propertyGroup => propertyGroup.Alias, propertyGroup => propertyGroup.Key);
|
||||
|
||||
Guid? ParentGroupKey(PropertyGroup group)
|
||||
{
|
||||
var path = group.Alias.Split(Constants.CharArrays.ForwardSlash);
|
||||
return path.Length == 1 || groupKeysByGroupAliases.TryGetValue(path.First(), out Guid parentGroupKey) == false
|
||||
? null
|
||||
: parentGroupKey;
|
||||
}
|
||||
|
||||
return source
|
||||
.PropertyGroups
|
||||
.Select(propertyGroup =>
|
||||
new TPropertyTypeContainerViewModel
|
||||
{
|
||||
Key = propertyGroup.Key,
|
||||
ParentKey = ParentGroupKey(propertyGroup),
|
||||
Type = propertyGroup.Type.ToString(),
|
||||
SortOrder = propertyGroup.SortOrder,
|
||||
Name = propertyGroup.Name,
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Umbraco.Cms.Api.Management.Mapping.Content;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Mapping.Document;
|
||||
|
||||
public class DocumentMapDefinition : ContentMapDefinition<IContent, DocumentValueViewModel, DocumentVariantViewModel>, IMapDefinition
|
||||
{
|
||||
public DocumentMapDefinition(PropertyEditorCollection propertyEditorCollection)
|
||||
: base(propertyEditorCollection)
|
||||
{
|
||||
}
|
||||
|
||||
public void DefineMaps(IUmbracoMapper mapper)
|
||||
=> mapper.Define<IContent, DocumentViewModel>((_, _) => new DocumentViewModel(), Map);
|
||||
|
||||
// Umbraco.Code.MapAll -Urls -TemplateKey
|
||||
private void Map(IContent source, DocumentViewModel target, MapperContext context)
|
||||
{
|
||||
target.Key = source.Key;
|
||||
target.ContentTypeKey = source.ContentType.Key;
|
||||
target.Values = MapValueViewModels(source);
|
||||
target.Variants = MapVariantViewModels(
|
||||
source,
|
||||
(culture, _, documentVariantViewModel) =>
|
||||
{
|
||||
documentVariantViewModel.State = GetSavedState(source, culture);
|
||||
documentVariantViewModel.PublishDate = culture == null
|
||||
? source.PublishDate
|
||||
: source.GetPublishDate(culture);
|
||||
});
|
||||
}
|
||||
|
||||
private ContentState GetSavedState(IContent content, string? culture)
|
||||
{
|
||||
if (content.Id <= 0 || (culture != null && content.IsCultureAvailable(culture) == false))
|
||||
{
|
||||
return ContentState.NotCreated;
|
||||
}
|
||||
|
||||
var isDraft = content.PublishedState == PublishedState.Unpublished ||
|
||||
(culture != null && content.IsCulturePublished(culture) == false);
|
||||
if (isDraft)
|
||||
{
|
||||
return ContentState.Draft;
|
||||
}
|
||||
|
||||
var isEdited = culture != null
|
||||
? content.IsCultureEdited(culture)
|
||||
: content.Edited;
|
||||
|
||||
return isEdited ? ContentState.PublishedPendingChanges : ContentState.Published;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Umbraco.Cms.Api.Management.Mapping.ContentType;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Extensions;
|
||||
using ContentTypeSort = Umbraco.Cms.Api.Management.ViewModels.ContentType.ContentTypeSort;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Mapping.DocumentType;
|
||||
|
||||
public class DocumentTypeMapDefinition : ContentTypeMapDefinition<IContentType, DocumentTypePropertyTypeViewModel, DocumentTypePropertyTypeContainerViewModel>, IMapDefinition
|
||||
{
|
||||
public void DefineMaps(IUmbracoMapper mapper)
|
||||
=> mapper.Define<IContentType, DocumentTypeViewModel>((_, _) => new DocumentTypeViewModel(), Map);
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
private void Map(IContentType source, DocumentTypeViewModel target, MapperContext context)
|
||||
{
|
||||
target.Key = source.Key;
|
||||
target.Alias = source.Alias;
|
||||
target.Name = source.Name ?? string.Empty;
|
||||
target.Description = source.Description;
|
||||
target.Icon = source.Icon ?? string.Empty;
|
||||
target.AllowedAsRoot = source.AllowedAsRoot;
|
||||
target.VariesByCulture = source.VariesByCulture();
|
||||
target.VariesBySegment = source.VariesBySegment();
|
||||
target.IsElement = source.IsElement;
|
||||
target.Containers = MapPropertyTypeContainers(source);
|
||||
target.Properties = MapPropertyTypes(source);
|
||||
|
||||
if (source.AllowedContentTypes != null)
|
||||
{
|
||||
target.AllowedContentTypes = source.AllowedContentTypes.Select(contentTypeSort
|
||||
=> new ContentTypeSort { Key = contentTypeSort.Key, SortOrder = contentTypeSort.SortOrder });
|
||||
}
|
||||
|
||||
if (source.AllowedTemplates != null)
|
||||
{
|
||||
target.AllowedTemplateKeys = source.AllowedTemplates.Select(template => template.Key);
|
||||
}
|
||||
|
||||
target.DefaultTemplateKey = source.DefaultTemplate?.Key;
|
||||
|
||||
if (source.HistoryCleanup != null)
|
||||
{
|
||||
target.Cleanup = new ContentTypeCleanup
|
||||
{
|
||||
PreventCleanup = source.HistoryCleanup.PreventCleanup,
|
||||
KeepAllVersionsNewerThanDays = source.HistoryCleanup.KeepAllVersionsNewerThanDays,
|
||||
KeepLatestVersionPerDayForDays = source.HistoryCleanup.KeepLatestVersionPerDayForDays
|
||||
};
|
||||
}
|
||||
|
||||
target.Compositions = source.ContentTypeComposition.Select(contentType => new ContentTypeComposition
|
||||
{
|
||||
Key = contentType.Key,
|
||||
CompositionType = contentType.Id == source.ParentId
|
||||
? ContentTypeCompositionType.Inheritance
|
||||
: ContentTypeCompositionType.Composition
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -1241,6 +1241,44 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/document-type/{key}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Document Type"
|
||||
],
|
||||
"operationId": "GetDocumentTypeByKey",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentTypeModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/tree/document-type/children": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -1385,6 +1423,44 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/document/{key}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Document"
|
||||
],
|
||||
"operationId": "GetDocumentByKey",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/recycle-bin/document/children": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -5465,6 +5541,16 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ContentStateModel": {
|
||||
"enum": [
|
||||
"NotCreated",
|
||||
"Draft",
|
||||
"Published",
|
||||
"PublishedPendingChanges"
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"ContentTreeItemModel": {
|
||||
"required": [
|
||||
"$type"
|
||||
@@ -5495,6 +5581,189 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ContentTypeCleanupModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"preventCleanup": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keepAllVersionsNewerThanDays": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"nullable": true
|
||||
},
|
||||
"keepLatestVersionPerDayForDays": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ContentTypeCompositionModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"compositionType": {
|
||||
"$ref": "#/components/schemas/ContentTypeCompositionTypeModel"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ContentTypeCompositionTypeModel": {
|
||||
"enum": [
|
||||
"Composition",
|
||||
"Inheritance"
|
||||
],
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"ContentTypeSortModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"sortOrder": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"alias": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
},
|
||||
"allowedAsRoot": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"variesByCulture": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"variesBySegment": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isElement": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"properties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentTypePropertyTypeModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"containers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentTypePropertyTypeContainerModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"allowedContentTypes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ContentTypeSortModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"compositions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ContentTypeCompositionModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cleanup": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ContentTypeCleanupModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ContentUrlInfoModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"culture": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ContentViewModelBaseDocumentValueDocumentVariantModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"contentTypeKey": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentValueModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"variants": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/DocumentVariantModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"CultureModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -5955,6 +6224,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"DocumentModel": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ContentViewModelBaseDocumentValueDocumentVariantModel"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"urls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ContentUrlInfoModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"templateKey": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DocumentTreeItemModel": {
|
||||
"required": [
|
||||
"$type"
|
||||
@@ -5987,6 +6282,47 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"DocumentTypeModel": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"allowedTemplateKeys": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
"defaultTemplateKey": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DocumentTypePropertyTypeContainerModel": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PropertyTypeContainerViewModelBaseModel"
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DocumentTypePropertyTypeModel": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PropertyTypeViewModelBaseModel"
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DocumentTypeTreeItemModel": {
|
||||
"required": [
|
||||
"$type"
|
||||
@@ -6013,6 +6349,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"DocumentValueModel": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/ValueViewModelBaseModel"
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DocumentVariantModel": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/VariantViewModelBaseModel"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"state": {
|
||||
"$ref": "#/components/schemas/ContentStateModel"
|
||||
},
|
||||
"publishDate": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"EntityTreeItemModel": {
|
||||
"required": [
|
||||
"$type"
|
||||
@@ -7345,6 +7709,111 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PropertyTypeAppearanceModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"labelOnTop": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PropertyTypeContainerViewModelBaseModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"parentKey": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"nullable": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"sortOrder": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PropertyTypeValidationModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mandatory": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"mandatoryMessage": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"regEx": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"regExMessage": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PropertyTypeViewModelBaseModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"containerKey": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"nullable": true
|
||||
},
|
||||
"alias": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"dataTypeKey": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"variesByCulture": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"variesBySegment": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"validation": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PropertyTypeValidationModel"
|
||||
}
|
||||
]
|
||||
},
|
||||
"appearance": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PropertyTypeAppearanceModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"RecycleBinItemModel": {
|
||||
"required": [
|
||||
"$type"
|
||||
@@ -8029,6 +8498,51 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ValueViewModelBaseModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"culture": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"segment": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"alias": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"VariantViewModelBaseModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"culture": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"segment": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updateDate": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"VersionModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
/// <summary>
|
||||
/// The saved state of a content item
|
||||
/// </summary>
|
||||
public enum ContentState
|
||||
{
|
||||
/// <summary>
|
||||
/// The item isn't created yet
|
||||
/// </summary>
|
||||
NotCreated = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The item is saved but isn't published
|
||||
/// </summary>
|
||||
Draft = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The item is published and there are no pending changes
|
||||
/// </summary>
|
||||
Published = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The item is published and there are pending changes
|
||||
/// </summary>
|
||||
PublishedPendingChanges = 4,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
public class ContentUrlInfo
|
||||
{
|
||||
public required string? Culture { get; init; }
|
||||
|
||||
public required string Url { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
public abstract class ContentViewModelBase<TValueViewModelBase, TVariantViewModel>
|
||||
where TValueViewModelBase : ValueViewModelBase
|
||||
where TVariantViewModel : VariantViewModelBase
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
public Guid ContentTypeKey { get; set; }
|
||||
|
||||
public IEnumerable<TValueViewModelBase> Values { get; set; } = Array.Empty<TValueViewModelBase>();
|
||||
|
||||
public IEnumerable<TVariantViewModel> Variants { get; set; } = Array.Empty<TVariantViewModel>();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
public abstract class ValueViewModelBase
|
||||
{
|
||||
public string? Culture { get; set; }
|
||||
|
||||
public string? Segment { get; set; }
|
||||
|
||||
public string Alias { get; set; } = string.Empty;
|
||||
|
||||
public object? Value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
public abstract class VariantViewModelBase
|
||||
{
|
||||
public string? Culture { get; set; }
|
||||
|
||||
public string? Segment { get; set; }
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
public DateTime UpdateDate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class ContentTypeCleanup
|
||||
{
|
||||
public bool PreventCleanup { get; init; }
|
||||
|
||||
public int? KeepAllVersionsNewerThanDays { get; init; }
|
||||
|
||||
public int? KeepLatestVersionPerDayForDays { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class ContentTypeComposition
|
||||
{
|
||||
public required Guid Key { get; init; }
|
||||
|
||||
public required ContentTypeCompositionType CompositionType { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public enum ContentTypeCompositionType
|
||||
{
|
||||
Composition,
|
||||
Inheritance
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class ContentTypeSort
|
||||
{
|
||||
public required Guid Key { get; init; }
|
||||
|
||||
public required int SortOrder { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public abstract class ContentTypeViewModelBase<TPropertyType, TPropertyTypeContainer>
|
||||
where TPropertyType : PropertyTypeViewModelBase
|
||||
where TPropertyTypeContainer : PropertyTypeContainerViewModelBase
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
public string Alias { get; set; } = string.Empty;
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string Icon { get; set; } = string.Empty;
|
||||
|
||||
public bool AllowedAsRoot { get; set; }
|
||||
|
||||
public bool VariesByCulture { get; set; }
|
||||
|
||||
public bool VariesBySegment { get; set; }
|
||||
|
||||
public bool IsElement { get; set; }
|
||||
|
||||
public IEnumerable<TPropertyType> Properties { get; set; } = Array.Empty<TPropertyType>();
|
||||
|
||||
public IEnumerable<TPropertyTypeContainer> Containers { get; set; } = Array.Empty<TPropertyTypeContainer>();
|
||||
|
||||
public IEnumerable<ContentTypeSort> AllowedContentTypes { get; set; } = Array.Empty<ContentTypeSort>();
|
||||
|
||||
public IEnumerable<ContentTypeComposition> Compositions { get; set; } = Array.Empty<ContentTypeComposition>();
|
||||
|
||||
public ContentTypeCleanup Cleanup { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class PropertyTypeAppearance
|
||||
{
|
||||
public bool LabelOnTop { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public abstract class PropertyTypeContainerViewModelBase
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
public Guid? ParentKey { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
// NOTE: This needs to be a string because it can be anything in the future (= not necessarily limited to "tab" or "group")
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public class PropertyTypeValidation
|
||||
{
|
||||
public bool Mandatory { get; set; }
|
||||
|
||||
public string? MandatoryMessage { get; set; }
|
||||
|
||||
public string? RegEx { get; set; }
|
||||
|
||||
public string? RegExMessage { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
public abstract class PropertyTypeViewModelBase
|
||||
{
|
||||
public Guid Key { get; set; }
|
||||
|
||||
public Guid? ContainerKey { get; set; }
|
||||
|
||||
public string Alias { get; set; } = string.Empty;
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public Guid DataTypeKey { get; set; }
|
||||
|
||||
public bool VariesByCulture { get; set; }
|
||||
|
||||
public bool VariesBySegment { get; set; }
|
||||
|
||||
public PropertyTypeValidation Validation { get; set; } = new();
|
||||
|
||||
public PropertyTypeAppearance Appearance { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public class DocumentValueViewModel : ValueViewModelBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public class DocumentVariantViewModel : VariantViewModelBase
|
||||
{
|
||||
public ContentState State { get; set; }
|
||||
|
||||
public DateTime? PublishDate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Content;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
|
||||
|
||||
public class DocumentViewModel : ContentViewModelBase<DocumentValueViewModel, DocumentVariantViewModel>
|
||||
{
|
||||
public IEnumerable<ContentUrlInfo> Urls { get; set; } = Array.Empty<ContentUrlInfo>();
|
||||
|
||||
public Guid? TemplateKey { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
|
||||
public class DocumentTypePropertyTypeContainerViewModel : PropertyTypeContainerViewModelBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
|
||||
public class DocumentTypePropertyTypeViewModel : PropertyTypeViewModelBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType;
|
||||
|
||||
public class DocumentTypeViewModel : ContentTypeViewModelBase<DocumentTypePropertyTypeViewModel, DocumentTypePropertyTypeContainerViewModel>
|
||||
{
|
||||
public IEnumerable<Guid> AllowedTemplateKeys { get; set; } = Array.Empty<Guid>();
|
||||
|
||||
public Guid? DefaultTemplateKey { get; set; }
|
||||
}
|
||||
@@ -56,6 +56,13 @@
|
||||
<Right>lib/net7.0/Umbraco.Core.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Core.Models.ContentTypeSort.#ctor(System.Lazy{System.Int32},System.Int32,System.String)</Target>
|
||||
<Left>lib/net7.0/Umbraco.Core.dll</Left>
|
||||
<Right>lib/net7.0/Umbraco.Core.dll</Right>
|
||||
<IsBaselineSuppression>true</IsBaselineSuppression>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:Umbraco.Cms.Core.Models.DataType.get_Configuration</Target>
|
||||
|
||||
@@ -21,16 +21,19 @@ public class ContentTypeSort : IValueObject, IDeepCloneable
|
||||
SortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public ContentTypeSort(Lazy<int> id, int sortOrder, string alias)
|
||||
// FIXME: remove integer ID in constructor
|
||||
public ContentTypeSort(Lazy<int> id, Guid key, int sortOrder, string alias)
|
||||
{
|
||||
Id = id;
|
||||
SortOrder = sortOrder;
|
||||
Alias = alias;
|
||||
Key = key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Id of the ContentType
|
||||
/// </summary>
|
||||
// FIXME: remove this in favor of Key (Id should only be used at repository level)
|
||||
public Lazy<int> Id { get; set; } = new(() => 0);
|
||||
|
||||
/// <summary>
|
||||
@@ -43,6 +46,11 @@ public class ContentTypeSort : IValueObject, IDeepCloneable
|
||||
/// </summary>
|
||||
public string Alias { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unique Key of the ContentType
|
||||
/// </summary>
|
||||
public Guid Key { get; set; }
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeSort)MemberwiseClone();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
@@ -10,13 +12,7 @@ namespace Umbraco.Extensions;
|
||||
|
||||
public static class UrlProviderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the URLs of the content item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Use when displaying URLs. If errors occur when generating the URLs, they will show in the list.</para>
|
||||
/// <para>Contains all the URLs that we can figure out (based upon domains, etc).</para>
|
||||
/// </remarks>
|
||||
[Obsolete("Use GetContentUrlsAsync that takes ILanguageService instead of ILocalizationService. Will be removed in V15.")]
|
||||
public static async Task<IEnumerable<UrlInfo>> GetContentUrlsAsync(
|
||||
this IContent content,
|
||||
IPublishedRouter publishedRouter,
|
||||
@@ -28,11 +24,40 @@ public static class UrlProviderExtensions
|
||||
ILogger<IContent> logger,
|
||||
UriUtility uriUtility,
|
||||
IPublishedUrlProvider publishedUrlProvider)
|
||||
=> await content.GetContentUrlsAsync(
|
||||
publishedRouter,
|
||||
umbracoContext,
|
||||
StaticServiceProvider.Instance.GetRequiredService<ILanguageService>(),
|
||||
textService,
|
||||
contentService,
|
||||
variationContextAccessor,
|
||||
logger,
|
||||
uriUtility,
|
||||
publishedUrlProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URLs of the content item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Use when displaying URLs. If errors occur when generating the URLs, they will show in the list.</para>
|
||||
/// <para>Contains all the URLs that we can figure out (based upon domains, etc).</para>
|
||||
/// </remarks>
|
||||
public static async Task<IEnumerable<UrlInfo>> GetContentUrlsAsync(
|
||||
this IContent content,
|
||||
IPublishedRouter publishedRouter,
|
||||
IUmbracoContext umbracoContext,
|
||||
ILanguageService languageService,
|
||||
ILocalizedTextService textService,
|
||||
IContentService contentService,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
ILogger<IContent> logger,
|
||||
UriUtility uriUtility,
|
||||
IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(content);
|
||||
ArgumentNullException.ThrowIfNull(publishedRouter);
|
||||
ArgumentNullException.ThrowIfNull(umbracoContext);
|
||||
ArgumentNullException.ThrowIfNull(localizationService);
|
||||
ArgumentNullException.ThrowIfNull(languageService);
|
||||
ArgumentNullException.ThrowIfNull(textService);
|
||||
ArgumentNullException.ThrowIfNull(contentService);
|
||||
ArgumentNullException.ThrowIfNull(variationContextAccessor);
|
||||
@@ -60,7 +85,7 @@ public static class UrlProviderExtensions
|
||||
// and, not only for those assigned to domains in the branch, because we want
|
||||
// to show what GetUrl() would return, for every culture.
|
||||
var urls = new HashSet<UrlInfo>();
|
||||
var cultures = localizationService.GetAllLanguages().Select(x => x.IsoCode).ToList();
|
||||
var cultures = (await languageService.GetAllAsync()).Select(x => x.IsoCode).ToList();
|
||||
|
||||
// get all URLs for all cultures
|
||||
// in a HashSet, so de-duplicates too
|
||||
|
||||
@@ -117,7 +117,7 @@ public static partial class UmbracoBuilderExtensions
|
||||
|
||||
builder.Services.AddScoped<IHttpScopeReference, HttpScopeReference>();
|
||||
|
||||
builder.Services.AddSingleton<IJsonSerializer, JsonNetSerializer>();
|
||||
builder.Services.AddSingleton<IJsonSerializer, ContextualJsonSerializer>();
|
||||
builder.Services.AddSingleton<IConfigurationEditorJsonSerializer, ContextualConfigurationEditorJsonSerializer>();
|
||||
builder.Services.AddSingleton<IMenuItemCollectionFactory, MenuItemCollectionFactory>();
|
||||
|
||||
|
||||
@@ -129,6 +129,7 @@ public class EntityMapDefinition : IMapDefinition
|
||||
private static void Map(EntityBasic source, ContentTypeSort target, MapperContext context)
|
||||
{
|
||||
target.Alias = source.Alias;
|
||||
target.Key = source.Key;
|
||||
target.Id = new Lazy<int>(() => Convert.ToInt32(source.Id));
|
||||
}
|
||||
|
||||
|
||||
@@ -1176,8 +1176,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging
|
||||
continue;
|
||||
}
|
||||
|
||||
allowedChildren?.Add(new ContentTypeSort(new Lazy<int>(() => allowedChild.Id), sortOrder,
|
||||
allowedChild.Alias));
|
||||
allowedChildren?.Add(new ContentTypeSort(new Lazy<int>(() => allowedChild.Id), allowedChild.Key, sortOrder, allowedChild.Alias));
|
||||
sortOrder++;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ internal class ContentTypeCommonRepository : IContentTypeCommonRepository
|
||||
// prepare
|
||||
// note: same alias could be used for media, content... but always different ids = ok
|
||||
var aliases = contentTypeDtos.ToDictionary(x => x.NodeId, x => x.Alias);
|
||||
var keys = contentTypeDtos.ToDictionary(x => x.NodeId, x => x.NodeDto.UniqueId);
|
||||
|
||||
// create
|
||||
var allowedDtoIx = 0;
|
||||
@@ -120,14 +121,17 @@ internal class ContentTypeCommonRepository : IContentTypeCommonRepository
|
||||
while (allowedDtoIx < allowedDtos?.Count && allowedDtos[allowedDtoIx].Id == contentTypeDto.NodeId)
|
||||
{
|
||||
ContentTypeAllowedContentTypeDto allowedDto = allowedDtos[allowedDtoIx];
|
||||
if (!aliases.TryGetValue(allowedDto.AllowedId, out var alias))
|
||||
if (!aliases.TryGetValue(allowedDto.AllowedId, out var alias)
|
||||
|| !keys.TryGetValue(allowedDto.AllowedId, out Guid key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
allowedContentTypes.Add(new ContentTypeSort(
|
||||
new Lazy<int>(() => allowedDto.AllowedId),
|
||||
allowedDto.SortOrder, alias!));
|
||||
key,
|
||||
allowedDto.SortOrder,
|
||||
alias!));
|
||||
allowedDtoIx++;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
// FIXME: move away from Json.NET; this is a temporary fix that attempts to use System.Text.Json for management API operations, Json.NET for other operations
|
||||
public class ContextualJsonSerializer : IJsonSerializer
|
||||
{
|
||||
private readonly IRequestAccessor _requestAccessor;
|
||||
private readonly IJsonSerializer _jsonNetSerializer;
|
||||
private readonly IJsonSerializer _systemTextSerializer;
|
||||
|
||||
public ContextualJsonSerializer(IRequestAccessor requestAccessor)
|
||||
{
|
||||
_requestAccessor = requestAccessor;
|
||||
_jsonNetSerializer = new JsonNetSerializer();
|
||||
_systemTextSerializer = new SystemTextJsonSerializer();
|
||||
}
|
||||
|
||||
public string Serialize(object? input) => ContextualizedSerializer().Serialize(input);
|
||||
|
||||
public T? Deserialize<T>(string input) => ContextualizedSerializer().Deserialize<T>(input);
|
||||
|
||||
public T? DeserializeSubset<T>(string input, string key) => throw new NotSupportedException();
|
||||
|
||||
private IJsonSerializer ContextualizedSerializer()
|
||||
{
|
||||
try
|
||||
{
|
||||
var requestedPath = _requestAccessor.GetRequestUrl()?.AbsolutePath;
|
||||
if (requestedPath != null)
|
||||
{
|
||||
// add white listed paths for the System.Text.Json config serializer here
|
||||
// - always use it for the new management API
|
||||
if (requestedPath.Contains("/umbraco/management/api/"))
|
||||
{
|
||||
return _systemTextSerializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// ignore - this whole thing is a temporary workaround, let's not make a fuss
|
||||
}
|
||||
|
||||
return _jsonNetSerializer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
// TODO: clean up all config editor serializers when we can migrate fully to System.Text.Json
|
||||
// FIXME: clean up all config editor serializers when we can migrate fully to System.Text.Json
|
||||
// - move this implementation to ConfigurationEditorJsonSerializer (delete the old implementation)
|
||||
// - use this implementation as the registered singleton (delete ContextualConfigurationEditorJsonSerializer)
|
||||
// - reuse the JsonObjectConverter implementation from management API (delete the local implementation - pending V12 branch update)
|
||||
@@ -21,9 +21,9 @@ public class SystemTextConfigurationEditorJsonSerializer : IConfigurationEditorJ
|
||||
// in some cases, configs aren't camel cased in the DB, so we have to resort to case insensitive
|
||||
// property name resolving when creating configuration objects (deserializing DB configs)
|
||||
PropertyNameCaseInsensitive = true,
|
||||
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString
|
||||
};
|
||||
_jsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
|
||||
_jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
_jsonSerializerOptions.Converters.Add(new JsonObjectConverter());
|
||||
}
|
||||
|
||||
@@ -32,73 +32,4 @@ public class SystemTextConfigurationEditorJsonSerializer : IConfigurationEditorJ
|
||||
public T? Deserialize<T>(string input) => JsonSerializer.Deserialize<T>(input, _jsonSerializerOptions);
|
||||
|
||||
public T? DeserializeSubset<T>(string input, string key) => throw new NotSupportedException();
|
||||
|
||||
// TODO: reuse the JsonObjectConverter implementation from management API
|
||||
private class JsonObjectConverter : System.Text.Json.Serialization.JsonConverter<object>
|
||||
{
|
||||
public override object Read(
|
||||
ref Utf8JsonReader reader,
|
||||
Type typeToConvert,
|
||||
JsonSerializerOptions options) =>
|
||||
ParseObject(ref reader);
|
||||
|
||||
public override void Write(
|
||||
Utf8JsonWriter writer,
|
||||
object objectToWrite,
|
||||
JsonSerializerOptions options)
|
||||
{
|
||||
if (objectToWrite is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If an object is equals "new object()", Json.Serialize would recurse forever and cause a stack overflow
|
||||
// We have no good way of checking if its an empty object
|
||||
// which is why we try to check if the object has any properties, and thus will be empty.
|
||||
if (objectToWrite.GetType().Name is "Object" && !objectToWrite.GetType().GetProperties().Any())
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonSerializer.Serialize(writer, objectToWrite, objectToWrite.GetType(), options);
|
||||
}
|
||||
}
|
||||
|
||||
private object ParseObject(ref Utf8JsonReader reader)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.StartArray)
|
||||
{
|
||||
var items = new List<object>();
|
||||
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
||||
{
|
||||
items.Add(ParseObject(ref reader));
|
||||
}
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
|
||||
if (reader.TokenType == JsonTokenType.StartObject)
|
||||
{
|
||||
var jsonNode = JsonNode.Parse(ref reader);
|
||||
if (jsonNode is JsonObject jsonObject)
|
||||
{
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
||||
|
||||
return reader.TokenType switch
|
||||
{
|
||||
JsonTokenType.True => true,
|
||||
JsonTokenType.False => false,
|
||||
JsonTokenType.Number when reader.TryGetInt32(out int i) => i,
|
||||
JsonTokenType.Number when reader.TryGetInt64(out long l) => l,
|
||||
JsonTokenType.Number => reader.GetDouble(),
|
||||
JsonTokenType.String when reader.TryGetDateTime(out DateTime datetime) => datetime,
|
||||
JsonTokenType.String => reader.GetString()!,
|
||||
_ => JsonDocument.ParseValue(ref reader).RootElement.Clone()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
public class SystemTextJsonSerializer : IJsonSerializer
|
||||
{
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
|
||||
public SystemTextJsonSerializer()
|
||||
{
|
||||
_jsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
||||
_jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
// we may need to add JsonObjectConverter at some point, but for the time being things work fine without
|
||||
// _jsonSerializerOptions.Converters.Add(new JsonObjectConverter());
|
||||
}
|
||||
|
||||
public string Serialize(object? input) => JsonSerializer.Serialize(input, _jsonSerializerOptions);
|
||||
|
||||
public T? Deserialize<T>(string input) => JsonSerializer.Deserialize<T>(input, _jsonSerializerOptions);
|
||||
|
||||
public T? DeserializeSubset<T>(string input, string key) => throw new NotSupportedException();
|
||||
}
|
||||
@@ -217,7 +217,7 @@ public class LoadTestController : Controller
|
||||
};
|
||||
containerType.AllowedContentTypes = containerType.AllowedContentTypes.Union(new[]
|
||||
{
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType.Id), 0, contentType.Alias)
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType.Id), contentType.Key, 0, contentType.Alias)
|
||||
});
|
||||
containerType.AllowedTemplates = containerType.AllowedTemplates.Union(new[] { containerTemplate });
|
||||
containerType.SetDefaultTemplate(containerTemplate);
|
||||
|
||||
@@ -51,7 +51,8 @@ public class ContentTypeSortBuilder
|
||||
var id = _id ?? 1;
|
||||
var alias = _alias ?? Guid.NewGuid().ToString().ToCamelCase();
|
||||
var sortOrder = _sortOrder ?? 0;
|
||||
var key = Guid.NewGuid();
|
||||
|
||||
return new ContentTypeSort(new Lazy<int>(() => id), sortOrder, alias);
|
||||
return new ContentTypeSort(new Lazy<int>(() => id), key, sortOrder, alias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -809,8 +809,8 @@ public class ContentTypeRepositoryTest : UmbracoIntegrationTest
|
||||
var contentType = repository.Get(_simpleContentType.Id);
|
||||
contentType.AllowedContentTypes = new List<ContentTypeSort>
|
||||
{
|
||||
new(new Lazy<int>(() => subpageContentType.Id), 0, subpageContentType.Alias),
|
||||
new(new Lazy<int>(() => simpleSubpageContentType.Id), 1, simpleSubpageContentType.Alias)
|
||||
new(new Lazy<int>(() => subpageContentType.Id), subpageContentType.Key, 0, subpageContentType.Alias),
|
||||
new(new Lazy<int>(() => simpleSubpageContentType.Id), simpleSubpageContentType.Key, 1, simpleSubpageContentType.Alias)
|
||||
};
|
||||
repository.Save(contentType);
|
||||
|
||||
|
||||
@@ -70,18 +70,18 @@ public class ContentServicePerformanceTest : UmbracoIntegrationTest
|
||||
ContentTypeService.Save(new[] { contentType1, contentType2, contentType3 });
|
||||
contentType1.AllowedContentTypes = new[]
|
||||
{
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType2.Id), 0, contentType2.Alias),
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType3.Id), 1, contentType3.Alias)
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType2.Id), contentType2.Key, 0, contentType2.Alias),
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType3.Id), contentType3.Key, 1, contentType3.Alias)
|
||||
};
|
||||
contentType2.AllowedContentTypes = new[]
|
||||
{
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType1.Id), 0, contentType1.Alias),
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType3.Id), 1, contentType3.Alias)
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType1.Id), contentType1.Key, 0, contentType1.Alias),
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType3.Id), contentType3.Key, 1, contentType3.Alias)
|
||||
};
|
||||
contentType3.AllowedContentTypes = new[]
|
||||
{
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType1.Id), 0, contentType1.Alias),
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType2.Id), 1, contentType2.Alias)
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType1.Id), contentType1.Key, 0, contentType1.Alias),
|
||||
new ContentTypeSort(new Lazy<int>(() => contentType2.Id), contentType2.Key, 1, contentType2.Alias)
|
||||
};
|
||||
ContentTypeService.Save(new[] { contentType1, contentType2, contentType3 });
|
||||
|
||||
|
||||
@@ -652,7 +652,7 @@ public class ContentServiceTagsTests : UmbracoIntegrationTest
|
||||
CreateAndAddTagsPropertyType(contentType);
|
||||
ContentTypeService.Save(contentType);
|
||||
contentType.AllowedContentTypes =
|
||||
new[] { new ContentTypeSort(new Lazy<int>(() => contentType.Id), 0, contentType.Alias) };
|
||||
new[] { new ContentTypeSort(new Lazy<int>(() => contentType.Id), contentType.Key, 0, contentType.Alias) };
|
||||
|
||||
var content = ContentBuilder.CreateSimpleContent(contentType, "Tagged content");
|
||||
content.AssignTags(PropertyEditorCollection, DataTypeService, Serializer, "tags",
|
||||
|
||||
@@ -1843,7 +1843,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
|
||||
ContentTypeBuilder.CreateSimpleContentType("umbTextpage1", "Textpage", defaultTemplateId: template.Id);
|
||||
contentType.AllowedContentTypes = new List<ContentTypeSort>
|
||||
{
|
||||
new(new Lazy<int>(() => contentType.Id), 0, contentType.Alias)
|
||||
new(new Lazy<int>(() => contentType.Id), contentType.Key, 0, contentType.Alias)
|
||||
};
|
||||
ContentTypeService.Save(contentType);
|
||||
|
||||
@@ -1883,7 +1883,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
|
||||
ContentTypeBuilder.CreateSimpleContentType("umbTextpage1", "Textpage", defaultTemplateId: template.Id);
|
||||
contentType.AllowedContentTypes = new List<ContentTypeSort>
|
||||
{
|
||||
new(new Lazy<int>(() => contentType.Id), 0, contentType.Alias)
|
||||
new(new Lazy<int>(() => contentType.Id), contentType.Key, 0, contentType.Alias)
|
||||
};
|
||||
ContentTypeService.Save(contentType);
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ public class DomainAndUrlsTests : UmbracoIntegrationTest
|
||||
root.GetContentUrlsAsync(
|
||||
GetRequiredService<IPublishedRouter>(),
|
||||
GetRequiredService<IUmbracoContextAccessor>().GetRequiredUmbracoContext(),
|
||||
GetRequiredService<ILocalizationService>(),
|
||||
GetRequiredService<ILanguageService>(),
|
||||
GetRequiredService<ILocalizedTextService>(),
|
||||
ContentService,
|
||||
GetRequiredService<IVariationContextAccessor>(),
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -58,16 +55,16 @@ public class GetContentUrlsTests : PublishedSnapshotServiceTestBase
|
||||
return textService.Object;
|
||||
}
|
||||
|
||||
private ILocalizationService GetLangService(params string[] isoCodes)
|
||||
private ILanguageService GetLangService(params string[] isoCodes)
|
||||
{
|
||||
var allLangs = isoCodes
|
||||
.Select(CultureInfo.GetCultureInfo)
|
||||
.Select(culture => new Language(culture.Name, culture.EnglishName) { IsDefault = true, IsMandatory = true })
|
||||
.ToArray();
|
||||
|
||||
var langServiceMock = new Mock<ILocalizationService>();
|
||||
langServiceMock.Setup(x => x.GetAllLanguages()).Returns(allLangs);
|
||||
langServiceMock.Setup(x => x.GetDefaultLanguageIsoCode()).Returns(allLangs.First(x => x.IsDefault).IsoCode);
|
||||
var langServiceMock = new Mock<ILanguageService>();
|
||||
langServiceMock.Setup(x => x.GetAllAsync()).ReturnsAsync(allLangs);
|
||||
langServiceMock.Setup(x => x.GetDefaultIsoCodeAsync()).ReturnsAsync(allLangs.First(x => x.IsDefault).IsoCode);
|
||||
|
||||
return langServiceMock.Object;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user