using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.BackOffice.Controllers;
internal class BlockGridSampleHelper
{
private const string ContainerName = "Umbraco Block Grid Demo";
private readonly IContentTypeService _contentTypeService;
private readonly IDataTypeService _dataTypeService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
public BlockGridSampleHelper(IContentTypeService contentTypeService, IDataTypeService dataTypeService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_contentTypeService = contentTypeService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_dataTypeService = dataTypeService;
}
///
/// Creates block grid elements for sample purposes:
/// - a "Headline" block with a text box
/// - an "Image" block with a single value media picker
/// - a "Rich Text" block with an RTE
/// - an empty "Two Column Layout" (will be used for layouting nested blocks)
///
/// The function that will perform the actual element creation
/// If an error occurs, this message will describe that error
/// A mapping table between element aliases and the created element UDIs, or null if an error occurs
public Dictionary? CreateSampleElements(Func> createElement, out string errorMessage)
{
errorMessage = string.Empty;
EntityContainer? container = GetOrCreateContainer();
if (container == null)
{
errorMessage = $"Unable to get or create content type container: {ContainerName}";
return null;
}
FindDataTypes(out IDataType? textBox, out IDataType? tinyMce, out IDataType? mediaPicker);
if (textBox == null || tinyMce == null || mediaPicker == null)
{
errorMessage = $"Could not find required data types - must have a text box, a rich text editor and a media picker (configured in single picker mode)";
return null;
}
// get any already created elements
IContentType[] existingContentTypes = _contentTypeService.GetChildren(container.Id).ToArray();
// this is the return value (will be populated below)
var elementUdisByAlias = new Dictionary();
// these describe the block grid elements we want to create
var elementDescriptors = new[]
{
new
{
Alias = "umbBlockGridDemoHeadlineBlock",
Icon = "icon-font color-black",
Name = "Headline",
Property = new { Alias = "headline", Label = "Headline", EditorId = textBox.Id }
},
new
{
Alias = "umbBlockGridDemoImageBlock",
Icon = "icon-umb-media color-black",
Name = "Image",
Property = new { Alias = "image", Label = "Image", EditorId = mediaPicker.Id }
},
new
{
Alias = "umbBlockGridDemoRichTextBlock",
Icon = "icon-script color-black",
Name = "Rich Text",
Property = new { Alias = "richText", Label = "Text", EditorId = tinyMce.Id }
},
new
{
Alias = "umbBlockGridDemoTwoColumnLayoutBlock",
Icon = "icon-book-alt color-black",
Name = "Two Column Layout",
Property = new { Alias = string.Empty, Label = string.Empty, EditorId = -1 }
}
};
foreach (var elementDescriptor in elementDescriptors)
{
IContentType? contentType = existingContentTypes.FirstOrDefault(c => c.Alias == elementDescriptor.Alias);
if (contentType != null)
{
elementUdisByAlias[elementDescriptor.Alias] = contentType.GetUdi();
continue;
}
var documentTypeSave = new DocumentTypeSave
{
Alias = elementDescriptor.Alias,
Icon = elementDescriptor.Icon,
Name = elementDescriptor.Name,
IsElement = true,
Groups = new[]
{
new PropertyGroupBasic
{
Alias = "content",
Name = "Content",
Type = PropertyGroupType.Group,
Properties = elementDescriptor.Property.Alias.IsNullOrWhiteSpace()
? Array.Empty()
: new[]
{
new PropertyTypeBasic
{
Alias = elementDescriptor.Property.Alias,
Label = elementDescriptor.Property.Label,
Validation = new PropertyTypeValidation { Mandatory = true },
DataTypeId = elementDescriptor.Property.EditorId
}
}
}
},
ParentId = container.Id,
Thumbnail = "folder.png"
};
ActionResult result = createElement(documentTypeSave);
if (result.Value != null)
{
elementUdisByAlias[elementDescriptor.Alias] = result.Value.GetUdi();
continue;
}
if (result.Result is ValidationErrorResult validationErrorResult)
{
errorMessage = validationErrorResult.Value switch
{
string error => error,
Exception exception => exception.Message,
_ => string.Empty
};
}
if(errorMessage.IsNullOrWhiteSpace())
{
errorMessage = $"Could not create element type: {elementDescriptor.Name}";
}
return null;
}
return elementUdisByAlias;
}
private EntityContainer? GetOrCreateContainer()
{
var userId = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1;
EntityContainer? container = _contentTypeService.GetContainers(ContainerName, 1).FirstOrDefault();
if (container == null)
{
Attempt?> attempt =
_contentTypeService.CreateContainer(Constants.System.Root, Guid.NewGuid(), ContainerName, userId);
container = attempt.Result?.Entity;
}
return container;
}
private void FindDataTypes(out IDataType? textBox, out IDataType? tinyMce, out IDataType? mediaPicker)
{
// order by ID to prioritize default installed data types
IDataType[] dataTypes = _dataTypeService.GetAll().OrderBy(d => d.Id).ToArray();
textBox = dataTypes.FirstOrDefault(d => d.EditorAlias == Constants.PropertyEditors.Aliases.TextBox);
tinyMce = dataTypes.FirstOrDefault(d => d.EditorAlias == Constants.PropertyEditors.Aliases.TinyMce);
mediaPicker = dataTypes.Where(d =>
d.EditorAlias == Constants.PropertyEditors.Aliases.MediaPicker3
&& d.ConfigurationAs()?.Multiple == false)
// prioritize the default "Image Media Picker" if it exists
.MinBy(d => d.Name == "Image Media Picker" ? 0 : 1);
}
}