I have been able to get it "somewhat" working, but still have no clue how to resolve this now.
I have copied-pasted what I could find from the Umbraco source code, adjusting to my needs, and this is what I came up with.
namespace Umbraco.Plugins.Connector.PropertyEditors
{
using System.Collections.Generic;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
public class TenantPreferencesDataType : DataType
{
public static string NAME = "Tenant Preferences DataType";
public TenantPreferencesDataType(IDataEditor editor, int parentId = -1) : base(editor, parentId)
{
base.Name = NAME;
base.DatabaseType = ValueStorageType.Ntext;
}
}
/// <summary>
/// Represents a property editor for Tenant Preferences.
/// </summary>
[DataEditor(alias: "tenantPreferences", type: EditorType.PropertyValue, view: "App_Plugins\\TenantPreferences\\edit.html", name: "Tenant Preferences", Group = "Total Code", HideLabel = true, Icon = "icon-readonly", IsDeprecated = false)]
public class TenantPreferencesPropertyEditor : DataEditor
{
/// <summary>
/// Initializes a new instance of the <see cref="TenantPreferencesPropertyEditor"/> class.
/// </summary>
public TenantPreferencesPropertyEditor(ILogger logger)
: base(logger)
{ }
/// <inheritdoc />
protected override IDataValueEditor CreateValueEditor() => new TenantPreferencesValueEditor(Attribute);
/// <inheritdoc />
protected override IConfigurationEditor CreateConfigurationEditor() => new LabelConfigurationEditor();
// provides the property value editor
internal class TenantPreferencesValueEditor : DataValueEditor
{
public TenantPreferencesValueEditor(DataEditorAttribute attribute)
: base(attribute)
{ }
/// <inheritdoc />
public override bool IsReadOnly => true;
}
}
/// <summary>
/// Represents the configuration for the label value editor.
/// </summary>
public class TenantPreferencesConfigurationEditor : ConfigurationEditor<TenantPreferencesConfiguration>
{
/// <inheritdoc />
public override TenantPreferencesConfiguration FromConfigurationEditor(IDictionary<string, object> editorValues, TenantPreferencesConfiguration configuration)
{
var newConfiguration = new TenantPreferencesConfiguration();
// get the value type
// not simply deserializing Json because we want to validate the valueType
if (editorValues.TryGetValue(Constants.PropertyEditors.ConfigurationKeys.DataValueType, out var valueTypeObj)
&& valueTypeObj is string stringValue)
{
if (!string.IsNullOrWhiteSpace(stringValue) && ValueTypes.IsValue(stringValue)) // validate
newConfiguration.ValueType = stringValue;
}
return newConfiguration;
}
}
public class TenantPreferencesConfiguration : IConfigureValueType
{
[ConfigurationField(Constants.PropertyEditors.ConfigurationKeys.DataValueType, "Value type", "valuetype")]
public string ValueType { get; set; } = ValueTypes.Json;
}
}
and attempted to create the datatype and added it to a document type
try
{
var contentType = contentTypeService.Get(DOCUMENT_TYPE_ALIAS);
if (contentType != null)
{
var exists = dataTypeService.GetDataType(TenantPreferencesDataType.NAME) != null;
if (!exists)
{
DataType tenantPreferencesDataType = new TenantPreferencesDataType(new TenantPreferencesPropertyEditor(logger));
dataTypeService.Save(tenantPreferencesDataType);
#region Tenant Info Tab
PropertyType tenantPreferencesPropType = new PropertyType(tenantPreferencesDataType, "tenantPreferences")
{
Name = "Tenant Preferences",
Description = "Tenant Preferences for Customers",
Variations = ContentVariation.Nothing,
PropertyEditorAlias = "tenantPreferences"
};
contentType.AddPropertyType(tenantPreferencesPropType, TENANT_TAB);
#endregion
contentTypeService.Save(contentType);
ConnectorContext.AuditService.Add(AuditType.New, -1, contentType.Id, "Document Type", $"Document Type {DOCUMENT_TYPE_NAME} has been updated");
}
}
}
catch (System.Exception ex)
{
logger.Error(typeof(HomeDocumentTypePhase2), ex.Message);
logger.Error(typeof(HomeDocumentTypePhase2), ex.StackTrace);
}
This is the error:
Received an error from the server
An error occured
Error mapping types.
Mapping types:
DataTypeSave -> IDataType
Umbraco.Web.Models.ContentEditing.DataTypeSave -> Umbraco.Core.Models.IDataType
Type Map configuration:
DataTypeSave -> IDataType
Umbraco.Web.Models.ContentEditing.DataTypeSave -> Umbraco.Core.Models.IDataType
Destination Member:
Editor
Exception Details
AutoMapper.AutoMapperMappingException: Error mapping types.
Mapping types:
DataTypeSave -> IDataType
Umbraco.Web.Models.ContentEditing.DataTypeSave -> Umbraco.Core.Models.IDataType
Type Map configuration:
DataTypeSave -> IDataType
Umbraco.Web.Models.ContentEditing.DataTypeSave -> Umbraco.Core.Models.IDataType
Destination Member:
Editor
Stacktrace
at lambda_method(Closure , DataTypeSave , IDataType , ResolutionContext )
at Umbraco.Web.Editors.DataTypeValidateAttribute.OnActionExecuting(HttpActionContext actionContext)
at System.Web.Http.Filters.ActionFilterAttribute.OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()
Inner Exception
System.InvalidOperationException: Sequence contains more than one matching element
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at Umbraco.Core.PropertyEditors.PropertyEditorCollection.get_Item(String alias)
at lambda_method(Closure , DataTypeSave , IDataType , ResolutionContext )
A DataType is based on a property editor and can be created from the backoffice. I don't really get what you are trying to achieve here. Do you want to create DataTyes programatically?
Previously, in version 7, it was possible to create property Editors without creating a manifest file. The Property Editor was programmatic
and could be even be loaded from the Assembly, as Embedded Resources.
But it seems that things have changed in version 8, as I have not been able to find any documentation pointing or even suggesting a similar approach.
I have developed the habit of creating Data Types in C#, but now that's completely obsolete!
here is an example of the code I use in v7, and works great, creating Data Types from Assembly
#region Data Types
/// <summary>
/// Creates new Custom Data Types from Reflection
/// </summary>
/// <returns>String Results from Creation</returns>
public virtual string CustomDataTypes()
{
List<Type> types = _assembly.GetInherited<ICustomDataType>();
if (types?.Count > 0)
{
int containerId = -1;
foreach (var type in types)
{
var dataService = _applicationContext.Services.DataTypeService;
var propertyEditor = type.GetProperty("PropertyEditor").Value<string>();
var name = type.GetProperty("Name").Value<string>();
var exists = dataService.GetDataTypeDefinitionByName(name) != null;
if (!exists)
{
var properties = type.GetProperties();
var alias = type.GetProperty("Alias").Value<string>();
var propertiEditor = type.GetProperty("PropertyEditor").Value<string>();
var containerName = type.GetProperty("Container").Value<string>();
var container = _applicationContext.Services.DataTypeService.GetContainers(containerName, 1).FirstOrDefault();
if (container == null)
{
var dataTypeContainer = _applicationContext.Services.DataTypeService.CreateContainer(containerId, containerName);
if (dataTypeContainer.Success)
containerId = dataTypeContainer.Result.Entity.Id;
}
else
{
containerId = container.Id;
}
DataTypeDefinition dataType = null;
if (!exists)
dataType = new DataTypeDefinition(containerId, alias);
else
dataType = dataService.GetDataTypeDefinitionByPropertyEditorAlias(alias).First() as DataTypeDefinition;
dataType.Name = name;
dataType.PropertyEditorAlias = propertiEditor;
Dictionary<string, object> prevalues = null;
if (type.GetProperty("PreValues").HasValue()) prevalues = type.GetProperty("PreValues").ValueCollection<string, object>();
if (prevalues != null)
{
var pDic = new Dictionary<string, PreValue>();
foreach (var p in prevalues) pDic.Add(p.Key, new PreValue(p.Value.ToString()));
dataService.SaveDataTypeAndPreValues(dataType, pDic);
}
else
{
dataService.Save(dataType);
}
}
}
return "Custom Types Created";
}
return "No Custom Types to be Created";
}
For version 8 I have found a workaround (though not what I want) which is to copy the physical assets into the App_Plugins folder, and then creating the DataType.
All the code I could find in the Umbraco.Web source points to the system default data types. I could not find anything to help creating custom data types. I hope this topic gets documented soon, so I can "fix" my little hack.
Here's the workaround for version 8
private void UpdateHomeDocumentType()
{
const string editorAlias = "userPreferences";
const string propertyAlias = "usertPreferencesProperty";
const string dataTypeName = "User Preferences Properties";
try
{
var contentType = contentTypeService.Get(DOCUMENT_TYPE_ALIAS);
if (contentType != null)
{
var exists = dataTypeService.GetDataType(dataTypeName) != null;
if (!exists)
{
var created = Web.Composing.Current.PropertyEditors.TryGet(editorAlias, out IDataEditor editor);
if (created)
{
DataType tenantPreferencesDataType = new DataType(editor)
{
Name = dataTypeName
};
dataTypeService.Save(tenantPreferencesDataType);
#region Tenant Info Tab
PropertyType tenantPreferencesPropType = new PropertyType(tenantPreferencesDataType, propertyAlias)
{
Name = "Tenant Preferences",
Description = "Tenant Preferences for Customers"
};
contentType.AddPropertyType(tenantPreferencesPropType, TENANT_TAB);
#endregion
contentTypeService.Save(contentType);
ConnectorContext.AuditService.Add(AuditType.New, -1, contentType.Id, "Document Type", $"Document Type {DOCUMENT_TYPE_NAME} has been updated");
}
else
{
ContentHelper.CopyPhysicalAssets(new TenantPreferencesEmbeddedResources()); // copy property editor files before trying to create the data type
// will need to restart the app, in order to force a refresh to attempt to create the data type again
throw new DataTypeNotCreatedException("In order to create the Data Type, a Reload is required");
}
}
else
{
}
}
}
catch (System.Exception ex)
{
logger.Error(typeof(HomeDocumentTypePhase2), ex.Message);
logger.Error(typeof(HomeDocumentTypePhase2), ex.StackTrace);
}
}
Creating Custom Data Type
I am not finding any documentation on how to create custom data types.
Are custom data types not required anymore when creating Property Editors?
I've created PropertyEditors for v7, but now it seems everything changed.
Where's the documentation for that, pleeeeease?
I have been able to get it "somewhat" working, but still have no clue how to resolve this now.
I have copied-pasted what I could find from the Umbraco source code, adjusting to my needs, and this is what I came up with.
and attempted to create the datatype and added it to a document type
This is the error:
A DataType is based on a property editor and can be created from the backoffice. I don't really get what you are trying to achieve here. Do you want to create DataTyes programatically?
Yes, programatically.
Previously, in version 7, it was possible to create property Editors without creating a manifest file. The Property Editor was programmatic and could be even be loaded from the Assembly, as Embedded Resources.
See http://www.nibble.be/?p=415
But it seems that things have changed in version 8, as I have not been able to find any documentation pointing or even suggesting a similar approach.
I have developed the habit of creating Data Types in C#, but now that's completely obsolete!
here is an example of the code I use in v7, and works great, creating Data Types from Assembly
For version 8 I have found a workaround (though not what I want) which is to copy the physical assets into the App_Plugins folder, and then creating the DataType.
All the code I could find in the Umbraco.Web source points to the system default data types. I could not find anything to help creating custom data types. I hope this topic gets documented soon, so I can "fix" my little hack.
Here's the workaround for version 8
is working on a reply...
This forum is in read-only mode while we transition to the new forum.
You can continue this topic on the new forum by tapping the "Continue discussion" link below.