Merge remote-tracking branch 'origin/dev-v7-contenttypeeditor' into 7.4.0
Conflicts: src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js
This commit is contained in:
@@ -3,22 +3,41 @@
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
public class InvalidCompositionException : Exception
|
||||
{
|
||||
public string ContentTypeAlias { get; set; }
|
||||
{
|
||||
public InvalidCompositionException(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliass)
|
||||
{
|
||||
ContentTypeAlias = contentTypeAlias;
|
||||
AddedCompositionAlias = addedCompositionAlias;
|
||||
PropertyTypeAliases = propertyTypeAliass;
|
||||
}
|
||||
|
||||
public string AddedCompositionAlias { get; set; }
|
||||
public InvalidCompositionException(string contentTypeAlias, string[] propertyTypeAliass)
|
||||
{
|
||||
ContentTypeAlias = contentTypeAlias;
|
||||
PropertyTypeAliases = propertyTypeAliass;
|
||||
}
|
||||
|
||||
public string PropertyTypeAlias { get; set; }
|
||||
public string ContentTypeAlias { get; private set; }
|
||||
|
||||
public string AddedCompositionAlias { get; private set; }
|
||||
|
||||
public string[] PropertyTypeAliases { get; private set; }
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format(
|
||||
"InvalidCompositionException - ContentType with alias '{0}' was added as a Compsition to ContentType with alias '{1}', " +
|
||||
"but there was a conflict on the PropertyType alias '{2}'. " +
|
||||
return AddedCompositionAlias.IsNullOrWhiteSpace()
|
||||
? string.Format(
|
||||
"ContentType with alias '{0}' has an invalid composition " +
|
||||
"and there was a conflict on the following PropertyTypes: '{1}'. " +
|
||||
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
|
||||
AddedCompositionAlias, ContentTypeAlias, PropertyTypeAlias);
|
||||
ContentTypeAlias, string.Join(", ", PropertyTypeAliases))
|
||||
: string.Format(
|
||||
"ContentType with alias '{0}' was added as a Composition to ContentType with alias '{1}', " +
|
||||
"but there was a conflict on the following PropertyTypes: '{2}'. " +
|
||||
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
|
||||
AddedCompositionAlias, ContentTypeAlias, string.Join(", ", PropertyTypeAliases));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,13 +94,7 @@ namespace Umbraco.Core.Models
|
||||
.Select(p => p.Alias)).ToList();
|
||||
|
||||
if (conflictingPropertyTypeAliases.Any())
|
||||
throw new InvalidCompositionException
|
||||
{
|
||||
AddedCompositionAlias = contentType.Alias,
|
||||
ContentTypeAlias = Alias,
|
||||
PropertyTypeAlias =
|
||||
string.Join(", ", conflictingPropertyTypeAliases)
|
||||
};
|
||||
throw new InvalidCompositionException(Alias, contentType.Alias, conflictingPropertyTypeAliases.ToArray());
|
||||
|
||||
_contentTypeComposition.Add(contentType);
|
||||
OnPropertyChanged(ContentTypeCompositionSelector);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using AutoMapper;
|
||||
|
||||
namespace Umbraco.Core.Models.Mapping
|
||||
{
|
||||
internal static class MappingExpressionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Ignores all unmapped members by default - Use with caution!
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <typeparam name="TDest"></typeparam>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
public static IMappingExpression<TSource, TDest> IgnoreAllUnmapped<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
|
||||
{
|
||||
expression.ForAllMembers(opt => opt.Ignore());
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public PropertyType(IDataTypeDefinition dataTypeDefinition)
|
||||
{
|
||||
if (dataTypeDefinition == null) throw new ArgumentNullException("dataTypeDefinition");
|
||||
|
||||
if(dataTypeDefinition.HasIdentity)
|
||||
_dataTypeDefinitionId = dataTypeDefinition.Id;
|
||||
|
||||
|
||||
@@ -118,9 +118,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select(isCount ? "COUNT(*)" : "*")
|
||||
.From<DataTypeDto>()
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<DataTypeDto, NodeDto>(left => left.DataTypeId, right => right.NodeId)
|
||||
.From<DataTypeDto>(SqlSyntax)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<DataTypeDto, NodeDto>(SqlSyntax, left => left.DataTypeId, right => right.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
|
||||
return sql;
|
||||
}
|
||||
@@ -338,9 +338,9 @@ AND umbracoNode.id <> @id",
|
||||
{
|
||||
//first just get all pre-values for this data type so we can compare them to see if we need to insert or update or replace
|
||||
var sql = new Sql().Select("*")
|
||||
.From<DataTypePreValueDto>()
|
||||
.From<DataTypePreValueDto>(SqlSyntax)
|
||||
.Where<DataTypePreValueDto>(dto => dto.DataTypeNodeId == dataType.Id)
|
||||
.OrderBy<DataTypePreValueDto>(dto => dto.SortOrder);
|
||||
.OrderBy<DataTypePreValueDto>(dto => dto.SortOrder, SqlSyntax);
|
||||
currentVals = Database.Fetch<DataTypePreValueDto>(sql).ToArray();
|
||||
}
|
||||
|
||||
@@ -431,7 +431,7 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
.From<NodeDto>()
|
||||
.From<NodeDto>(SqlSyntax)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.Text.StartsWith(nodeName));
|
||||
|
||||
int uniqueNumber = 1;
|
||||
|
||||
@@ -21,6 +21,18 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
}
|
||||
|
||||
public BackOfficeUserManager(
|
||||
IUserStore<BackOfficeIdentityUser, int> store,
|
||||
IdentityFactoryOptions<BackOfficeUserManager> options,
|
||||
MembershipProviderBase membershipProvider)
|
||||
: base(store)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException("options");
|
||||
var manager = new BackOfficeUserManager(store);
|
||||
InitUserManager(manager, membershipProvider, options);
|
||||
}
|
||||
|
||||
#region Static Create methods
|
||||
/// <summary>
|
||||
/// Creates a BackOfficeUserManager instance with all default options and the default BackOfficeUserManager
|
||||
/// </summary>
|
||||
@@ -56,13 +68,10 @@ namespace Umbraco.Core.Security
|
||||
BackOfficeUserStore customUserStore,
|
||||
MembershipProviderBase membershipProvider)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException("options");
|
||||
if (customUserStore == null) throw new ArgumentNullException("customUserStore");
|
||||
|
||||
var manager = new BackOfficeUserManager(customUserStore);
|
||||
|
||||
return InitUserManager(manager, membershipProvider, options);
|
||||
}
|
||||
var manager = new BackOfficeUserManager(customUserStore, options, membershipProvider);
|
||||
return manager;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the user manager with the correct options
|
||||
|
||||
@@ -308,15 +308,28 @@ namespace Umbraco.Core.Services
|
||||
|
||||
}
|
||||
|
||||
public void Validate(IContentTypeComposition compo)
|
||||
/// <summary>
|
||||
/// Validates the composition, if its invalid a list of property type aliases that were duplicated is returned
|
||||
/// </summary>
|
||||
/// <param name="compo"></param>
|
||||
/// <returns></returns>
|
||||
public Attempt<string[]> ValidateComposition(IContentTypeComposition compo)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
ValidateLocked(compo);
|
||||
try
|
||||
{
|
||||
ValidateLocked(compo);
|
||||
return Attempt<string[]>.Succeed();
|
||||
}
|
||||
catch (InvalidCompositionException ex)
|
||||
{
|
||||
return Attempt.Fail(ex.PropertyTypeAliases, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateLocked(IContentTypeComposition compositionContentType)
|
||||
protected void ValidateLocked(IContentTypeComposition compositionContentType)
|
||||
{
|
||||
// performs business-level validation of the composition
|
||||
// should ensure that it is absolutely safe to save the composition
|
||||
@@ -369,10 +382,8 @@ namespace Umbraco.Core.Services
|
||||
if (contentTypeDependency == null) continue;
|
||||
var intersect = contentTypeDependency.PropertyTypes.Select(x => x.Alias.ToLowerInvariant()).Intersect(propertyTypeAliases).ToArray();
|
||||
if (intersect.Length == 0) continue;
|
||||
|
||||
var message = string.Format("The following PropertyType aliases from the current ContentType conflict with existing PropertyType aliases: {0}.",
|
||||
string.Join(", ", intersect));
|
||||
throw new Exception(message);
|
||||
|
||||
throw new InvalidCompositionException(compositionContentType.Alias, intersect.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public interface IContentTypeService : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates the composition, if its invalid a list of property type aliases that were duplicated is returned
|
||||
/// </summary>
|
||||
/// <param name="compo"></param>
|
||||
/// <returns></returns>
|
||||
Attempt<string[]> ValidateComposition(IContentTypeComposition compo);
|
||||
|
||||
Attempt<int> CreateFolder(int parentId, string name, int userId = 0);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -367,6 +367,7 @@
|
||||
<Compile Include="Models\IMigrationEntry.cs" />
|
||||
<Compile Include="Models\IPublishedContentWithKey.cs" />
|
||||
<Compile Include="Models\IServerRegistration.cs" />
|
||||
<Compile Include="Models\Mapping\MappingExpressionExtensions.cs" />
|
||||
<Compile Include="Models\MigrationEntry.cs" />
|
||||
<Compile Include="Models\PartialViewType.cs" />
|
||||
<Compile Include="Models\PublicAccessEntry.cs" />
|
||||
|
||||
@@ -26,12 +26,12 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
public class ContentTypeModelMappingTests : BaseUmbracoConfigurationTest
|
||||
{
|
||||
//Mocks of services that can be setup on a test by test basis to return whatever we want
|
||||
private Mock<IContentTypeService> _contentTypeService = new Mock<IContentTypeService>();
|
||||
private Mock<IContentService> _contentService = new Mock<IContentService>();
|
||||
private Mock<IDataTypeService> _dataTypeService = new Mock<IDataTypeService>();
|
||||
private readonly Mock<IContentTypeService> _contentTypeService = new Mock<IContentTypeService>();
|
||||
private readonly Mock<IContentService> _contentService = new Mock<IContentService>();
|
||||
private readonly Mock<IDataTypeService> _dataTypeService = new Mock<IDataTypeService>();
|
||||
private Mock<PropertyEditorResolver> _propertyEditorResolver;
|
||||
|
||||
private Mock<IEntityService> _entityService = new Mock<IEntityService>();
|
||||
private readonly Mock<IEntityService> _entityService = new Mock<IEntityService>();
|
||||
private readonly Mock<IFileService> _fileService = new Mock<IFileService>();
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
@@ -47,7 +47,9 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
|
||||
contentService: _contentService.Object,
|
||||
contentTypeService:_contentTypeService.Object,
|
||||
dataTypeService:_dataTypeService.Object),
|
||||
dataTypeService:_dataTypeService.Object,
|
||||
entityService:_entityService.Object,
|
||||
fileService: _fileService.Object),
|
||||
|
||||
nullCacheHelper,
|
||||
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
|
||||
@@ -67,84 +69,9 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
entityMapper.ConfigureMappings(configuration, appContext);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void PropertyTypeDisplay_To_PropertyType()
|
||||
{
|
||||
// setup the mocks to return the data we want to test against...
|
||||
|
||||
_dataTypeService.Setup(x => x.GetDataTypeDefinitionById(It.IsAny<int>()))
|
||||
.Returns(Mock.Of<IDataTypeDefinition>(
|
||||
definition =>
|
||||
definition.Id == 555
|
||||
&& definition.PropertyEditorAlias == "myPropertyType"
|
||||
&& definition.DatabaseType == DataTypeDatabaseType.Nvarchar));
|
||||
|
||||
var display = new PropertyTypeDisplay()
|
||||
{
|
||||
Id = 1,
|
||||
Alias = "test",
|
||||
ContentTypeId = 4,
|
||||
Description = "testing",
|
||||
DataTypeId = 555,
|
||||
|
||||
Value = "testsdfasdf",
|
||||
Inherited = false,
|
||||
Editor = "blah",
|
||||
SortOrder = 6,
|
||||
ContentTypeName = "Hello",
|
||||
Label = "asdfasdf",
|
||||
GroupId = 8,
|
||||
Validation = new PropertyTypeValidation()
|
||||
{
|
||||
Mandatory = true,
|
||||
Pattern = "asdfasdfa"
|
||||
}
|
||||
};
|
||||
|
||||
var result = Mapper.Map<PropertyType>(display);
|
||||
|
||||
Assert.AreEqual(1, result.Id);
|
||||
Assert.AreEqual("test", result.Alias);
|
||||
Assert.AreEqual("testing", result.Description);
|
||||
Assert.AreEqual("blah", result.PropertyEditorAlias);
|
||||
Assert.AreEqual(6, result.SortOrder);
|
||||
Assert.AreEqual("asdfasdf", result.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContentGroupDisplay_To_PropertyGroup()
|
||||
{
|
||||
var display = new PropertyGroupDisplay()
|
||||
{
|
||||
ContentTypeId = 2,
|
||||
Id = 1,
|
||||
Inherited = false,
|
||||
Name = "test",
|
||||
ParentGroupId = 4,
|
||||
ParentTabContentTypeNames = new[]
|
||||
{
|
||||
"hello", "world"
|
||||
},
|
||||
SortOrder = 5,
|
||||
ParentTabContentTypes = new[]
|
||||
{
|
||||
10, 11
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var result = Mapper.Map<PropertyGroup>(display);
|
||||
|
||||
Assert.AreEqual(1, result.Id);
|
||||
Assert.AreEqual("test", result.Name);
|
||||
Assert.AreEqual(4, result.ParentId);
|
||||
Assert.AreEqual(5, result.SortOrder);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContentTypeDisplay_To_IContentType()
|
||||
public void ContentTypeSave_To_IContentType()
|
||||
{
|
||||
//Arrange
|
||||
|
||||
@@ -157,8 +84,14 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
&& definition.PropertyEditorAlias == "myPropertyType"
|
||||
&& definition.DatabaseType == DataTypeDatabaseType.Nvarchar));
|
||||
|
||||
|
||||
var display = CreateSimpleContentTypeDisplay();
|
||||
|
||||
_fileService.Setup(x => x.GetTemplate(It.IsAny<string>()))
|
||||
.Returns((string alias) => Mock.Of<ITemplate>(
|
||||
definition =>
|
||||
definition.Id == alias.GetHashCode() && definition.Alias == alias));
|
||||
|
||||
|
||||
var display = CreateContentTypeSave();
|
||||
|
||||
//Act
|
||||
|
||||
@@ -194,10 +127,14 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(display.AllowedTemplates.Count(), result.AllowedTemplates.Count());
|
||||
var allowedTemplateAliases = display.AllowedTemplates
|
||||
.Concat(new[] {display.DefaultTemplate})
|
||||
.Distinct();
|
||||
|
||||
Assert.AreEqual(allowedTemplateAliases.Count(), result.AllowedTemplates.Count());
|
||||
for (var i = 0; i < display.AllowedTemplates.Count(); i++)
|
||||
{
|
||||
Assert.AreEqual(display.AllowedTemplates.ElementAt(i).Id, result.AllowedTemplates.ElementAt(i).Id);
|
||||
Assert.AreEqual(display.AllowedTemplates.ElementAt(i), result.AllowedTemplates.ElementAt(i).Alias);
|
||||
}
|
||||
|
||||
Assert.AreEqual(display.AllowedContentTypes.Count(), result.AllowedContentTypes.Count());
|
||||
@@ -208,7 +145,7 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContentTypeDisplay_With_Composition_To_IContentType()
|
||||
public void ContentTypeSave_With_Composition_To_IContentType()
|
||||
{
|
||||
//Arrange
|
||||
|
||||
@@ -222,7 +159,7 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
&& definition.DatabaseType == DataTypeDatabaseType.Nvarchar));
|
||||
|
||||
|
||||
var display = CreateCompositionContentTypeDisplay();
|
||||
var display = CreateCompositionContentTypeSave();
|
||||
|
||||
//Act
|
||||
|
||||
@@ -306,6 +243,91 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyGroupBasic_To_PropertyGroup()
|
||||
{
|
||||
var basic = new PropertyGroupBasic<PropertyTypeBasic>()
|
||||
{
|
||||
Id = 222,
|
||||
Name = "Group 1",
|
||||
SortOrder = 1,
|
||||
Properties = new[]
|
||||
{
|
||||
new PropertyTypeBasic()
|
||||
{
|
||||
Id = 33,
|
||||
SortOrder = 1,
|
||||
Alias = "prop1",
|
||||
Description = "property 1",
|
||||
DataTypeId = 99,
|
||||
GroupId = 222,
|
||||
Label = "Prop 1",
|
||||
Validation = new PropertyTypeValidation()
|
||||
{
|
||||
Mandatory = true,
|
||||
Pattern = null
|
||||
}
|
||||
},
|
||||
new PropertyTypeBasic()
|
||||
{
|
||||
Id = 34,
|
||||
SortOrder = 2,
|
||||
Alias = "prop2",
|
||||
Description = "property 2",
|
||||
DataTypeId = 99,
|
||||
GroupId = 222,
|
||||
Label = "Prop 2",
|
||||
Validation = new PropertyTypeValidation()
|
||||
{
|
||||
Mandatory = false,
|
||||
Pattern = null
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var result = Mapper.Map<PropertyGroup>(basic);
|
||||
|
||||
Assert.AreEqual(basic.Name, result.Name);
|
||||
Assert.AreEqual(basic.Id, result.Id);
|
||||
Assert.AreEqual(basic.SortOrder, result.SortOrder);
|
||||
Assert.AreEqual(basic.Properties.Count(), result.PropertyTypes.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyTypeBasic_To_PropertyType()
|
||||
{
|
||||
_dataTypeService.Setup(x => x.GetDataTypeDefinitionById(It.IsAny<int>()))
|
||||
.Returns(new DataTypeDefinition("test"));
|
||||
|
||||
var basic = new PropertyTypeBasic()
|
||||
{
|
||||
Id = 33,
|
||||
SortOrder = 1,
|
||||
Alias = "prop1",
|
||||
Description = "property 1",
|
||||
DataTypeId = 99,
|
||||
GroupId = 222,
|
||||
Label = "Prop 1",
|
||||
Validation = new PropertyTypeValidation()
|
||||
{
|
||||
Mandatory = true,
|
||||
Pattern = "xyz"
|
||||
}
|
||||
};
|
||||
|
||||
var result = Mapper.Map<PropertyType>(basic);
|
||||
|
||||
Assert.AreEqual(basic.Id, result.Id);
|
||||
Assert.AreEqual(basic.SortOrder, result.SortOrder);
|
||||
Assert.AreEqual(basic.Alias, result.Alias);
|
||||
Assert.AreEqual(basic.Description, result.Description);
|
||||
Assert.AreEqual(basic.DataTypeId, result.DataTypeDefinitionId);
|
||||
Assert.AreEqual(basic.Label, result.Name);
|
||||
Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory);
|
||||
Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IContentTypeComposition_To_ContentTypeDisplay()
|
||||
{
|
||||
@@ -318,6 +340,9 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
_dataTypeService.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny<int>()))
|
||||
.Returns(new PreValueCollection(new Dictionary<string, PreValue>()));
|
||||
|
||||
_entityService.Setup(x => x.GetObjectType(It.IsAny<int>()))
|
||||
.Returns(UmbracoObjectTypes.DocumentType);
|
||||
|
||||
//return a textbox property editor for any requested editor by alias
|
||||
_propertyEditorResolver.Setup(resolver => resolver.GetByAlias(It.IsAny<string>()))
|
||||
.Returns(new TextboxPropertyEditor());
|
||||
@@ -403,31 +428,19 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
}
|
||||
|
||||
|
||||
private ContentTypeDisplay CreateSimpleContentTypeDisplay()
|
||||
private ContentTypeSave CreateContentTypeSave()
|
||||
{
|
||||
return new ContentTypeDisplay
|
||||
return new ContentTypeSave
|
||||
{
|
||||
Alias = "test",
|
||||
AllowAsRoot = true,
|
||||
AllowedTemplates = new List<EntityBasic>
|
||||
AllowedTemplates = new []
|
||||
{
|
||||
new EntityBasic
|
||||
{
|
||||
Id = 555,
|
||||
Alias = "template1",
|
||||
Name = "Template1"
|
||||
},
|
||||
new EntityBasic
|
||||
{
|
||||
Id = 556,
|
||||
Alias = "template2",
|
||||
Name = "Template2"
|
||||
}
|
||||
"template1",
|
||||
"template2"
|
||||
},
|
||||
|
||||
AllowedContentTypes = new [] {666, 667},
|
||||
AvailableCompositeContentTypes = new List<EntityBasic>(),
|
||||
DefaultTemplate = new EntityBasic(){ Alias = "test" },
|
||||
DefaultTemplate = "test",
|
||||
Description = "hello world",
|
||||
Icon = "tree-icon",
|
||||
Id = 1234,
|
||||
@@ -437,18 +450,17 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
ParentId = -1,
|
||||
Thumbnail = "tree-thumb",
|
||||
IsContainer = true,
|
||||
Groups = new List<PropertyGroupDisplay>()
|
||||
Groups = new []
|
||||
{
|
||||
new PropertyGroupDisplay
|
||||
new PropertyGroupBasic<PropertyTypeBasic>()
|
||||
{
|
||||
Id = 987,
|
||||
Name = "Tab 1",
|
||||
ParentGroupId = -1,
|
||||
SortOrder = 0,
|
||||
Inherited = false,
|
||||
Properties = new List<PropertyTypeDisplay>
|
||||
Properties = new []
|
||||
{
|
||||
new PropertyTypeDisplay
|
||||
new PropertyTypeBasic
|
||||
{
|
||||
Alias = "property1",
|
||||
Description = "this is property 1",
|
||||
@@ -459,17 +471,8 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
Mandatory = false,
|
||||
Pattern = ""
|
||||
},
|
||||
Editor = "myPropertyType",
|
||||
Value = "value 1",
|
||||
//View = ??? - isn't this the same as editor?
|
||||
Config = new Dictionary<string, object>
|
||||
{
|
||||
{"item1", "value1"},
|
||||
{"item2", "value2"}
|
||||
},
|
||||
SortOrder = 0,
|
||||
DataTypeId = 555,
|
||||
View = "blah"
|
||||
DataTypeId = 555
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -477,30 +480,19 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
};
|
||||
}
|
||||
|
||||
private ContentTypeDisplay CreateCompositionContentTypeDisplay()
|
||||
private ContentTypeSave CreateCompositionContentTypeSave()
|
||||
{
|
||||
return new ContentTypeDisplay
|
||||
return new ContentTypeSave
|
||||
{
|
||||
Alias = "test",
|
||||
AllowAsRoot = true,
|
||||
AllowedTemplates = new List<EntityBasic>
|
||||
AllowedTemplates = new[]
|
||||
{
|
||||
new EntityBasic
|
||||
{
|
||||
Id = 555,
|
||||
Alias = "template1",
|
||||
Name = "Template1"
|
||||
},
|
||||
new EntityBasic
|
||||
{
|
||||
Id = 556,
|
||||
Alias = "template2",
|
||||
Name = "Template2"
|
||||
}
|
||||
"template1",
|
||||
"template2"
|
||||
},
|
||||
AllowedContentTypes = new[] { 666, 667 },
|
||||
AvailableCompositeContentTypes = new List<EntityBasic>(),
|
||||
DefaultTemplate = new EntityBasic() { Alias = "test" },
|
||||
DefaultTemplate = "test",
|
||||
Description = "hello world",
|
||||
Icon = "tree-icon",
|
||||
Id = 1234,
|
||||
@@ -510,18 +502,17 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
ParentId = -1,
|
||||
Thumbnail = "tree-thumb",
|
||||
IsContainer = true,
|
||||
Groups = new List<PropertyGroupDisplay>()
|
||||
Groups = new[]
|
||||
{
|
||||
new PropertyGroupDisplay
|
||||
new PropertyGroupBasic<PropertyTypeBasic>()
|
||||
{
|
||||
Id = 987,
|
||||
Name = "Tab 1",
|
||||
ParentGroupId = -1,
|
||||
SortOrder = 0,
|
||||
Inherited = false,
|
||||
Properties = new List<PropertyTypeDisplay>
|
||||
Properties = new[]
|
||||
{
|
||||
new PropertyTypeDisplay
|
||||
new PropertyTypeBasic
|
||||
{
|
||||
Alias = "property1",
|
||||
Description = "this is property 1",
|
||||
@@ -532,30 +523,20 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
Mandatory = false,
|
||||
Pattern = ""
|
||||
},
|
||||
Editor = "myPropertyType",
|
||||
Value = "value 1",
|
||||
//View = ??? - isn't this the same as editor?
|
||||
Config = new Dictionary<string, object>
|
||||
{
|
||||
{"item1", "value1"},
|
||||
{"item2", "value2"}
|
||||
},
|
||||
SortOrder = 0,
|
||||
DataTypeId = 555,
|
||||
View = "blah"
|
||||
DataTypeId = 555
|
||||
}
|
||||
}
|
||||
},
|
||||
new PropertyGroupDisplay
|
||||
new PropertyGroupBasic<PropertyTypeBasic>()
|
||||
{
|
||||
Id = 894,
|
||||
Name = "Tab 2",
|
||||
ParentGroupId = -1,
|
||||
SortOrder = 0,
|
||||
Inherited = true,
|
||||
Properties = new List<PropertyTypeDisplay>
|
||||
Properties = new[]
|
||||
{
|
||||
new PropertyTypeDisplay
|
||||
new PropertyTypeBasic
|
||||
{
|
||||
Alias = "parentProperty",
|
||||
Description = "this is a property from the parent",
|
||||
@@ -565,17 +546,9 @@ namespace Umbraco.Tests.Models.Mapping
|
||||
{
|
||||
Mandatory = false,
|
||||
Pattern = ""
|
||||
},
|
||||
Editor = "myPropertyType",
|
||||
Value = "parent value",
|
||||
//View = ??? - isn't this the same as editor?
|
||||
Config = new Dictionary<string, object>
|
||||
{
|
||||
{"item1", "value1"}
|
||||
},
|
||||
},
|
||||
SortOrder = 0,
|
||||
DataTypeId = 555,
|
||||
View = "blah"
|
||||
DataTypeId = 555
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -46,8 +46,11 @@
|
||||
nameLocked: "=",
|
||||
menu: "=",
|
||||
icon: "=",
|
||||
hideIcon: "@",
|
||||
alias: "=",
|
||||
hideAlias: "@",
|
||||
description: "=",
|
||||
hideDescription: "@",
|
||||
navigation: "="
|
||||
},
|
||||
link: link
|
||||
|
||||
+5
-9
@@ -69,18 +69,14 @@ angular.module("umbraco.directives")
|
||||
function resizeInput() {
|
||||
|
||||
if (domEl.scrollWidth !== domEl.clientWidth) {
|
||||
|
||||
if (ngModelController.$modelValue === undefined || ngModelController.$modelValue === "" || ngModelController.$modelValue === null) {
|
||||
|
||||
if (attr.placeholder) {
|
||||
attr.$set('size', attr.placeholder.length);
|
||||
element.width('auto');
|
||||
}
|
||||
|
||||
} else {
|
||||
if (ngModelController.$modelValue) {
|
||||
element.width(domEl.scrollWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if(!ngModelController.$modelValue && attr.placeholder) {
|
||||
attr.$set('size', attr.placeholder.length);
|
||||
element.width('auto');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
angular.module("umbraco.directives")
|
||||
.directive('umbGenerateAlias', function ($timeout, contentTypeResource) {
|
||||
.directive('umbGenerateAlias', function ($timeout, entityResource) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'views/components/umb-generate-alias.html',
|
||||
@@ -29,7 +29,7 @@ angular.module("umbraco.directives")
|
||||
scope.alias = "Generating Alias...";
|
||||
|
||||
generateAliasTimeout = $timeout(function () {
|
||||
contentTypeResource.getSafeAlias(value, true).then(function(safeAlias){
|
||||
entityResource.getSafeAlias(value, true).then(function (safeAlias) {
|
||||
scope.alias = safeAlias.alias;
|
||||
});
|
||||
}, 500);
|
||||
|
||||
-1
@@ -298,7 +298,6 @@
|
||||
|
||||
if (addGroup) {
|
||||
groups.push({
|
||||
groups: [],
|
||||
properties: [],
|
||||
parentTabContentTypes: [],
|
||||
parentTabContentTypeNames: [],
|
||||
|
||||
+14
-2
@@ -11,7 +11,7 @@
|
||||
|
||||
function LockedFieldDirective($timeout, localizationService) {
|
||||
|
||||
function link(scope, el, attr, ngModel) {
|
||||
function link(scope, el, attr, ngModel) {
|
||||
|
||||
var input = el.children('.umb-locked-field__input');
|
||||
|
||||
@@ -22,6 +22,16 @@
|
||||
scope.locked = true;
|
||||
}
|
||||
|
||||
// if regex validation is not defined as an attr set default state
|
||||
// if this is set to an empty string then regex validation can be ignored.
|
||||
if (scope.regexValidation === undefined || scope.regexValidation === null) {
|
||||
scope.regexValidation = "^[a-zA-Z]\\w.*$";
|
||||
}
|
||||
|
||||
if (scope.serverValidationField === undefined || scope.serverValidationField === null) {
|
||||
scope.serverValidationField = "";
|
||||
}
|
||||
|
||||
// if locked state is not defined as an attr set default state
|
||||
if (scope.placeholderText === undefined || scope.placeholderText === null) {
|
||||
scope.placeholderText = "Enter value...";
|
||||
@@ -71,7 +81,9 @@
|
||||
scope: {
|
||||
model: '=ngModel',
|
||||
locked: "=?",
|
||||
placeholderText: "=?"
|
||||
placeholderText: "=?",
|
||||
regexValidation: "=?",
|
||||
serverValidationField: "@"
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ angular.module('umbraco.directives.validation')
|
||||
}
|
||||
};
|
||||
validators[key] = validateFn;
|
||||
ctrl.$formatters.push(validateFn);
|
||||
|
||||
ctrl.$parsers.push(validateFn);
|
||||
});
|
||||
|
||||
|
||||
+2
-4
@@ -9,9 +9,7 @@ function valHighlight($timeout) {
|
||||
restrict: "A",
|
||||
link: function (scope, element, attrs, ctrl) {
|
||||
|
||||
scope.$watch(function() {
|
||||
return scope.$eval(attrs.valHighlight);
|
||||
}, function(newVal, oldVal) {
|
||||
attrs.$observe("valHighlight", function (newVal) {
|
||||
if (newVal === true) {
|
||||
element.addClass("highlight-error");
|
||||
$timeout(function () {
|
||||
@@ -23,7 +21,7 @@ function valHighlight($timeout) {
|
||||
element.removeClass("highlight-error");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
-1
@@ -29,7 +29,6 @@ function valEmail(valEmailExpression) {
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(patternValidator);
|
||||
ctrl.$parsers.push(patternValidator);
|
||||
}
|
||||
};
|
||||
|
||||
+21
@@ -16,6 +16,27 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not
|
||||
return {
|
||||
require: "form",
|
||||
restrict: "A",
|
||||
controller: function($scope) {
|
||||
//This exposes an API for direct use with this directive
|
||||
|
||||
var unsubscribe = [];
|
||||
var self = this;
|
||||
|
||||
//This is basically the same as a directive subscribing to an event but maybe a little
|
||||
// nicer since the other directive can use this directive's API instead of a magical event
|
||||
this.onValidationStatusChanged = function (cb) {
|
||||
unsubscribe.push($scope.$on("valStatusChanged", function(evt, args) {
|
||||
cb.apply(self, [evt, args]);
|
||||
}));
|
||||
};
|
||||
|
||||
//Ensure to remove the event handlers when this instance is destroyted
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var u in unsubscribe) {
|
||||
unsubscribe[u]();
|
||||
}
|
||||
});
|
||||
},
|
||||
link: function (scope, element, attr, formCtrl) {
|
||||
|
||||
scope.$watch(function () {
|
||||
|
||||
-3
@@ -59,9 +59,6 @@ function valPropertyValidator(serverValidationManager) {
|
||||
}
|
||||
};
|
||||
|
||||
// Formatters are invoked when the model is modified in the code.
|
||||
modelCtrl.$formatters.push(validate);
|
||||
|
||||
// Parsers are called as soon as the value in the form input is modified
|
||||
modelCtrl.$parsers.push(validate);
|
||||
|
||||
|
||||
+37
-36
@@ -13,50 +13,51 @@ function valRegex() {
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
|
||||
var flags = "";
|
||||
if (attrs.valRegexFlags) {
|
||||
try {
|
||||
flags = scope.$eval(attrs.valRegexFlags);
|
||||
if (!flags) {
|
||||
flags = attrs.valRegexFlags;
|
||||
var regex;
|
||||
|
||||
attrs.$observe("valRegexFlags", function (newVal) {
|
||||
if (newVal) {
|
||||
flags = newVal;
|
||||
}
|
||||
});
|
||||
|
||||
attrs.$observe("valRegex", function (newVal) {
|
||||
if (newVal) {
|
||||
try {
|
||||
var resolved = newVal;
|
||||
if (resolved) {
|
||||
regex = new RegExp(resolved, flags);
|
||||
}
|
||||
else {
|
||||
regex = new RegExp(attrs.valRegex, flags);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
regex = new RegExp(attrs.valRegex, flags);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
flags = attrs.valRegexFlags;
|
||||
}
|
||||
}
|
||||
var regex;
|
||||
try {
|
||||
var resolved = scope.$eval(attrs.valRegex);
|
||||
if (resolved) {
|
||||
regex = new RegExp(resolved, flags);
|
||||
}
|
||||
else {
|
||||
regex = new RegExp(attrs.valRegex, flags);
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
regex = new RegExp(attrs.valRegex, flags);
|
||||
}
|
||||
});
|
||||
|
||||
var patternValidator = function (viewValue) {
|
||||
//NOTE: we don't validate on empty values, use required validator for that
|
||||
if (regex) {
|
||||
//NOTE: we don't validate on empty values, use required validator for that
|
||||
if (!viewValue || regex.test(viewValue.toString())) {
|
||||
// it is valid
|
||||
ctrl.$setValidity('valRegex', true);
|
||||
//assign a message to the validator
|
||||
ctrl.errorMsg = "";
|
||||
return viewValue;
|
||||
}
|
||||
else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('valRegex', false);
|
||||
//assign a message to the validator
|
||||
ctrl.errorMsg = "Value is invalid, it does not match the correct pattern";
|
||||
return undefined;
|
||||
// it is valid
|
||||
ctrl.$setValidity('valRegex', true);
|
||||
//assign a message to the validator
|
||||
ctrl.errorMsg = "";
|
||||
return viewValue;
|
||||
}
|
||||
else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('valRegex', false);
|
||||
//assign a message to the validator
|
||||
ctrl.errorMsg = "Value is invalid, it does not match the correct pattern";
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(patternValidator);
|
||||
ctrl.$parsers.push(patternValidator);
|
||||
}
|
||||
};
|
||||
|
||||
+37
-34
@@ -11,43 +11,46 @@ function valServerField(serverValidationManager) {
|
||||
restrict: "A",
|
||||
link: function (scope, element, attr, ctrl) {
|
||||
|
||||
if (!attr.valServerField) {
|
||||
throw "valServerField must have a field name for referencing server errors";
|
||||
}
|
||||
var fieldName = null;
|
||||
|
||||
attr.$observe("valServerField", function (newVal) {
|
||||
if (newVal && fieldName === null) {
|
||||
fieldName = newVal;
|
||||
|
||||
//subscribe to the changed event of the view model. This is required because when we
|
||||
// have a server error we actually invalidate the form which means it cannot be
|
||||
// resubmitted. So once a field is changed that has a server error assigned to it
|
||||
// we need to re-validate it for the server side validator so the user can resubmit
|
||||
// the form. Of course normal client-side validators will continue to execute.
|
||||
ctrl.$viewChangeListeners.push(function () {
|
||||
if (ctrl.$invalid) {
|
||||
ctrl.$setValidity('valServerField', true);
|
||||
}
|
||||
});
|
||||
|
||||
//subscribe to the server validation changes
|
||||
serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
|
||||
if (!isValid) {
|
||||
ctrl.$setValidity('valServerField', false);
|
||||
//assign an error msg property to the current validator
|
||||
ctrl.errorMsg = fieldErrors[0].errorMsg;
|
||||
}
|
||||
else {
|
||||
ctrl.$setValidity('valServerField', true);
|
||||
//reset the error message
|
||||
ctrl.errorMsg = "";
|
||||
}
|
||||
});
|
||||
|
||||
//when the element is disposed we need to unsubscribe!
|
||||
// NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
|
||||
// but they are a different callback instance than the above.
|
||||
element.bind('$destroy', function () {
|
||||
serverValidationManager.unsubscribe(null, fieldName);
|
||||
});
|
||||
|
||||
var fieldName = attr.valServerField;
|
||||
|
||||
//subscribe to the changed event of the view model. This is required because when we
|
||||
// have a server error we actually invalidate the form which means it cannot be
|
||||
// resubmitted. So once a field is changed that has a server error assigned to it
|
||||
// we need to re-validate it for the server side validator so the user can resubmit
|
||||
// the form. Of course normal client-side validators will continue to execute.
|
||||
ctrl.$viewChangeListeners.push(function () {
|
||||
if (ctrl.$invalid) {
|
||||
ctrl.$setValidity('valServerField', true);
|
||||
}
|
||||
});
|
||||
|
||||
//subscribe to the server validation changes
|
||||
serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
|
||||
if (!isValid) {
|
||||
ctrl.$setValidity('valServerField', false);
|
||||
//assign an error msg property to the current validator
|
||||
ctrl.errorMsg = fieldErrors[0].errorMsg;
|
||||
}
|
||||
else {
|
||||
ctrl.$setValidity('valServerField', true);
|
||||
//reset the error message
|
||||
ctrl.errorMsg = "";
|
||||
}
|
||||
});
|
||||
|
||||
//when the element is disposed we need to unsubscribe!
|
||||
// NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
|
||||
// but they are a different callback instance than the above.
|
||||
element.bind('$destroy', function () {
|
||||
serverValidationManager.unsubscribe(null, fieldName);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+6
-6
@@ -8,16 +8,16 @@
|
||||
**/
|
||||
function valTab() {
|
||||
return {
|
||||
require: "^form",
|
||||
require: ['^form', '^valFormManager'],
|
||||
restrict: "A",
|
||||
link: function (scope, element, attr, formCtrl) {
|
||||
|
||||
var tabId = "tab" + scope.tab.id;
|
||||
|
||||
link: function (scope, element, attr, ctrs) {
|
||||
|
||||
var valFormManager = ctrs[1];
|
||||
var tabId = "tab" + scope.tab.id;
|
||||
scope.tabHasError = false;
|
||||
|
||||
//listen for form validation changes
|
||||
scope.$on("valStatusChanged", function(evt, args) {
|
||||
valFormManager.onValidationStatusChanged(function (evt, args) {
|
||||
if (!args.form.$valid) {
|
||||
var tabContent = element.closest(".umb-panel").find("#" + tabId);
|
||||
//check if the validation messages are contained inside of this tabs
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @name umbraco.resources.contentTypeResource
|
||||
* @description Loads in data for content types
|
||||
**/
|
||||
function contentTypeResource($q, $http, umbRequestHelper) {
|
||||
function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
|
||||
return {
|
||||
|
||||
@@ -145,16 +145,6 @@ function contentTypeResource($q, $http, umbRequestHelper) {
|
||||
'Failed to retrieve content type scaffold');
|
||||
},
|
||||
|
||||
getSafeAlias: function (value, camelCase) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"contentTypeApiBaseUrl",
|
||||
"GetSafeAlias", { value: value, camelCase: camelCase })),
|
||||
'Failed to retrieve content type scaffold');
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.contentTypeResource#save
|
||||
@@ -169,8 +159,10 @@ function contentTypeResource($q, $http, umbRequestHelper) {
|
||||
*/
|
||||
save: function (contentType) {
|
||||
|
||||
var saveModel = umbDataFormatter.formatContentTypePostData(contentType);
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostSave"), contentType),
|
||||
$http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostSave"), saveModel),
|
||||
'Failed to save data for content type id ' + contentType.id);
|
||||
},
|
||||
|
||||
|
||||
@@ -36,6 +36,16 @@ function entityResource($q, $http, umbRequestHelper) {
|
||||
//the factory object returned
|
||||
return {
|
||||
|
||||
getSafeAlias: function (value, camelCase) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"entityApiBaseUrl",
|
||||
"GetSafeAlias", { value: value, camelCase: camelCase })),
|
||||
'Failed to retrieve content type scaffold');
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.entityResource#getPath
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @name umbraco.resources.mediaTypeResource
|
||||
* @description Loads in data for media types
|
||||
**/
|
||||
function mediaTypeResource($q, $http, umbRequestHelper) {
|
||||
function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
|
||||
return {
|
||||
|
||||
@@ -81,12 +81,10 @@ function mediaTypeResource($q, $http, umbRequestHelper) {
|
||||
|
||||
save: function (contentType) {
|
||||
|
||||
var saveModel = umbDataFormatter.formatContentTypePostData(contentType);
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"mediaTypeApiBaseUrl",
|
||||
"PostSave"
|
||||
), contentType),
|
||||
$http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostSave"), saveModel),
|
||||
'Failed to save data for content type id ' + contentType.id);
|
||||
},
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @name umbraco.resources.memberTypeResource
|
||||
* @description Loads in data for member types
|
||||
**/
|
||||
function memberTypeResource($q, $http, umbRequestHelper) {
|
||||
function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
|
||||
return {
|
||||
|
||||
@@ -74,8 +74,10 @@ function memberTypeResource($q, $http, umbRequestHelper) {
|
||||
*/
|
||||
save: function (contentType) {
|
||||
|
||||
var saveModel = umbDataFormatter.formatContentTypePostData(contentType);
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(umbRequestHelper.getApiUrl("memberTypeApiBaseUrl", "PostSave"), contentType),
|
||||
$http.post(umbRequestHelper.getApiUrl("memberTypeApiBaseUrl", "PostSave"), saveModel),
|
||||
'Failed to save data for member type id ' + contentType.id);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
|
||||
var self = this;
|
||||
|
||||
//we will use the default one for content if not specified
|
||||
var rebindCallback = args.rebindCallback === undefined ? self.reBindChangedProperties : args.rebindCallback;
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, statusMessage: args.statusMessage })) {
|
||||
@@ -62,7 +65,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
self.handleSuccessfulSave({
|
||||
scope: args.scope,
|
||||
savedContent: data,
|
||||
rebindCallback: self.reBindChangedProperties(args.content, data)
|
||||
rebindCallback: function() {
|
||||
rebindCallback.apply(self, [args.content, data]);
|
||||
}
|
||||
});
|
||||
|
||||
args.scope.busy = false;
|
||||
@@ -72,7 +77,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
self.handleSaveError({
|
||||
redirectOnFailure: true,
|
||||
err: err,
|
||||
rebindCallback: self.reBindChangedProperties(args.content, err.data)
|
||||
rebindCallback: function() {
|
||||
rebindCallback.apply(self, [args.content, err.data]);
|
||||
}
|
||||
});
|
||||
//show any notifications
|
||||
if (angular.isArray(err.data.notifications)) {
|
||||
@@ -91,6 +98,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
|
||||
/** Returns the action button definitions based on what permissions the user has.
|
||||
The content.allowedActions parameter contains a list of chars, each represents a button by permission so
|
||||
here we'll build the buttons according to the chars of the user. */
|
||||
|
||||
@@ -24,6 +24,28 @@ function dataTypeHelper() {
|
||||
|
||||
return preValues;
|
||||
|
||||
},
|
||||
|
||||
rebindChangedProperties: function (origContent, savedContent) {
|
||||
|
||||
//a method to ignore built-in prop changes
|
||||
var shouldIgnore = function (propName) {
|
||||
return _.some(["notifications", "ModelState"], function (i) {
|
||||
return i === propName;
|
||||
});
|
||||
};
|
||||
//check for changed built-in properties of the content
|
||||
for (var o in origContent) {
|
||||
|
||||
//ignore the ones listed in the array
|
||||
if (shouldIgnore(o)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_.isEqual(origContent[o], savedContent[o])) {
|
||||
origContent[o] = savedContent[o];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* A utility class used to streamline how forms are developed, to ensure that validation is check and displayed consistently and to ensure that the correct events
|
||||
* fire when they need to.
|
||||
*/
|
||||
function formHelper(angularHelper, serverValidationManager, $timeout, notificationsService, dialogService) {
|
||||
function formHelper(angularHelper, serverValidationManager, $timeout, notificationsService, dialogService, localizationService) {
|
||||
return {
|
||||
|
||||
/**
|
||||
@@ -157,9 +157,19 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
|
||||
*
|
||||
* @param {object} err The error object returned from the http promise
|
||||
*/
|
||||
handleServerValidation: function(modelState) {
|
||||
handleServerValidation: function (modelState) {
|
||||
for (var e in modelState) {
|
||||
|
||||
//This is where things get interesting....
|
||||
// We need to support validation for all editor types such as both the content and content type editors.
|
||||
// The Content editor ModelState is quite specific with the way that Properties are validated especially considering
|
||||
// that each property is a User Developer property editor.
|
||||
// The way that Content Type Editor ModelState is created is simply based on the ASP.Net validation data-annotations
|
||||
// system.
|
||||
// So, to do this (since we need to support backwards compat), we need to hack a little bit. For Content Properties,
|
||||
// which are user defined, we know that they will exist with a prefixed ModelState of "_Properties.", so if we detect
|
||||
// this, then we know it's a Property.
|
||||
|
||||
//the alias in model state can be in dot notation which indicates
|
||||
// * the first part is the content property alias
|
||||
// * the second part is the field to which the valiation msg is associated with
|
||||
@@ -167,7 +177,11 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
|
||||
//If it is not prefixed with "Properties" that means the error is for a field of the object directly.
|
||||
|
||||
var parts = e.split(".");
|
||||
if (parts.length > 1) {
|
||||
|
||||
//Check if this is for content properties - specific to content/media/member editors because those are special
|
||||
// user defined properties with custom controls.
|
||||
if (parts.length > 1 && parts[0] === "_Properties") {
|
||||
|
||||
var propertyAlias = parts[1];
|
||||
|
||||
//if it contains 2 '.' then we will wire it up to a property's field
|
||||
@@ -182,12 +196,15 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
|
||||
|
||||
}
|
||||
else {
|
||||
//the parts are only 1, this means its not a property but a native content property
|
||||
serverValidationManager.addFieldError(parts[0], modelState[e][0]);
|
||||
|
||||
//Everthing else is just a 'Field'... the field name could contain any level of 'parts' though, for example:
|
||||
// Groups[0].Properties[2].Alias
|
||||
serverValidationManager.addFieldError(e, modelState[e][0]);
|
||||
}
|
||||
|
||||
//add to notifications
|
||||
notificationsService.error("Validation", modelState[e][0]);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -487,6 +487,53 @@ angular.module('umbraco.services').factory('umbPropEditorHelper', umbPropEditorH
|
||||
function umbDataFormatter() {
|
||||
return {
|
||||
|
||||
formatContentTypePostData: function (displayModel, action) {
|
||||
|
||||
//create the save model from the display model
|
||||
var saveModel = _.pick(displayModel,
|
||||
'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes',
|
||||
'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed',
|
||||
'key', 'parentId', 'alias', 'path');
|
||||
|
||||
//TODO: Map these
|
||||
saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; });
|
||||
saveModel.defaultTemplate = displayModel.defaultTemplate ? displayModel.defaultTemplate.alias : null;
|
||||
var realGroups = _.reject(displayModel.groups, function(g) {
|
||||
//do not include these tabs
|
||||
return g.tabState === "init";
|
||||
});
|
||||
saveModel.groups = _.map(realGroups, function (g) {
|
||||
|
||||
var saveGroup = _.pick(g, 'inherited', 'id', 'sortOrder', 'name');
|
||||
|
||||
var realProperties = _.reject(g.properties, function (p) {
|
||||
//do not include these properties
|
||||
return p.propertyState === "init" || p.inherited === true;
|
||||
});
|
||||
|
||||
var saveProperties = _.map(realProperties, function (p) {
|
||||
var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId');
|
||||
return saveProperty;
|
||||
});
|
||||
|
||||
saveGroup.properties = saveProperties;
|
||||
|
||||
//if this is an inherited group and there are not non-inherited properties on it, then don't send up the data
|
||||
if (saveGroup.inherited === true && saveProperties.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return saveGroup;
|
||||
});
|
||||
|
||||
//we don't want any null groups
|
||||
saveModel.groups = _.reject(saveModel.groups, function(g) {
|
||||
return !g;
|
||||
});
|
||||
|
||||
return saveModel;
|
||||
},
|
||||
|
||||
/** formats the display model used to display the data type to the model used to save the data type */
|
||||
formatDataTypePostData: function(displayModel, preValues, action) {
|
||||
var saveModel = {
|
||||
|
||||
@@ -123,7 +123,7 @@ input.umb-group-builder__group-title-input {
|
||||
}
|
||||
|
||||
.umb-group-builder__group-title-input.-placeholder {
|
||||
border: 1px dashed #979797 !important;
|
||||
border: 1px dashed #979797;
|
||||
}
|
||||
|
||||
.umb-group-builder__group-inherited-label {
|
||||
@@ -412,11 +412,10 @@ input.umb-group-builder__group-sort-value {
|
||||
}
|
||||
|
||||
textarea.editor-label {
|
||||
border: none;
|
||||
border-color:transparent;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 0 0 0;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -27,9 +27,8 @@
|
||||
|
||||
input.umb-locked-field__input {
|
||||
background: rgba(255, 255, 255, 0); // if using transparent it will hide the text in safari
|
||||
border: none;
|
||||
font-size: 13px;
|
||||
padding: 0;
|
||||
border-color:transparent;
|
||||
font-size: 13px;
|
||||
margin-bottom: 0;
|
||||
color: #ccc;
|
||||
transition: color 0.25s;
|
||||
|
||||
@@ -442,37 +442,11 @@ input[type="checkbox"][readonly] {
|
||||
// FORM FIELD FEEDBACK STATES
|
||||
// --------------------------
|
||||
|
||||
// Warning
|
||||
.show-validation.ng-invalid .control-group.warning {
|
||||
.formFieldState(@formWarningText, @formWarningText, @formWarningBackground);
|
||||
}
|
||||
// Error
|
||||
.show-validation.ng-invalid .control-group.error {
|
||||
.show-validation.ng-invalid .control-group.error,
|
||||
.show-validation.ng-invalid .umb-panel-header-content-wrapper {
|
||||
.formFieldState(@formErrorText, @formErrorText, @formErrorBackground);
|
||||
}
|
||||
// Success
|
||||
.show-validation.ng-invalid .control-group.success {
|
||||
.formFieldState(@formSuccessText, @formSuccessText, @formSuccessBackground);
|
||||
}
|
||||
// Success
|
||||
.show-validation.ng-invalid .control-group.info {
|
||||
.formFieldState(@formInfoText, @formInfoText, @formInfoBackground);
|
||||
}
|
||||
|
||||
// HTML5 invalid states
|
||||
// Shares styles with the .control-group.error above
|
||||
|
||||
.show-validation input:focus:invalid,
|
||||
.show-validation textarea:focus:invalid,
|
||||
.show-validation select:focus:invalid {
|
||||
color: @formErrorText;
|
||||
border-color: #ee5f5b;
|
||||
&:focus {
|
||||
border-color: darken(#ee5f5b, 10%);
|
||||
@shadow: 0 0 6px lighten(#ee5f5b, 20%);
|
||||
.box-shadow(@shadow);
|
||||
}
|
||||
}
|
||||
|
||||
//val-highlight directive styling
|
||||
.highlight-error {
|
||||
@@ -747,6 +721,11 @@ input.search-query {
|
||||
margin-bottom: @baseLineHeight / 2;
|
||||
}
|
||||
|
||||
//modifier for control group
|
||||
.control-group.-no-margin {
|
||||
margin-bottom:0;
|
||||
}
|
||||
|
||||
// Legend collapses margin, so next element is responsible for spacing
|
||||
legend + .control-group {
|
||||
margin-top: @baseLineHeight;
|
||||
@@ -819,4 +798,4 @@ legend + .control-group {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,12 +182,6 @@
|
||||
select.ng-invalid,
|
||||
textarea.ng-invalid {
|
||||
border-color: @borderColor;
|
||||
.box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
|
||||
&:focus {
|
||||
border-color: darken(@borderColor, 10%);
|
||||
@shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@borderColor, 20%);
|
||||
.box-shadow(@shadow);
|
||||
}
|
||||
}
|
||||
// Give a small background color for input-prepend/-append
|
||||
.input-prepend .add-on,
|
||||
@@ -196,6 +190,18 @@
|
||||
background-color: @backgroundColor;
|
||||
border-color: @textColor;
|
||||
}
|
||||
//SD: We could do this but need to get the colors right
|
||||
/*input.ng-invalid {
|
||||
&:-moz-placeholder {
|
||||
color: lighten(@textColor, 50%) !important;
|
||||
}
|
||||
&:-ms-input-placeholder {
|
||||
color: lighten(@textColor, 50%) !important;
|
||||
}
|
||||
&::-webkit-input-placeholder {
|
||||
color: lighten(@textColor, 50%) !important;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
// CSS3 PROPERTIES
|
||||
|
||||
@@ -99,21 +99,13 @@
|
||||
color: @red;
|
||||
}
|
||||
|
||||
.umb-headline-editor-wrapper input.ng-invalid::-moz-placeholder,
|
||||
.umb-headline-editor-wrapper input.ng-invalid:-ms-input-placeholder,
|
||||
.umb-headline-editor-wrapper input.ng-invalid::-webkit-input-placeholder {
|
||||
color: @red;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.umb-headline-editor-wrapper input.ng-invalid::-moz-placeholder {
|
||||
color: @red;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.umb-headline-editor-wrapper input.ng-invalid:-ms-input-placeholder {
|
||||
color: @red;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
/*
|
||||
.umb-panel-header i {
|
||||
font-size: 13px;
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
<umb-editor-header
|
||||
name="dashboard.name"
|
||||
name-locked="page.nameLocked"
|
||||
tabs="dashboard.tabs">
|
||||
tabs="dashboard.tabs"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
<div class="umb-el-wrap">
|
||||
<label class="control-label" for="dataTypeName">Name</label>
|
||||
<div class="controls">
|
||||
<input type="text" ng-model="model.dataType.name" class="umb-editor" umb-auto-focus focus-on-filled="true" />
|
||||
<input type="text" ng-model="model.dataType.name" class="umb-editor" umb-auto-focus focus-on-filled="true" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+22
-25
@@ -1,36 +1,33 @@
|
||||
<div class="content-type-editor-dialog edit-property-settings" ng-controller="Umbraco.Overlay.PropertySettingsOverlay as vm">
|
||||
|
||||
<div class="umb-control-group clearfix">
|
||||
|
||||
<div class="validation-wrapper">
|
||||
<textarea class="editor-label"
|
||||
name="propertyLabel"
|
||||
ng-model="model.property.label"
|
||||
placeholder="Enter a name..."
|
||||
umb-auto-focus
|
||||
focus-on-filled="true"
|
||||
umb-auto-resize
|
||||
required
|
||||
overlay-submit-on-enter>
|
||||
</textarea>
|
||||
<span class="validation-label" val-msg-for="propertyLabel" val-toggle-msg="required"><localize key="required" /></span>
|
||||
</div>
|
||||
|
||||
<umb-generate-alias enable-lock="true" alias-from="model.property.label" alias="model.property.alias"></umb-generate-alias>
|
||||
|
||||
<div class="umb-control-group">
|
||||
<div class="control-group -no-margin">
|
||||
<textarea class="editor-label"
|
||||
name="propertyLabel"
|
||||
ng-model="model.property.label"
|
||||
placeholder="Enter a name..."
|
||||
umb-auto-focus
|
||||
focus-on-filled="true"
|
||||
umb-auto-resize
|
||||
required
|
||||
overlay-submit-on-enter></textarea>
|
||||
</div>
|
||||
<div class="control-group -no-margin">
|
||||
<umb-generate-alias enable-lock="true" alias-from="model.property.label" alias="model.property.alias"></umb-generate-alias>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group clearfix">
|
||||
<div class="umb-control-group control-group">
|
||||
<textarea
|
||||
class="editor-description"
|
||||
ng-model="model.property.description"
|
||||
placeholder="Add your description here..."
|
||||
overlay-submit-on-enter
|
||||
umb-auto-resize>
|
||||
class="editor-description"
|
||||
ng-model="model.property.description"
|
||||
placeholder="Add your description here..."
|
||||
overlay-submit-on-enter
|
||||
umb-auto-resize>
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<div class="editor-wrapper umb-control-group clearfix" ng-model="model.property.editor" val-require-component>
|
||||
<div class="editor-wrapper umb-control-group control-group" ng-model="model.property.editor" val-require-component>
|
||||
|
||||
<a href="" ng-if="!model.property.editor" class="editor-placeholder" hotkey="alt+shift+e" ng-click="vm.openEditorPickerOverlay(model.property)">Add Editor</a>
|
||||
|
||||
|
||||
@@ -6,19 +6,29 @@
|
||||
|
||||
<div class="umb-panel-header-left-side">
|
||||
|
||||
<div class="umb-panel-header-icon" ng-if="icon!==undefined" ng-click="openIconPicker()" ng-class="{'-placeholder': $parent.icon==='' || $parent.icon===null}">
|
||||
<div class="umb-panel-header-icon" ng-if="!hideIcon" ng-click="openIconPicker()" ng-class="{'-placeholder': $parent.icon==='' || $parent.icon===null}">
|
||||
<i class="icon {{$parent.icon}}" ng-if="$parent.icon!=='' && $parent.icon!==null"></i>
|
||||
<div class="umb-panel-header-icon-text" ng-if="$parent.icon==='' || $parent.icon===null">Add icon</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-panel-header-title-wrapper">
|
||||
|
||||
<input type="text" class="umb-panel-header-name-input" placeholder="Enter name..." ng-if="name!==undefined && !nameLocked" ng-model="$parent.name" ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}" umb-auto-focus focus-on-filled="true" />
|
||||
<div class="umb-panel-header-name" ng-if="name!==undefined && nameLocked">{{ name }}</div>
|
||||
<ng-form name="headerNameForm">
|
||||
<input type="text" class="umb-panel-header-name-input" placeholder="Enter name..."
|
||||
name="headerName"
|
||||
ng-show="!nameLocked"
|
||||
ng-model="name"
|
||||
ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}"
|
||||
umb-auto-focus
|
||||
focus-on-filled="true"
|
||||
required />
|
||||
</ng-form>
|
||||
|
||||
<umb-generate-alias class="umb-panel-header-alias" ng-if="alias!==undefined" alias="$parent.alias" alias-from="$parent.name" enable-lock="true"></umb-generate-alias>
|
||||
<div class="umb-panel-header-name" ng-if="nameLocked">{{ name }}</div>
|
||||
|
||||
<input type="text" class="umb-panel-header-description" placeholder="Enter description..." ng-if="description!==undefined" ng-model="$parent.description" />
|
||||
<umb-generate-alias class="umb-panel-header-alias" ng-if="!hideAlias" alias="$parent.alias" alias-from="$parent.name" enable-lock="true"></umb-generate-alias>
|
||||
|
||||
<input type="text" class="umb-panel-header-description" placeholder="Enter description..." ng-if="!hideDescription" ng-model="$parent.description" />
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<div class="umb-editor-sub-views">
|
||||
<div ng-if="activeView.view && activeView.active" ng-include="activeView.view"></div>
|
||||
<div ng-repeat="sub in subViews" ng-include="sub.view" ng-show="activeView.name == sub.name" ng-class="'sub-view-' + sub.name"></div>
|
||||
<div ng-if="!activeView.view && activeView.active" ng-transclude></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -29,10 +29,23 @@
|
||||
|
||||
<div class="umb-group-builder__group-title-wrapper">
|
||||
|
||||
<div class="umb-group-builder__group-title" ng-class="{'-active':tab.tabState=='active', '-inherited': tab.inherited}">
|
||||
<i class="umb-group-builder__group-title-icon icon-navigation" ng-if="sortingMode && !tab.inherited"></i>
|
||||
<input class="umb-group-builder__group-title-input" type="text" placeholder="Enter name..." ng-model="tab.name" ng-class="{'-placeholder': tab.name == ''}" ng-change="updateGroupTitle(tab)" ng-disabled="tab.inherited" umb-auto-focus umb-auto-resize />
|
||||
</div>
|
||||
<ng-form name="groupNameForm">
|
||||
<div class="umb-group-builder__group-title control-group -no-margin" ng-class="{'-active':tab.tabState=='active', '-inherited': tab.inherited}">
|
||||
<i class="umb-group-builder__group-title-icon icon-navigation" ng-if="sortingMode && !tab.inherited"></i>
|
||||
<input class="umb-group-builder__group-title-input" type="text" placeholder="Enter name..."
|
||||
name="groupName"
|
||||
ng-model="tab.name"
|
||||
ng-class="{'-placeholder': tab.name == ''}"
|
||||
ng-change="updateGroupTitle(tab)"
|
||||
ng-disabled="tab.inherited"
|
||||
umb-auto-focus
|
||||
umb-auto-resize
|
||||
required
|
||||
val-server-field="{{'Groups[' + $index + '].Name'}}" />
|
||||
|
||||
<span class="help-inline" val-msg-for="groupName" val-toggle-msg="valServerField"></span>
|
||||
</div>
|
||||
</ng-form>
|
||||
|
||||
<input type="number" class="umb-group-builder__group-sort-value" ng-if="sortingMode" ng-model="tab.sortOrder" ng-disabled="tab.inherited" on-blur="changeSortOrderValue()" />
|
||||
|
||||
@@ -80,19 +93,32 @@
|
||||
<i class="icon icon-merge"></i> Inherited from {{property.contentTypeName}}
|
||||
</div>
|
||||
|
||||
<div ng-if="!sortingMode">
|
||||
<ng-form name="propertyTypeForm">
|
||||
<div class="control-group -no-margin" ng-if="!sortingMode">
|
||||
|
||||
<div class="umb-group-builder__property-meta-alias" ng-if="property.inherited">{{ property.alias }}</div>
|
||||
<umb-locked-field ng-if="!property.inherited"
|
||||
locked="locked"
|
||||
ng-model="property.alias"
|
||||
placeholder-text="'Alias...'"
|
||||
server-validation-field="{{'Groups[' + $parent.$parent.$parent.$parent.$index + '].Properties[' + $index + '].Alias'}}">
|
||||
</umb-locked-field>
|
||||
|
||||
<div class="umb-group-builder__property-meta-alias">{{ property.alias }}</div>
|
||||
<div class="umb-group-builder__property-meta-label">
|
||||
<textarea placeholder="Label..." ng-model="property.label" ng-disabled="property.inherited"
|
||||
name="groupName"
|
||||
umb-auto-resize
|
||||
required
|
||||
val-server-field="{{'Groups[' + $parent.$parent.$parent.$parent.$index + '].Properties[' + $index + '].Label'}}"></textarea>
|
||||
|
||||
<span class="help-inline" val-msg-for="groupName" val-toggle-msg="valServerField"></span>
|
||||
</div>
|
||||
|
||||
<div class="umb-group-builder__property-meta-label">
|
||||
<textarea placeholder="Label..." ng-model="property.label" ng-disabled="property.inherited" umb-auto-resize></textarea>
|
||||
<div class="umb-group-builder__property-meta-description">
|
||||
<textarea ng-model="property.description" placeholder="Enter your description..." ng-disabled="property.inherited" umb-auto-resize></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-group-builder__property-meta-description">
|
||||
<textarea ng-model="property.description" placeholder="Enter your description..." ng-disabled="property.inherited" umb-auto-resize></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-form>
|
||||
|
||||
<div ng-if="sortingMode">
|
||||
<i class="icon icon-navigation" ng-if="!property.inherited"></i>
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
<div class="umb-locked-field">
|
||||
<ng-form>
|
||||
<a ng-if="locked" href="" ng-click="unlock()" class="umb-locked-field__toggle">
|
||||
<i class="umb-locked-field__lock-icon icon-lock"></i>
|
||||
</a>
|
||||
|
||||
<a ng-if="locked" href="" ng-click="unlock()" class="umb-locked-field__toggle">
|
||||
<i class="umb-locked-field__lock-icon icon-lock"></i>
|
||||
</a>
|
||||
<a ng-if="!locked" href="" ng-click="lock()" class="umb-locked-field__toggle">
|
||||
<i class="umb-locked-field__lock-icon icon-unlocked -unlocked"></i>
|
||||
</a>
|
||||
|
||||
<a ng-if="!locked" href="" ng-click="lock()" class="umb-locked-field__toggle">
|
||||
<i class="umb-locked-field__lock-icon icon-unlocked -unlocked"></i>
|
||||
</a>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
class="umb-locked-field__input"
|
||||
name="lockedField"
|
||||
ng-model="model"
|
||||
ng-disabled="locked"
|
||||
ng-class="{'-unlocked': !locked}"
|
||||
placeholder="{{placeholderText}}"
|
||||
umb-auto-resize
|
||||
/>
|
||||
<input type="text"
|
||||
class="umb-locked-field__input"
|
||||
name="lockedField"
|
||||
ng-model="model"
|
||||
ng-disabled="locked"
|
||||
ng-class="{'-unlocked': !locked}"
|
||||
placeholder="{{placeholderText}}"
|
||||
val-regex="{{regexValidation}}"
|
||||
umb-auto-resize
|
||||
required
|
||||
val-server-field="{{serverValidationField}}" />
|
||||
|
||||
<span class="validation-label" val-msg-for="lockedField" val-toggle-msg="required"><localize key="required" /></span>
|
||||
<span ng-if="regexValidation.length > 0" class="validation-label" val-msg-for="lockedField" val-toggle-msg="valRegex">Invalid alias</span>
|
||||
<span ng-if="serverValidationField.length > 0" class="validation-label" val-msg-for="lockedField" val-toggle-msg="valServerField"></span>
|
||||
</ng-form>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
<umb-editor-header
|
||||
menu="page.menu"
|
||||
name="content.name"
|
||||
tabs="content.tabs">
|
||||
tabs="content.tabs"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
<umb-editor-header
|
||||
name="page.name"
|
||||
name-locked="page.nameLocked">
|
||||
name-locked="page.nameLocked"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true" >
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @description
|
||||
* The controller for the content editor
|
||||
*/
|
||||
function DataTypeEditController($scope, $routeParams, $location, appState, navigationService, treeService, dataTypeResource, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, formHelper, editorState) {
|
||||
function DataTypeEditController($scope, $routeParams, $location, appState, navigationService, treeService, dataTypeResource, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, formHelper, editorState, dataTypeHelper) {
|
||||
|
||||
//setup scope vars
|
||||
$scope.page = {};
|
||||
@@ -158,6 +158,8 @@ function DataTypeEditController($scope, $routeParams, $location, appState, navig
|
||||
|
||||
$scope.page.saveButtonState = "success";
|
||||
|
||||
dataTypeHelper.rebindChangedProperties($scope.content, data);
|
||||
|
||||
}, function(err) {
|
||||
|
||||
//NOTE: in the case of data type values we are setting the orig/new props
|
||||
@@ -171,6 +173,8 @@ function DataTypeEditController($scope, $routeParams, $location, appState, navig
|
||||
|
||||
//share state
|
||||
editorState.set($scope.content);
|
||||
|
||||
dataTypeHelper.rebindChangedProperties($scope.content, data);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
<umb-editor-header
|
||||
menu="page.menu"
|
||||
name="content.name"
|
||||
name-locked="page.nameLocked">
|
||||
name-locked="page.nameLocked"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
|
||||
|
||||
@@ -6,268 +6,268 @@
|
||||
* @description
|
||||
* The controller for the content type editor
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function DocumentTypesEditController($scope, $routeParams, modelsResource, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService) {
|
||||
|
||||
function DocumentTypesEditController($scope, $routeParams, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter) {
|
||||
var vm = this;
|
||||
|
||||
var vm = this;
|
||||
vm.save = save;
|
||||
|
||||
vm.save = save;
|
||||
|
||||
vm.currentNode = null;
|
||||
vm.contentType = {};
|
||||
vm.page = {};
|
||||
vm.page.loading = false;
|
||||
vm.page.saveButtonState = "init";
|
||||
vm.page.navigation = [
|
||||
vm.currentNode = null;
|
||||
vm.contentType = {};
|
||||
vm.page = {};
|
||||
vm.page.loading = false;
|
||||
vm.page.saveButtonState = "init";
|
||||
vm.page.navigation = [
|
||||
{
|
||||
"name": "Design",
|
||||
"icon": "icon-document-dashed-line",
|
||||
"view": "views/documenttypes/views/design/design.html",
|
||||
"active": true
|
||||
"name": "Design",
|
||||
"icon": "icon-document-dashed-line",
|
||||
"view": "views/documenttypes/views/design/design.html",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"name": "List view",
|
||||
"icon": "icon-list",
|
||||
"view": "views/documenttypes/views/listview/listview.html"
|
||||
"name": "List view",
|
||||
"icon": "icon-list",
|
||||
"view": "views/documenttypes/views/listview/listview.html"
|
||||
},
|
||||
{
|
||||
"name": "Permissions",
|
||||
"icon": "icon-keychain",
|
||||
"view": "views/documenttypes/views/permissions/permissions.html"
|
||||
"name": "Permissions",
|
||||
"icon": "icon-keychain",
|
||||
"view": "views/documenttypes/views/permissions/permissions.html"
|
||||
},
|
||||
{
|
||||
"name": "Templates",
|
||||
"icon": "icon-layout",
|
||||
"view": "views/documenttypes/views/templates/templates.html"
|
||||
"name": "Templates",
|
||||
"icon": "icon-layout",
|
||||
"view": "views/documenttypes/views/templates/templates.html"
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
vm.page.keyboardShortcutsOverview = [
|
||||
vm.page.keyboardShortcutsOverview = [
|
||||
{
|
||||
"name": "Sections",
|
||||
"shortcuts": [
|
||||
"name": "Sections",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Navigate sections",
|
||||
"keys": [{"key": "1"}, {"key": "4"}],
|
||||
"keyRange": true
|
||||
"description": "Navigate sections",
|
||||
"keys": [{ "key": "1" }, { "key": "4" }],
|
||||
"keyRange": true
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Design",
|
||||
"shortcuts": [
|
||||
"name": "Design",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Add tab",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "t"}]
|
||||
"description": "Add tab",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
|
||||
},
|
||||
{
|
||||
"description": "Add property",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "p"}]
|
||||
"description": "Add property",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
|
||||
},
|
||||
{
|
||||
"description": "Add editor",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "e"}]
|
||||
"description": "Add editor",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }]
|
||||
},
|
||||
{
|
||||
"description": "Edit data type",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "d"}]
|
||||
"description": "Edit data type",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "List view",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Toggle list view",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "l" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "List view",
|
||||
"shortcuts": [
|
||||
"name": "Permissions",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Toggle list view",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "l"}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Permissions",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Toggle allow as root",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "r"}]
|
||||
"description": "Toggle allow as root",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "r" }]
|
||||
},
|
||||
{
|
||||
"description": "Add child node",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "c"}]
|
||||
"description": "Add child node",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "c" }]
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Templates",
|
||||
"shortcuts": [
|
||||
"name": "Templates",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Add template",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "t"}]
|
||||
"description": "Add template",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
if ($routeParams.create) {
|
||||
if ($routeParams.create) {
|
||||
|
||||
vm.page.loading = true;
|
||||
vm.page.loading = true;
|
||||
|
||||
//we are creating so get an empty data type item
|
||||
contentTypeResource.getScaffold($routeParams.id)
|
||||
.then(function(dt) {
|
||||
//we are creating so get an empty data type item
|
||||
contentTypeResource.getScaffold($routeParams.id)
|
||||
.then(function (dt) {
|
||||
|
||||
init(dt);
|
||||
init(dt);
|
||||
|
||||
vm.page.loading = false;
|
||||
vm.page.loading = false;
|
||||
|
||||
});
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
|
||||
vm.page.loading = true;
|
||||
vm.page.loading = true;
|
||||
|
||||
contentTypeResource.getById($routeParams.id).then(function(dt){
|
||||
init(dt);
|
||||
contentTypeResource.getById($routeParams.id).then(function (dt) {
|
||||
init(dt);
|
||||
|
||||
syncTreeNode(vm.contentType, dt.path, true);
|
||||
syncTreeNode(vm.contentType, dt.path, true);
|
||||
|
||||
vm.page.loading = false;
|
||||
vm.page.loading = false;
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* ---------- SAVE ---------- */
|
||||
/* ---------- SAVE ---------- */
|
||||
|
||||
function save() {
|
||||
function save() {
|
||||
|
||||
// validate form
|
||||
if (formHelper.submitForm({ scope: $scope })) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
formHelper.resetForm({ scope: $scope });
|
||||
vm.page.saveButtonState = "busy";
|
||||
|
||||
// if form validates - perform save
|
||||
performSave();
|
||||
// reformat allowed content types to array if id's
|
||||
vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes);
|
||||
|
||||
}
|
||||
// update placeholder template information on new doc types
|
||||
if (!$routeParams.notemplate && vm.contentType.id === 0) {
|
||||
vm.contentType = contentTypeHelper.updateTemplatePlaceholder(vm.contentType);
|
||||
}
|
||||
|
||||
}
|
||||
contentEditingHelper.contentEditorPerformSave({
|
||||
statusMessage: "Saving...",
|
||||
saveMethod: contentTypeResource.save,
|
||||
scope: $scope,
|
||||
content: vm.contentType,
|
||||
//no-op for rebind callback... we don't really need to rebind for content types
|
||||
rebindCallback: angular.noop
|
||||
}).then(function (data) {
|
||||
//success
|
||||
syncTreeNode(vm.contentType, data.path);
|
||||
|
||||
function performSave() {
|
||||
vm.page.saveButtonState = "success";
|
||||
|
||||
vm.page.saveButtonState = "busy";
|
||||
deferred.resolve(data);
|
||||
}, function (err) {
|
||||
//error
|
||||
if (err) {
|
||||
editorState.set($scope.content);
|
||||
}
|
||||
else {
|
||||
localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) {
|
||||
localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) {
|
||||
notificationsService.error(headerValue, msgValue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// reformat allowed content types to array if id's
|
||||
vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes);
|
||||
vm.page.saveButtonState = "error";
|
||||
|
||||
// update placeholder template information on new doc types
|
||||
if (!$routeParams.notemplate && vm.contentType.id === 0) {
|
||||
vm.contentType = contentTypeHelper.updateTemplatePlaceholder(vm.contentType);
|
||||
}
|
||||
deferred.reject(err);
|
||||
});
|
||||
|
||||
contentTypeResource.save(vm.contentType).then(function(dt){
|
||||
return deferred.promise;
|
||||
|
||||
formHelper.resetForm({ scope: $scope, notifications: dt.notifications });
|
||||
contentEditingHelper.handleSuccessfulSave({
|
||||
scope: $scope,
|
||||
savedContent: dt,
|
||||
rebindCallback: function() {
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
function init(contentType) {
|
||||
|
||||
notificationsService.success("Document type save");
|
||||
//post save logic here -the saved doctype returns as a new object
|
||||
init(dt);
|
||||
// set all tab to inactive
|
||||
if (contentType.groups.length !== 0) {
|
||||
angular.forEach(contentType.groups, function (group) {
|
||||
|
||||
syncTreeNode(vm.contentType, dt.path);
|
||||
angular.forEach(group.properties, function (property) {
|
||||
// get data type details for each property
|
||||
getDataTypeDetails(property);
|
||||
});
|
||||
|
||||
vm.page.saveButtonState = "success";
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
// convert legacy icons
|
||||
convertLegacyIcons(contentType);
|
||||
|
||||
}
|
||||
// sort properties after sort order
|
||||
angular.forEach(contentType.groups, function (group) {
|
||||
group.properties = $filter('orderBy')(group.properties, 'sortOrder');
|
||||
});
|
||||
|
||||
// insert template on new doc types
|
||||
if (!$routeParams.notemplate && contentType.id === 0) {
|
||||
contentType.defaultTemplate = contentTypeHelper.insertDefaultTemplatePlaceholder(contentType.defaultTemplate);
|
||||
contentType.allowedTemplates = contentTypeHelper.insertTemplatePlaceholder(contentType.allowedTemplates);
|
||||
}
|
||||
|
||||
function init(contentType){
|
||||
//set a shared state
|
||||
editorState.set(contentType);
|
||||
|
||||
// set all tab to inactive
|
||||
if( contentType.groups.length !== 0 ) {
|
||||
angular.forEach(contentType.groups, function(group){
|
||||
vm.contentType = contentType;
|
||||
|
||||
angular.forEach(group.properties, function(property){
|
||||
// get data type details for each property
|
||||
getDataTypeDetails(property);
|
||||
}
|
||||
|
||||
function convertLegacyIcons(contentType) {
|
||||
|
||||
// convert icons for composite content types
|
||||
iconHelper.formatContentTypeIcons(contentType.availableCompositeContentTypes);
|
||||
|
||||
// make array to store contentType icon
|
||||
var contentTypeArray = [];
|
||||
|
||||
// push icon to array
|
||||
contentTypeArray.push({ "icon": contentType.icon });
|
||||
|
||||
// run through icon method
|
||||
iconHelper.formatContentTypeIcons(contentTypeArray);
|
||||
|
||||
// set icon back on contentType
|
||||
contentType.icon = contentTypeArray[0].icon;
|
||||
|
||||
}
|
||||
|
||||
function getDataTypeDetails(property) {
|
||||
|
||||
if (property.propertyState !== "init") {
|
||||
|
||||
dataTypeResource.getById(property.dataTypeId)
|
||||
.then(function (dataType) {
|
||||
property.dataTypeIcon = dataType.icon;
|
||||
property.dataTypeName = dataType.name;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// convert legacy icons
|
||||
convertLegacyIcons(contentType);
|
||||
|
||||
// sort properties after sort order
|
||||
angular.forEach(contentType.groups, function(group){
|
||||
group.properties = $filter('orderBy')(group.properties, 'sortOrder');
|
||||
});
|
||||
|
||||
// insert template on new doc types
|
||||
if (!$routeParams.notemplate && contentType.id === 0) {
|
||||
contentType.defaultTemplate = contentTypeHelper.insertDefaultTemplatePlaceholder(contentType.defaultTemplate);
|
||||
contentType.allowedTemplates = contentTypeHelper.insertTemplatePlaceholder(contentType.allowedTemplates);
|
||||
}
|
||||
|
||||
//set a shared state
|
||||
editorState.set(contentType);
|
||||
|
||||
vm.contentType = contentType;
|
||||
|
||||
}
|
||||
|
||||
function convertLegacyIcons(contentType) {
|
||||
|
||||
// convert icons for composite content types
|
||||
iconHelper.formatContentTypeIcons(contentType.availableCompositeContentTypes);
|
||||
|
||||
// make array to store contentType icon
|
||||
var contentTypeArray = [];
|
||||
|
||||
// push icon to array
|
||||
contentTypeArray.push({"icon":contentType.icon});
|
||||
|
||||
// run through icon method
|
||||
iconHelper.formatContentTypeIcons(contentTypeArray);
|
||||
|
||||
// set icon back on contentType
|
||||
contentType.icon = contentTypeArray[0].icon;
|
||||
|
||||
}
|
||||
|
||||
function getDataTypeDetails(property) {
|
||||
|
||||
if( property.propertyState !== "init" ) {
|
||||
|
||||
dataTypeResource.getById(property.dataTypeId)
|
||||
.then(function(dataType) {
|
||||
property.dataTypeIcon = dataType.icon;
|
||||
property.dataTypeName = dataType.name;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Syncs the content type to it's tree node - this occurs on first load and after saving */
|
||||
function syncTreeNode(dt, path, initialLoad) {
|
||||
/** Syncs the content type to it's tree node - this occurs on first load and after saving */
|
||||
function syncTreeNode(dt, path, initialLoad) {
|
||||
|
||||
navigationService.syncTree({ tree: "documenttypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
|
||||
vm.currentNode = syncArgs.node;
|
||||
});
|
||||
navigationService.syncTree({ tree: "documenttypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
|
||||
vm.currentNode = syncArgs.node;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.EditController", DocumentTypesEditController);
|
||||
angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.EditController", DocumentTypesEditController);
|
||||
|
||||
})();
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
<umb-editor-header
|
||||
name="content.name"
|
||||
tabs="content.tabs"
|
||||
menu="page.menu">
|
||||
menu="page.menu"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
<umb-editor-header
|
||||
name="page.name"
|
||||
name-locked="page.nameLocked">
|
||||
name-locked="page.nameLocked"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
@@ -6,240 +6,240 @@
|
||||
* @description
|
||||
* The controller for the media type editor
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function MediaTypesEditController($scope, $routeParams, mediaTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter) {
|
||||
function MediaTypesEditController($scope, $routeParams, mediaTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService) {
|
||||
|
||||
var vm = this;
|
||||
var vm = this;
|
||||
|
||||
vm.save = save;
|
||||
vm.save = save;
|
||||
|
||||
vm.currentNode = null;
|
||||
vm.contentType = {};
|
||||
vm.page = {};
|
||||
vm.page.loading = false;
|
||||
vm.page.saveButtonState = "init";
|
||||
vm.page.navigation = [
|
||||
vm.currentNode = null;
|
||||
vm.contentType = {};
|
||||
vm.page = {};
|
||||
vm.page.loading = false;
|
||||
vm.page.saveButtonState = "init";
|
||||
vm.page.navigation = [
|
||||
{
|
||||
"name": "Design",
|
||||
"icon": "icon-document-dashed-line",
|
||||
"view": "views/mediatypes/views/design/design.html",
|
||||
"active": true
|
||||
"name": "Design",
|
||||
"icon": "icon-document-dashed-line",
|
||||
"view": "views/mediatypes/views/design/design.html",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"name": "List view",
|
||||
"icon": "icon-list",
|
||||
"view": "views/mediatypes/views/listview/listview.html"
|
||||
"name": "List view",
|
||||
"icon": "icon-list",
|
||||
"view": "views/mediatypes/views/listview/listview.html"
|
||||
},
|
||||
{
|
||||
"name": "Permissions",
|
||||
"icon": "icon-keychain",
|
||||
"view": "views/mediatypes/views/permissions/permissions.html"
|
||||
"name": "Permissions",
|
||||
"icon": "icon-keychain",
|
||||
"view": "views/mediatypes/views/permissions/permissions.html"
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
vm.page.keyboardShortcutsOverview = [
|
||||
vm.page.keyboardShortcutsOverview = [
|
||||
{
|
||||
"name": "Sections",
|
||||
"shortcuts": [
|
||||
"name": "Sections",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Navigate sections",
|
||||
"keys": [{"key": "1"}, {"key": "3"}],
|
||||
"keyRange": true
|
||||
"description": "Navigate sections",
|
||||
"keys": [{ "key": "1" }, { "key": "3" }],
|
||||
"keyRange": true
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Design",
|
||||
"shortcuts": [
|
||||
"name": "Design",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Add tab",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "t"}]
|
||||
"description": "Add tab",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
|
||||
},
|
||||
{
|
||||
"description": "Add property",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "p"}]
|
||||
"description": "Add property",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
|
||||
},
|
||||
{
|
||||
"description": "Add editor",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "e"}]
|
||||
"description": "Add editor",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }]
|
||||
},
|
||||
{
|
||||
"description": "Edit data type",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "d"}]
|
||||
"description": "Edit data type",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "List view",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Toggle list view",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "l" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "List view",
|
||||
"shortcuts": [
|
||||
"name": "Permissions",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Toggle list view",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "l"}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Permissions",
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Toggle allow as root",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "r"}]
|
||||
"description": "Toggle allow as root",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "r" }]
|
||||
},
|
||||
{
|
||||
"description": "Add child node",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "c"}]
|
||||
"description": "Add child node",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "c" }]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
if ($routeParams.create) {
|
||||
if ($routeParams.create) {
|
||||
|
||||
vm.page.loading = true;
|
||||
vm.page.loading = true;
|
||||
|
||||
//we are creating so get an empty data type item
|
||||
mediaTypeResource.getScaffold($routeParams.id)
|
||||
.then(function(dt) {
|
||||
init(dt);
|
||||
//we are creating so get an empty data type item
|
||||
mediaTypeResource.getScaffold($routeParams.id)
|
||||
.then(function (dt) {
|
||||
init(dt);
|
||||
|
||||
vm.page.loading = false;
|
||||
vm.page.loading = false;
|
||||
});
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
|
||||
vm.page.loading = true;
|
||||
vm.page.loading = true;
|
||||
|
||||
mediaTypeResource.getById($routeParams.id).then(function(dt){
|
||||
init(dt);
|
||||
mediaTypeResource.getById($routeParams.id).then(function (dt) {
|
||||
init(dt);
|
||||
|
||||
syncTreeNode(vm.contentType, dt.path, true);
|
||||
syncTreeNode(vm.contentType, dt.path, true);
|
||||
|
||||
vm.page.loading = false;
|
||||
});
|
||||
}
|
||||
vm.page.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* ---------- SAVE ---------- */
|
||||
/* ---------- SAVE ---------- */
|
||||
|
||||
function save() {
|
||||
function save() {
|
||||
|
||||
// validate form
|
||||
if (formHelper.submitForm({ scope: $scope })) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
formHelper.resetForm({ scope: $scope });
|
||||
vm.page.saveButtonState = "busy";
|
||||
|
||||
// if form validates - perform save
|
||||
performSave();
|
||||
// reformat allowed content types to array if id's
|
||||
vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes);
|
||||
|
||||
}
|
||||
contentEditingHelper.contentEditorPerformSave({
|
||||
statusMessage: "Saving...",
|
||||
saveMethod: mediaTypeResource.save,
|
||||
scope: $scope,
|
||||
content: vm.contentType,
|
||||
//no-op for rebind callback... we don't really need to rebind for content types
|
||||
rebindCallback: angular.noop
|
||||
}).then(function (data) {
|
||||
//success
|
||||
syncTreeNode(vm.contentType, data.path);
|
||||
|
||||
}
|
||||
vm.page.saveButtonState = "success";
|
||||
|
||||
function performSave() {
|
||||
deferred.resolve(data);
|
||||
}, function (err) {
|
||||
//error
|
||||
if (err) {
|
||||
editorState.set($scope.content);
|
||||
}
|
||||
else {
|
||||
localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) {
|
||||
localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) {
|
||||
notificationsService.error(headerValue, msgValue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
vm.page.saveButtonState = "busy";
|
||||
vm.page.saveButtonState = "error";
|
||||
|
||||
// reformat allowed content types to array if id's
|
||||
vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes);
|
||||
deferred.reject(err);
|
||||
});
|
||||
|
||||
mediaTypeResource.save(vm.contentType).then(function(dt){
|
||||
return deferred.promise;
|
||||
|
||||
formHelper.resetForm({ scope: $scope, notifications: dt.notifications });
|
||||
contentEditingHelper.handleSuccessfulSave({
|
||||
scope: $scope,
|
||||
savedContent: dt,
|
||||
rebindCallback: function() {
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
function init(contentType) {
|
||||
|
||||
notificationsService.success("Media type saved");
|
||||
//post save logic here -the saved doctype returns as a new object
|
||||
init(dt);
|
||||
// set all tab to inactive
|
||||
if (contentType.groups.length !== 0) {
|
||||
angular.forEach(contentType.groups, function (group) {
|
||||
|
||||
syncTreeNode(vm.contentType, dt.path);
|
||||
angular.forEach(group.properties, function (property) {
|
||||
// get data type details for each property
|
||||
getDataTypeDetails(property);
|
||||
});
|
||||
|
||||
vm.page.saveButtonState = "success";
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
// convert legacy icons
|
||||
convertLegacyIcons(contentType);
|
||||
|
||||
}
|
||||
// sort properties after sort order
|
||||
angular.forEach(contentType.groups, function (group) {
|
||||
group.properties = $filter('orderBy')(group.properties, 'sortOrder');
|
||||
});
|
||||
|
||||
//set a shared state
|
||||
editorState.set(contentType);
|
||||
|
||||
function init(contentType){
|
||||
vm.contentType = contentType;
|
||||
|
||||
// set all tab to inactive
|
||||
if( contentType.groups.length !== 0 ) {
|
||||
angular.forEach(contentType.groups, function(group){
|
||||
}
|
||||
|
||||
angular.forEach(group.properties, function(property){
|
||||
// get data type details for each property
|
||||
getDataTypeDetails(property);
|
||||
function convertLegacyIcons(contentType) {
|
||||
|
||||
// convert icons for composite content types
|
||||
iconHelper.formatContentTypeIcons(contentType.availableCompositeContentTypes);
|
||||
|
||||
// make array to store contentType icon
|
||||
var contentTypeArray = [];
|
||||
|
||||
// push icon to array
|
||||
contentTypeArray.push({ "icon": contentType.icon });
|
||||
|
||||
// run through icon method
|
||||
iconHelper.formatContentTypeIcons(contentTypeArray);
|
||||
|
||||
// set icon back on contentType
|
||||
contentType.icon = contentTypeArray[0].icon;
|
||||
|
||||
}
|
||||
|
||||
function getDataTypeDetails(property) {
|
||||
|
||||
if (property.propertyState !== "init") {
|
||||
|
||||
dataTypeResource.getById(property.dataTypeId)
|
||||
.then(function (dataType) {
|
||||
property.dataTypeIcon = dataType.icon;
|
||||
property.dataTypeName = dataType.name;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// convert legacy icons
|
||||
convertLegacyIcons(contentType);
|
||||
|
||||
// sort properties after sort order
|
||||
angular.forEach(contentType.groups, function(group){
|
||||
group.properties = $filter('orderBy')(group.properties, 'sortOrder');
|
||||
});
|
||||
|
||||
//set a shared state
|
||||
editorState.set(contentType);
|
||||
|
||||
vm.contentType = contentType;
|
||||
|
||||
}
|
||||
|
||||
function convertLegacyIcons(contentType) {
|
||||
|
||||
// convert icons for composite content types
|
||||
iconHelper.formatContentTypeIcons(contentType.availableCompositeContentTypes);
|
||||
|
||||
// make array to store contentType icon
|
||||
var contentTypeArray = [];
|
||||
|
||||
// push icon to array
|
||||
contentTypeArray.push({"icon":contentType.icon});
|
||||
|
||||
// run through icon method
|
||||
iconHelper.formatContentTypeIcons(contentTypeArray);
|
||||
|
||||
// set icon back on contentType
|
||||
contentType.icon = contentTypeArray[0].icon;
|
||||
|
||||
}
|
||||
|
||||
function getDataTypeDetails(property) {
|
||||
|
||||
if( property.propertyState !== "init" ) {
|
||||
|
||||
dataTypeResource.getById(property.dataTypeId)
|
||||
.then(function(dataType) {
|
||||
property.dataTypeIcon = dataType.icon;
|
||||
property.dataTypeName = dataType.name;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Syncs the content type to it's tree node - this occurs on first load and after saving */
|
||||
function syncTreeNode(dt, path, initialLoad) {
|
||||
/** Syncs the content type to it's tree node - this occurs on first load and after saving */
|
||||
function syncTreeNode(dt, path, initialLoad) {
|
||||
|
||||
navigationService.syncTree({ tree: "mediatypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
|
||||
vm.currentNode = syncArgs.node;
|
||||
});
|
||||
navigationService.syncTree({ tree: "mediatypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
|
||||
vm.currentNode = syncArgs.node;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.EditController", MediaTypesEditController);
|
||||
angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.EditController", MediaTypesEditController);
|
||||
|
||||
})();
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
name="content.name"
|
||||
name-locked="page.nameLocked"
|
||||
tabs="content.tabs"
|
||||
menu="page.menu">
|
||||
menu="page.menu"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
name="content.name"
|
||||
name-locked="page.lockedName"
|
||||
tabs="content.tabs"
|
||||
menu="page.menu">
|
||||
menu="page.menu"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
@@ -6,188 +6,188 @@
|
||||
* @description
|
||||
* The controller for the member type editor
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function MemberTypesEditController($scope, $rootScope, $routeParams, $log, $filter, memberTypeResource, dataTypeResource, editorState, iconHelper, formHelper, navigationService, contentEditingHelper, notificationsService) {
|
||||
function MemberTypesEditController($scope, $rootScope, $routeParams, $log, $filter, memberTypeResource, dataTypeResource, editorState, iconHelper, formHelper, navigationService, contentEditingHelper, notificationsService, $q, localizationService) {
|
||||
|
||||
var vm = this;
|
||||
var vm = this;
|
||||
|
||||
vm.save = save;
|
||||
vm.save = save;
|
||||
|
||||
vm.currentNode = null;
|
||||
vm.contentType = {};
|
||||
vm.page = {};
|
||||
vm.currentNode = null;
|
||||
vm.contentType = {};
|
||||
vm.page = {};
|
||||
vm.page.loading = false;
|
||||
vm.page.saveButtonState = "init";
|
||||
vm.page.navigation = [
|
||||
vm.page.navigation = [
|
||||
{
|
||||
"name": "Design",
|
||||
"icon": "icon-document-dashed-line",
|
||||
"view": "views/membertypes/views/design/design.html",
|
||||
"active": true
|
||||
"name": "Design",
|
||||
"icon": "icon-document-dashed-line",
|
||||
"view": "views/membertypes/views/design/design.html",
|
||||
"active": true
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
vm.page.keyboardShortcutsOverview = [
|
||||
vm.page.keyboardShortcutsOverview = [
|
||||
{
|
||||
"shortcuts": [
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": "Add tab",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "t"}]
|
||||
"description": "Add tab",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
|
||||
},
|
||||
{
|
||||
"description": "Add property",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "p"}]
|
||||
"description": "Add property",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
|
||||
},
|
||||
{
|
||||
"description": "Add editor",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "e"}]
|
||||
"description": "Add editor",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }]
|
||||
},
|
||||
{
|
||||
"description": "Edit data type",
|
||||
"keys": [{"key": "alt"},{"key": "shift"},{"key": "d"}]
|
||||
"description": "Edit data type",
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
if ($routeParams.create) {
|
||||
if ($routeParams.create) {
|
||||
|
||||
vm.page.loading = true;
|
||||
vm.page.loading = true;
|
||||
|
||||
//we are creating so get an empty data type item
|
||||
memberTypeResource.getScaffold($routeParams.id)
|
||||
.then(function(dt) {
|
||||
init(dt);
|
||||
//we are creating so get an empty data type item
|
||||
memberTypeResource.getScaffold($routeParams.id)
|
||||
.then(function (dt) {
|
||||
init(dt);
|
||||
|
||||
vm.page.loading = false;
|
||||
vm.page.loading = false;
|
||||
});
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
|
||||
vm.page.loading = true;
|
||||
vm.page.loading = true;
|
||||
|
||||
memberTypeResource.getById($routeParams.id).then(function(dt){
|
||||
init(dt);
|
||||
memberTypeResource.getById($routeParams.id).then(function (dt) {
|
||||
init(dt);
|
||||
|
||||
syncTreeNode(vm.contentType, dt.path, true);
|
||||
syncTreeNode(vm.contentType, dt.path, true);
|
||||
|
||||
vm.page.loading = false;
|
||||
});
|
||||
}
|
||||
vm.page.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
function save() {
|
||||
function save() {
|
||||
|
||||
// validate form
|
||||
if (formHelper.submitForm({ scope: $scope })) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
formHelper.resetForm({ scope: $scope });
|
||||
vm.page.saveButtonState = "busy";
|
||||
|
||||
contentEditingHelper.contentEditorPerformSave({
|
||||
statusMessage: "Saving...",
|
||||
saveMethod: memberTypeResource.save,
|
||||
scope: $scope,
|
||||
content: vm.contentType,
|
||||
//no-op for rebind callback... we don't really need to rebind for content types
|
||||
rebindCallback: angular.noop
|
||||
}).then(function (data) {
|
||||
//success
|
||||
syncTreeNode(vm.contentType, data.path);
|
||||
|
||||
// if form validates - perform save
|
||||
performSave();
|
||||
vm.page.saveButtonState = "success";
|
||||
|
||||
}
|
||||
deferred.resolve(data);
|
||||
}, function (err) {
|
||||
//error
|
||||
if (err) {
|
||||
editorState.set($scope.content);
|
||||
}
|
||||
else {
|
||||
localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) {
|
||||
localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) {
|
||||
notificationsService.error(headerValue, msgValue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
vm.page.saveButtonState = "error";
|
||||
|
||||
function performSave() {
|
||||
deferred.reject(err);
|
||||
});
|
||||
|
||||
vm.page.saveButtonState = "busy";
|
||||
return deferred.promise;
|
||||
|
||||
memberTypeResource.save(vm.contentType).then(function(dt){
|
||||
}
|
||||
|
||||
formHelper.resetForm({ scope: $scope, notifications: dt.notifications });
|
||||
contentEditingHelper.handleSuccessfulSave({
|
||||
scope: $scope,
|
||||
savedContent: dt,
|
||||
rebindCallback: function() {
|
||||
function init(contentType) {
|
||||
|
||||
}
|
||||
});
|
||||
// set all tab to inactive
|
||||
if (contentType.groups.length !== 0) {
|
||||
angular.forEach(contentType.groups, function (group) {
|
||||
|
||||
notificationsService.success("Member type saved");
|
||||
//post save logic here -the saved doctype returns as a new object
|
||||
init(dt);
|
||||
angular.forEach(group.properties, function (property) {
|
||||
// get data type details for each property
|
||||
getDataTypeDetails(property);
|
||||
});
|
||||
|
||||
syncTreeNode(vm.contentType, dt.path);
|
||||
});
|
||||
}
|
||||
|
||||
vm.page.saveButtonState = "success";
|
||||
// convert legacy icons
|
||||
convertLegacyIcons(contentType);
|
||||
|
||||
// sort properties after sort order
|
||||
angular.forEach(contentType.groups, function (group) {
|
||||
group.properties = $filter('orderBy')(group.properties, 'sortOrder');
|
||||
});
|
||||
|
||||
});
|
||||
//set a shared state
|
||||
editorState.set(contentType);
|
||||
|
||||
}
|
||||
vm.contentType = contentType;
|
||||
|
||||
function init(contentType){
|
||||
}
|
||||
|
||||
// set all tab to inactive
|
||||
if( contentType.groups.length !== 0 ) {
|
||||
angular.forEach(contentType.groups, function(group){
|
||||
function convertLegacyIcons(contentType) {
|
||||
|
||||
angular.forEach(group.properties, function(property){
|
||||
// get data type details for each property
|
||||
getDataTypeDetails(property);
|
||||
// make array to store contentType icon
|
||||
var contentTypeArray = [];
|
||||
|
||||
// push icon to array
|
||||
contentTypeArray.push({ "icon": contentType.icon });
|
||||
|
||||
// run through icon method
|
||||
iconHelper.formatContentTypeIcons(contentTypeArray);
|
||||
|
||||
// set icon back on contentType
|
||||
contentType.icon = contentTypeArray[0].icon;
|
||||
|
||||
}
|
||||
|
||||
function getDataTypeDetails(property) {
|
||||
|
||||
if (property.propertyState !== "init") {
|
||||
|
||||
dataTypeResource.getById(property.dataTypeId)
|
||||
.then(function (dataType) {
|
||||
property.dataTypeIcon = dataType.icon;
|
||||
property.dataTypeName = dataType.name;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
/** Syncs the content type to it's tree node - this occurs on first load and after saving */
|
||||
function syncTreeNode(dt, path, initialLoad) {
|
||||
|
||||
// convert legacy icons
|
||||
convertLegacyIcons(contentType);
|
||||
navigationService.syncTree({ tree: "membertypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
|
||||
vm.currentNode = syncArgs.node;
|
||||
});
|
||||
|
||||
// sort properties after sort order
|
||||
angular.forEach(contentType.groups, function(group){
|
||||
group.properties = $filter('orderBy')(group.properties, 'sortOrder');
|
||||
});
|
||||
|
||||
//set a shared state
|
||||
editorState.set(contentType);
|
||||
|
||||
vm.contentType = contentType;
|
||||
|
||||
}
|
||||
|
||||
function convertLegacyIcons(contentType) {
|
||||
|
||||
// make array to store contentType icon
|
||||
var contentTypeArray = [];
|
||||
|
||||
// push icon to array
|
||||
contentTypeArray.push({"icon":contentType.icon});
|
||||
|
||||
// run through icon method
|
||||
iconHelper.formatContentTypeIcons(contentTypeArray);
|
||||
|
||||
// set icon back on contentType
|
||||
contentType.icon = contentTypeArray[0].icon;
|
||||
|
||||
}
|
||||
|
||||
function getDataTypeDetails(property) {
|
||||
|
||||
if( property.propertyState !== "init" ) {
|
||||
|
||||
dataTypeResource.getById(property.dataTypeId)
|
||||
.then(function(dataType) {
|
||||
property.dataTypeIcon = dataType.icon;
|
||||
property.dataTypeName = dataType.name;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Syncs the content type to it's tree node - this occurs on first load and after saving */
|
||||
function syncTreeNode(dt, path, initialLoad) {
|
||||
|
||||
navigationService.syncTree({ tree: "membertypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
|
||||
vm.currentNode = syncArgs.node;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.EditController", MemberTypesEditController);
|
||||
angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.EditController", MemberTypesEditController);
|
||||
|
||||
})();
|
||||
|
||||
@@ -5,7 +5,7 @@ describe('edit content controller tests', function () {
|
||||
beforeEach(module('umbraco'));
|
||||
|
||||
//inject the contentMocks service
|
||||
beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, contentMocks, entityMocks, mocksUtils) {
|
||||
beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, contentMocks, entityMocks, mocksUtils, localizationMocks) {
|
||||
|
||||
//for these tests we don't want any authorization to occur
|
||||
mocksUtils.disableAuth();
|
||||
@@ -17,6 +17,7 @@ describe('edit content controller tests', function () {
|
||||
//see /mocks/content.mocks.js for how its setup
|
||||
contentMocks.register();
|
||||
entityMocks.register();
|
||||
localizationMocks.register();
|
||||
|
||||
//this controller requires an angular form controller applied to it
|
||||
scope.contentForm = angularHelper.getNullForm("contentForm");
|
||||
|
||||
@@ -5,7 +5,7 @@ describe('edit media controller tests', function () {
|
||||
beforeEach(module('umbraco'));
|
||||
|
||||
//inject the contentMocks service
|
||||
beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, mediaMocks, entityMocks, mocksUtils) {
|
||||
beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, mediaMocks, entityMocks, mocksUtils, localizationMocks) {
|
||||
//for these tests we don't want any authorization to occur
|
||||
mocksUtils.disableAuth();
|
||||
|
||||
@@ -16,6 +16,7 @@ describe('edit media controller tests', function () {
|
||||
//see /mocks/content.mocks.js for how its setup
|
||||
mediaMocks.register();
|
||||
entityMocks.register();
|
||||
localizationMocks.register();
|
||||
|
||||
//this controller requires an angular form controller applied to it
|
||||
scope.contentForm = angularHelper.getNullForm("contentForm");
|
||||
|
||||
+2
-1
@@ -5,7 +5,7 @@ describe('Content picker controller tests', function () {
|
||||
beforeEach(module('umbraco'));
|
||||
|
||||
//inject the contentMocks service
|
||||
beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, entityMocks, mocksUtils) {
|
||||
beforeEach(inject(function ($rootScope, $controller, angularHelper, $httpBackend, entityMocks, mocksUtils, localizationMocks) {
|
||||
|
||||
//for these tests we don't want any authorization to occur
|
||||
mocksUtils.disableAuth();
|
||||
@@ -28,6 +28,7 @@ describe('Content picker controller tests', function () {
|
||||
//have the contentMocks register its expect urls on the httpbackend
|
||||
//see /mocks/content.mocks.js for how its setup
|
||||
entityMocks.register();
|
||||
localizationMocks.register();
|
||||
|
||||
controller = $controller('Umbraco.PropertyEditors.ContentPickerController', {
|
||||
$scope: scope,
|
||||
|
||||
@@ -8,7 +8,9 @@ describe('contentEditingHelper tests', function () {
|
||||
//Only for 1.2: beforeEach(module('ngRoute'));
|
||||
|
||||
|
||||
beforeEach(inject(function ($injector) {
|
||||
beforeEach(inject(function ($injector, localizationMocks) {
|
||||
localizationMocks.register();
|
||||
|
||||
contentEditingHelper = $injector.get('contentEditingHelper');
|
||||
$routeParams = $injector.get('$routeParams');
|
||||
serverValidationManager = $injector.get('serverValidationManager');
|
||||
@@ -108,7 +110,7 @@ describe('contentEditingHelper tests', function () {
|
||||
var allProps = contentEditingHelper.getAllProps(content);
|
||||
|
||||
//act
|
||||
formHelper.handleServerValidation({ "Property.bodyText": ["Required"] });
|
||||
formHelper.handleServerValidation({ "_Properties.bodyText": ["Required"] });
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.items.length).toBe(1);
|
||||
@@ -124,7 +126,7 @@ describe('contentEditingHelper tests', function () {
|
||||
var allProps = contentEditingHelper.getAllProps(content);
|
||||
|
||||
//act
|
||||
formHelper.handleServerValidation({ "Property.bodyText.value": ["Required"] });
|
||||
formHelper.handleServerValidation({ "_Properties.bodyText.value": ["Required"] });
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.items.length).toBe(1);
|
||||
@@ -144,8 +146,8 @@ describe('contentEditingHelper tests', function () {
|
||||
{
|
||||
"Name": ["Required"],
|
||||
"UpdateDate": ["Invalid date"],
|
||||
"Property.bodyText.value": ["Required field"],
|
||||
"Property.textarea": ["Invalid format"]
|
||||
"_Properties.bodyText.value": ["Required field"],
|
||||
"_Properties.textarea": ["Invalid format"]
|
||||
});
|
||||
|
||||
//assert
|
||||
|
||||
@@ -785,6 +785,8 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="sortPleaseWait"><![CDATA[ Please wait. Items are being sorted, this can take a while.<br/> <br/> Do not close this window during sorting]]></key>
|
||||
</area>
|
||||
<area alias="speechBubbles">
|
||||
<key alias="validationFailedHeader">Validation</key>
|
||||
<key alias="validationFailedMessage">Validation errors must be fixed before the item can be saved</key>
|
||||
<key alias="operationFailedHeader">Failed</key>
|
||||
<key alias="invalidUserPermissionsText">Insufficient user permissions, could not complete the operation</key>
|
||||
<key alias="operationCancelledHeader">Cancelled</key>
|
||||
|
||||
@@ -785,6 +785,8 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="sortPleaseWait"><![CDATA[ Please wait. Items are being sorted, this can take a while.<br/> <br/> Do not close this window during sorting]]></key>
|
||||
</area>
|
||||
<area alias="speechBubbles">
|
||||
<key alias="validationFailedHeader">Validation</key>
|
||||
<key alias="validationFailedMessage">Validation errors must be fixed before the item can be saved</key>
|
||||
<key alias="operationFailedHeader">Failed</key>
|
||||
<key alias="invalidUserPermissionsText">Insufficient user permissions, could not complete the operation</key>
|
||||
<key alias="operationCancelledHeader">Cancelled</key>
|
||||
|
||||
@@ -16,7 +16,7 @@ using Newtonsoft.Json;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -81,6 +81,36 @@ namespace Umbraco.Web.Editors
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all user defined properties.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<string> GetAllPropertyTypeAliases()
|
||||
{
|
||||
return ApplicationContext.Services.ContentTypeService.GetAllPropertyTypeAliases();
|
||||
}
|
||||
|
||||
public ContentPropertyDisplay GetPropertyTypeScaffold(int id)
|
||||
{
|
||||
var dataTypeDiff = Services.DataTypeService.GetDataTypeDefinitionById(id);
|
||||
|
||||
if (dataTypeDiff == null)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
var preVals = UmbracoContext.Current.Application.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(id);
|
||||
var editor = PropertyEditorResolver.Current.GetByAlias(dataTypeDiff.PropertyEditorAlias);
|
||||
|
||||
return new ContentPropertyDisplay()
|
||||
{
|
||||
Editor = dataTypeDiff.PropertyEditorAlias,
|
||||
Validation = new PropertyTypeValidation() { },
|
||||
View = editor.ValueEditor.View,
|
||||
Config = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a document type container wth a given ID
|
||||
/// </summary>
|
||||
@@ -90,19 +120,21 @@ namespace Umbraco.Web.Editors
|
||||
[HttpPost]
|
||||
public HttpResponseMessage DeleteContainerById(int id)
|
||||
{
|
||||
//TODO: This needs to be implemented correctly
|
||||
|
||||
var foundType = Services.EntityService.Get(id);
|
||||
if (foundType == null)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
if(foundType.HasChildren())
|
||||
if (foundType.HasChildren())
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
//TODO: what service to use to delete?
|
||||
|
||||
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
@@ -115,109 +147,39 @@ namespace Umbraco.Web.Editors
|
||||
: Request.CreateValidationErrorResponse(result.Exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move a content type to a container
|
||||
/// </summary>
|
||||
/// <param name="move"></param>
|
||||
/// <returns></returns>
|
||||
public HttpResponseMessage PostMove(MoveOrCopy move)
|
||||
|
||||
|
||||
public ContentTypeDisplay PostSave(ContentTypeSave contentTypeSave)
|
||||
{
|
||||
//TODO, validate move
|
||||
|
||||
//TODO, service method for moving
|
||||
|
||||
var response = Request.CreateResponse(HttpStatusCode.OK);
|
||||
|
||||
//TODO, response
|
||||
response.Content = new StringContent("", Encoding.UTF8, "application/json");
|
||||
return response;
|
||||
}
|
||||
|
||||
public ContentTypeDisplay PostSave(ContentTypeDisplay contentType)
|
||||
{
|
||||
var ctService = Services.ContentTypeService;
|
||||
|
||||
//TODO: warn on content type alias conflicts
|
||||
//TODO: warn on property alias conflicts
|
||||
|
||||
//TODO: Validate the submitted model
|
||||
|
||||
var ctId = Convert.ToInt32(contentType.Id);
|
||||
|
||||
//filter out empty properties
|
||||
contentType.Groups = contentType.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList();
|
||||
foreach (var group in contentType.Groups)
|
||||
{
|
||||
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
|
||||
}
|
||||
|
||||
if (ctId > 0)
|
||||
{
|
||||
//its an update to an existing
|
||||
IContentType found = ctService.GetContentType(ctId);
|
||||
if (found == null)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
Mapper.Map(contentType, found);
|
||||
ctService.Save(found);
|
||||
|
||||
//map the saved item back to the content type (it should now get id etc set)
|
||||
Mapper.Map(found, contentType);
|
||||
return contentType;
|
||||
}
|
||||
else
|
||||
{
|
||||
//ensure alias is set
|
||||
if (string.IsNullOrEmpty(contentType.Alias))
|
||||
contentType.Alias = contentType.Name.ToSafeAlias();
|
||||
|
||||
//set id to null to ensure its handled as a new type
|
||||
contentType.Id = null;
|
||||
contentType.CreateDate = DateTime.Now;
|
||||
contentType.UpdateDate = DateTime.Now;
|
||||
|
||||
|
||||
//create a default template if it doesnt exist -but only if default template is == to the content type
|
||||
if (contentType.DefaultTemplate != null && contentType.DefaultTemplate.Alias == contentType.Alias)
|
||||
var savedCt = PerformPostSave<IContentType, ContentTypeDisplay>(
|
||||
contentTypeSave: contentTypeSave,
|
||||
getContentType: i => Services.ContentTypeService.GetContentType(i),
|
||||
saveContentType: type => Services.ContentTypeService.Save(type),
|
||||
beforeCreateNew: ctSave =>
|
||||
{
|
||||
var template = Services.FileService.GetTemplate(contentType.Alias);
|
||||
if (template == null)
|
||||
//create a default template if it doesnt exist -but only if default template is == to the content type
|
||||
//TODO: Is this really what we want? What if we don't want any template assigned at all ?
|
||||
if (ctSave.DefaultTemplate.IsNullOrWhiteSpace() == false && ctSave.DefaultTemplate == ctSave.Alias)
|
||||
{
|
||||
template = new Template(contentType.Name, contentType.Alias);
|
||||
Services.FileService.SaveTemplate(template);
|
||||
var template = Services.FileService.GetTemplate(ctSave.Alias);
|
||||
if (template == null)
|
||||
{
|
||||
template = new Template(ctSave.Name, ctSave.Alias);
|
||||
Services.FileService.SaveTemplate(template);
|
||||
}
|
||||
|
||||
//make sure the template alias is set on the default and allowed template so we can map it back
|
||||
ctSave.DefaultTemplate = template.Alias;
|
||||
}
|
||||
});
|
||||
|
||||
//make sure the template id is set on the default and allowed template
|
||||
contentType.DefaultTemplate.Id = template.Id;
|
||||
var found = contentType.AllowedTemplates.FirstOrDefault(x => x.Alias == contentType.Alias);
|
||||
if (found != null)
|
||||
found.Id = template.Id;
|
||||
}
|
||||
var display = Mapper.Map<ContentTypeDisplay>(savedCt);
|
||||
|
||||
//check if the type is trying to allow type 0 below itself - id zero refers to the currently unsaved type
|
||||
//always filter these 0 types out
|
||||
var allowItselfAsChild = false;
|
||||
if (contentType.AllowedContentTypes != null)
|
||||
{
|
||||
allowItselfAsChild = contentType.AllowedContentTypes.Any(x => x == 0);
|
||||
contentType.AllowedContentTypes = contentType.AllowedContentTypes.Where(x => x > 0).ToList();
|
||||
}
|
||||
display.AddSuccessNotification(
|
||||
Services.TextService.Localize("speechBubbles/contentTypeSavedHeader"),
|
||||
string.Empty);
|
||||
|
||||
//save as new
|
||||
var newCt = Mapper.Map<IContentType>(contentType);
|
||||
ctService.Save(newCt);
|
||||
|
||||
//we need to save it twice to allow itself under itself.
|
||||
if (allowItselfAsChild)
|
||||
{
|
||||
newCt.AddContentType(newCt);
|
||||
ctService.Save(newCt);
|
||||
}
|
||||
|
||||
//map the saved item back to the content type (it should now get id etc set)
|
||||
Mapper.Map(newCt, contentType);
|
||||
return contentType;
|
||||
}
|
||||
return display;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -296,6 +258,6 @@ namespace Umbraco.Web.Editors
|
||||
return basics;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
@@ -22,6 +23,7 @@ namespace Umbraco.Web.Editors
|
||||
/// Am abstract API controller providing functionality used for dealing with content and media types
|
||||
/// </summary>
|
||||
[PluginController("UmbracoApi")]
|
||||
[PrefixlessBodyModelValidator]
|
||||
public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController
|
||||
{
|
||||
private ICultureDictionary _cultureDictionary;
|
||||
@@ -43,7 +45,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
}
|
||||
|
||||
public DataTypeBasic GetAssignedListViewDataType(int contentTypeId)
|
||||
protected internal DataTypeBasic GetAssignedListViewDataType(int contentTypeId)
|
||||
{
|
||||
var objectType = Services.EntityService.GetObjectType(contentTypeId);
|
||||
|
||||
@@ -76,49 +78,41 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all user defined properties.
|
||||
/// Validates the composition and adds errors to the model state if any are found then throws an error response if there are errors
|
||||
/// </summary>
|
||||
/// <param name="contentTypeSave"></param>
|
||||
/// <param name="composition"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<string> GetAllPropertyTypeAliases()
|
||||
protected void ValidateComposition(ContentTypeSave contentTypeSave, IContentTypeComposition composition)
|
||||
{
|
||||
return ApplicationContext.Services.ContentTypeService.GetAllPropertyTypeAliases();
|
||||
}
|
||||
|
||||
public ContentPropertyDisplay GetPropertyTypeScaffold(int id)
|
||||
{
|
||||
var dataTypeDiff = Services.DataTypeService.GetDataTypeDefinitionById(id);
|
||||
|
||||
if (dataTypeDiff == null)
|
||||
var validateAttempt = Services.ContentTypeService.ValidateComposition(composition);
|
||||
if (validateAttempt == false)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
//if it's not successful then we need to return some model state for the property aliases that
|
||||
// are duplicated
|
||||
var propertyAliases = validateAttempt.Result.Distinct();
|
||||
foreach (var propertyAlias in propertyAliases)
|
||||
{
|
||||
//find the property relating to these
|
||||
var prop = contentTypeSave.Groups.SelectMany(x => x.Properties).Single(x => x.Alias == propertyAlias);
|
||||
var group = contentTypeSave.Groups.Single(x => x.Properties.Contains(prop));
|
||||
var propIndex = group.Properties.IndexOf(prop);
|
||||
var groupIndex = contentTypeSave.Groups.IndexOf(group);
|
||||
|
||||
var key = string.Format("Groups[{0}].Properties[{1}].Alias", groupIndex, propIndex);
|
||||
ModelState.AddModelError(key, "Duplicate property aliases not allowed between compositions");
|
||||
}
|
||||
|
||||
var display = Mapper.Map<ContentTypeDisplay>(composition);
|
||||
//map the 'save' data on top
|
||||
display = Mapper.Map(contentTypeSave, display);
|
||||
display.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(display));
|
||||
}
|
||||
|
||||
var preVals = UmbracoContext.Current.Application.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(id);
|
||||
var editor = PropertyEditorResolver.Current.GetByAlias(dataTypeDiff.PropertyEditorAlias);
|
||||
|
||||
return new ContentPropertyDisplay()
|
||||
{
|
||||
Editor = dataTypeDiff.PropertyEditorAlias,
|
||||
Validation = new PropertyTypeValidation() { },
|
||||
View = editor.ValueEditor.View,
|
||||
Config = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals)
|
||||
};
|
||||
}
|
||||
|
||||
public dynamic GetSafeAlias(string value, bool camelCase = true)
|
||||
{
|
||||
var returnValue = (string.IsNullOrWhiteSpace(value)) ? string.Empty : value.ToSafeAlias(camelCase);
|
||||
dynamic returnObj = new System.Dynamic.ExpandoObject();
|
||||
returnObj.alias = returnValue;
|
||||
returnObj.original = value;
|
||||
returnObj.camelCase = camelCase;
|
||||
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public string TranslateItem(string text)
|
||||
protected string TranslateItem(string text)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
@@ -132,6 +126,97 @@ namespace Umbraco.Web.Editors
|
||||
return CultureDictionary[text].IfNullOrWhiteSpace(text);
|
||||
}
|
||||
|
||||
protected TContentType PerformPostSave<TContentType, TContentTypeDisplay>(
|
||||
ContentTypeSave contentTypeSave,
|
||||
Func<int, TContentType> getContentType,
|
||||
Action<TContentType> saveContentType,
|
||||
bool validateComposition = true,
|
||||
Action<ContentTypeSave> beforeCreateNew = null)
|
||||
where TContentType : IContentTypeComposition
|
||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
{
|
||||
var ctId = Convert.ToInt32(contentTypeSave.Id);
|
||||
|
||||
if (ModelState.IsValid == false)
|
||||
{
|
||||
var ct = getContentType(ctId);
|
||||
//Required data is invalid so we cannot continue
|
||||
var forDisplay = Mapper.Map<TContentTypeDisplay>(ct);
|
||||
//map the 'save' data on top
|
||||
forDisplay = Mapper.Map(contentTypeSave, forDisplay);
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
|
||||
}
|
||||
|
||||
//filter out empty properties
|
||||
contentTypeSave.Groups = contentTypeSave.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList();
|
||||
foreach (var group in contentTypeSave.Groups)
|
||||
{
|
||||
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
|
||||
}
|
||||
|
||||
if (ctId > 0)
|
||||
{
|
||||
//its an update to an existing
|
||||
var found = getContentType(ctId);
|
||||
if (found == null)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
Mapper.Map(contentTypeSave, found);
|
||||
|
||||
if (validateComposition)
|
||||
{
|
||||
//NOTE: this throws an error response if it is not valid
|
||||
ValidateComposition(contentTypeSave, found);
|
||||
}
|
||||
|
||||
saveContentType(found);
|
||||
|
||||
return found;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (beforeCreateNew != null)
|
||||
{
|
||||
beforeCreateNew(contentTypeSave);
|
||||
}
|
||||
|
||||
//set id to null to ensure its handled as a new type
|
||||
contentTypeSave.Id = null;
|
||||
contentTypeSave.CreateDate = DateTime.Now;
|
||||
contentTypeSave.UpdateDate = DateTime.Now;
|
||||
|
||||
//check if the type is trying to allow type 0 below itself - id zero refers to the currently unsaved type
|
||||
//always filter these 0 types out
|
||||
var allowItselfAsChild = false;
|
||||
if (contentTypeSave.AllowedContentTypes != null)
|
||||
{
|
||||
allowItselfAsChild = contentTypeSave.AllowedContentTypes.Any(x => x == 0);
|
||||
contentTypeSave.AllowedContentTypes = contentTypeSave.AllowedContentTypes.Where(x => x > 0).ToList();
|
||||
}
|
||||
|
||||
//save as new
|
||||
var newCt = Mapper.Map<TContentType>(contentTypeSave);
|
||||
|
||||
if (validateComposition)
|
||||
{
|
||||
//NOTE: this throws an error response if it is not valid
|
||||
ValidateComposition(contentTypeSave, newCt);
|
||||
}
|
||||
|
||||
saveContentType(newCt);
|
||||
|
||||
//we need to save it twice to allow itself under itself.
|
||||
if (allowItselfAsChild)
|
||||
{
|
||||
//NOTE: This will throw if the composition isn't right... but it shouldn't be at this stage
|
||||
newCt.AddContentType(newCt);
|
||||
saveContentType(newCt);
|
||||
}
|
||||
return newCt;
|
||||
}
|
||||
}
|
||||
|
||||
private ICultureDictionary CultureDictionary
|
||||
{
|
||||
get
|
||||
|
||||
@@ -42,6 +42,23 @@ namespace Umbraco.Web.Editors
|
||||
[PluginController("UmbracoApi")]
|
||||
public class EntityController : UmbracoAuthorizedJsonController
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns an Umbraco alias given a string
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="camelCase"></param>
|
||||
/// <returns></returns>
|
||||
public dynamic GetSafeAlias(string value, bool camelCase = true)
|
||||
{
|
||||
var returnValue = (string.IsNullOrWhiteSpace(value)) ? string.Empty : value.ToSafeAlias(camelCase);
|
||||
dynamic returnObj = new System.Dynamic.ExpandoObject();
|
||||
returnObj.alias = returnValue;
|
||||
returnObj.original = value;
|
||||
returnObj.camelCase = camelCase;
|
||||
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for results based on the entity type
|
||||
/// </summary>
|
||||
|
||||
@@ -14,7 +14,9 @@ using System.Net;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Umbraco.Web.WebApi;
|
||||
using ContentType = System.Net.Mime.ContentType;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -100,58 +102,20 @@ namespace Umbraco.Web.Editors
|
||||
.Select(Mapper.Map<IMediaType, ContentTypeBasic>);
|
||||
}
|
||||
|
||||
public ContentTypeCompositionDisplay PostSave(ContentTypeCompositionDisplay contentType)
|
||||
public ContentTypeCompositionDisplay PostSave(ContentTypeSave contentTypeSave)
|
||||
{
|
||||
var savedCt = PerformPostSave<IMediaType, ContentTypeCompositionDisplay>(
|
||||
contentTypeSave: contentTypeSave,
|
||||
getContentType: i => Services.ContentTypeService.GetMediaType(i),
|
||||
saveContentType: type => Services.ContentTypeService.Save(type));
|
||||
|
||||
var ctService = ApplicationContext.Services.ContentTypeService;
|
||||
var display = Mapper.Map<ContentTypeCompositionDisplay>(savedCt);
|
||||
|
||||
//TODO: warn on content type alias conflicts
|
||||
//TODO: warn on property alias conflicts
|
||||
|
||||
//TODO: Validate the submitted model
|
||||
|
||||
//filter out empty properties
|
||||
contentType.Groups = contentType.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList();
|
||||
foreach (var group in contentType.Groups)
|
||||
{
|
||||
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
|
||||
}
|
||||
|
||||
var ctId = Convert.ToInt32(contentType.Id);
|
||||
|
||||
if (ctId > 0)
|
||||
{
|
||||
//its an update to an existing
|
||||
IMediaType found = ctService.GetMediaType(ctId);
|
||||
if (found == null)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
Mapper.Map(contentType, found);
|
||||
ctService.Save(found);
|
||||
|
||||
//map the saved item back to the content type (it should now get id etc set)
|
||||
Mapper.Map(found, contentType);
|
||||
return contentType;
|
||||
}
|
||||
else
|
||||
{
|
||||
//ensure alias is set
|
||||
if (string.IsNullOrEmpty(contentType.Alias))
|
||||
contentType.Alias = contentType.Name.ToSafeAlias();
|
||||
|
||||
contentType.Id = null;
|
||||
|
||||
//save as new
|
||||
IMediaType newCt = new MediaType(-1);
|
||||
Mapper.Map(contentType, newCt);
|
||||
|
||||
ctService.Save(newCt);
|
||||
|
||||
//map the saved item back to the content type (it should now get id etc set)
|
||||
Mapper.Map(newCt, contentType);
|
||||
return contentType;
|
||||
}
|
||||
display.AddSuccessNotification(
|
||||
Services.TextService.Localize("speechBubbles/contentTypeSavedHeader"),
|
||||
string.Empty);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Web.Security;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
@@ -18,16 +19,12 @@ using ContentType = System.Net.Mime.ContentType;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
//TODO: We'll need to be careful about the security on this controller, when we start implementing
|
||||
// methods to modify content types we'll need to enforce security on the individual methods, we
|
||||
// cannot put security on the whole controller because things like GetAllowedChildren are required for content editing.
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An API controller used for dealing with content types
|
||||
/// </summary>
|
||||
[PluginController("UmbracoApi")]
|
||||
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
|
||||
[EnableOverrideAuthorization]
|
||||
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
|
||||
public class MemberTypeController : ContentTypeControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
@@ -105,58 +102,21 @@ namespace Umbraco.Web.Editors
|
||||
return Enumerable.Empty<ContentTypeBasic>();
|
||||
}
|
||||
|
||||
public ContentTypeCompositionDisplay PostSave(ContentTypeCompositionDisplay contentType)
|
||||
public ContentTypeCompositionDisplay PostSave(ContentTypeSave contentTypeSave)
|
||||
{
|
||||
var savedCt = PerformPostSave<IMemberType, ContentTypeCompositionDisplay>(
|
||||
contentTypeSave: contentTypeSave,
|
||||
getContentType: i => Services.MemberTypeService.Get(i),
|
||||
saveContentType: type => Services.MemberTypeService.Save(type),
|
||||
validateComposition: false);
|
||||
|
||||
var ctService = ApplicationContext.Services.MemberTypeService;
|
||||
var display = Mapper.Map<ContentTypeCompositionDisplay>(savedCt);
|
||||
|
||||
//TODO: warn on content type alias conflicts
|
||||
//TODO: warn on property alias conflicts
|
||||
|
||||
//TODO: Validate the submitted model
|
||||
|
||||
//filter out empty properties
|
||||
contentType.Groups = contentType.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList();
|
||||
foreach (var group in contentType.Groups)
|
||||
{
|
||||
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
|
||||
}
|
||||
|
||||
var ctId = Convert.ToInt32(contentType.Id);
|
||||
|
||||
if (ctId > 0)
|
||||
{
|
||||
//its an update to an existing
|
||||
IMemberType found = ctService.Get(ctId);
|
||||
if (found == null)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
Mapper.Map(contentType, found);
|
||||
ctService.Save(found);
|
||||
|
||||
//map the saved item back to the content type (it should now get id etc set)
|
||||
Mapper.Map(found, contentType);
|
||||
return contentType;
|
||||
}
|
||||
else
|
||||
{
|
||||
//ensure alias is set
|
||||
if (string.IsNullOrEmpty(contentType.Alias))
|
||||
contentType.Alias = contentType.Name.ToSafeAlias();
|
||||
|
||||
contentType.Id = null;
|
||||
|
||||
//save as new
|
||||
IMemberType newCt = new MemberType(-1);
|
||||
Mapper.Map(contentType, newCt);
|
||||
|
||||
ctService.Save(newCt);
|
||||
|
||||
//map the saved item back to the content type (it should now get id etc set)
|
||||
Mapper.Map(newCt, contentType);
|
||||
return contentType;
|
||||
}
|
||||
display.AddSuccessNotification(
|
||||
Services.TextService.Localize("speechBubbles/contentTypeSavedHeader"),
|
||||
string.Empty);
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ namespace Umbraco.Web
|
||||
if (!result.MemberNames.Any())
|
||||
{
|
||||
//add a model state error for the entire property
|
||||
modelState.AddModelError(string.Format("{0}.{1}", "Properties", propertyAlias), result.ErrorMessage);
|
||||
modelState.AddModelError(string.Format("{0}.{1}", "_Properties", propertyAlias), result.ErrorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -72,7 +72,7 @@ namespace Umbraco.Web
|
||||
// so that we can try to match it up to a real sub field of this editor
|
||||
foreach (var field in result.MemberNames)
|
||||
{
|
||||
modelState.AddModelError(string.Format("{0}.{1}.{2}", "Properties", propertyAlias, field), result.ErrorMessage);
|
||||
modelState.AddModelError(string.Format("{0}.{1}.{2}", "_Properties", propertyAlias, field), result.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
@@ -30,6 +31,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
|
||||
/// </summary>
|
||||
[DataMember(Name = "notifications")]
|
||||
[ReadOnly(true)]
|
||||
public List<Notification> Notifications { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -43,6 +45,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// NOTE: The ProperCase is important because when we return ModeState normally it will always be proper case.
|
||||
/// </remarks>
|
||||
[DataMember(Name = "ModelState")]
|
||||
[ReadOnly(true)]
|
||||
public IDictionary<string, object> Errors { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models.Validation;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
@@ -15,10 +17,20 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeBasic : EntityBasic
|
||||
{
|
||||
/// <summary>
|
||||
/// Overridden to apply our own validation attributes since this is not always required for other classes
|
||||
/// </summary>
|
||||
[Required]
|
||||
[RegularExpression(@"^([a-zA-Z]\w.*)$", ErrorMessage = "Invalid alias")]
|
||||
[DataMember(Name = "alias")]
|
||||
public override string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "updateDate")]
|
||||
[ReadOnly(true)]
|
||||
public DateTime UpdateDate { get; set; }
|
||||
|
||||
[DataMember(Name = "createDate")]
|
||||
[ReadOnly(true)]
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
[DataMember(Name = "description")]
|
||||
@@ -31,6 +43,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// Returns true if the icon represents a CSS class instead of a file path
|
||||
/// </summary>
|
||||
[DataMember(Name = "iconIsClass")]
|
||||
[ReadOnly(true)]
|
||||
public bool IconIsClass
|
||||
{
|
||||
get
|
||||
@@ -48,6 +61,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// Returns the icon file path if the icon is not a class, otherwise returns an empty string
|
||||
/// </summary>
|
||||
[DataMember(Name = "iconFilePath")]
|
||||
[ReadOnly(true)]
|
||||
public string IconFilePath
|
||||
{
|
||||
get
|
||||
@@ -62,6 +76,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// Returns true if the icon represents a CSS class instead of a file path
|
||||
/// </summary>
|
||||
[DataMember(Name = "thumbnailIsClass")]
|
||||
[ReadOnly(true)]
|
||||
public bool ThumbnailIsClass
|
||||
{
|
||||
get
|
||||
@@ -79,6 +94,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// Returns the icon file path if the icon is not a class, otherwise returns an empty string
|
||||
/// </summary>
|
||||
[DataMember(Name = "thumbnailFilePath")]
|
||||
[ReadOnly(true)]
|
||||
public string ThumbnailFilePath
|
||||
{
|
||||
get
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Models.Validation;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeCompositionDisplay : ContentTypeBasic
|
||||
public class ContentTypeCompositionDisplay : ContentTypeBasic, INotificationModel
|
||||
{
|
||||
public ContentTypeCompositionDisplay()
|
||||
{
|
||||
@@ -17,15 +20,17 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
AllowedContentTypes = new List<int>();
|
||||
CompositeContentTypes = new List<string>();
|
||||
AvailableCompositeContentTypes = new List<EntityBasic>();
|
||||
Notifications = new List<Notification>();
|
||||
}
|
||||
|
||||
//name, alias, icon, thumb, desc, inherited from basic
|
||||
|
||||
//name, alias, icon, thumb, desc, inherited from basic
|
||||
|
||||
//List view
|
||||
[DataMember(Name = "isContainer")]
|
||||
public bool IsContainer { get; set; }
|
||||
|
||||
[DataMember(Name = "listViewEditorName")]
|
||||
[ReadOnly(true)]
|
||||
public string ListViewEditorName { get; set; }
|
||||
|
||||
//Tabs
|
||||
@@ -41,9 +46,31 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
public IEnumerable<string> CompositeContentTypes { get; set; }
|
||||
|
||||
[DataMember(Name = "availableCompositeContentTypes")]
|
||||
[ReadOnly(true)]
|
||||
public IEnumerable<EntityBasic> AvailableCompositeContentTypes { get; set; }
|
||||
|
||||
[DataMember(Name = "allowAsRoot")]
|
||||
public bool AllowAsRoot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
|
||||
/// </summary>
|
||||
[DataMember(Name = "notifications")]
|
||||
[ReadOnly(true)]
|
||||
public List<Notification> Notifications { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used for validation of a content item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A content item can be invalid but still be saved. This occurs when there's property validation errors, we will
|
||||
/// still save the item but it cannot be published. So we need a way of returning validation errors as well as the
|
||||
/// updated model.
|
||||
///
|
||||
/// NOTE: The ProperCase is important because when we return ModeState normally it will always be proper case.
|
||||
/// </remarks>
|
||||
[DataMember(Name = "ModelState")]
|
||||
[ReadOnly(true)]
|
||||
public IDictionary<string, object> Errors { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
@@ -7,6 +8,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
{
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeSave : ContentTypeBasic, IValidatableObject
|
||||
{
|
||||
public ContentTypeSave()
|
||||
{
|
||||
//initialize collections so at least their never null
|
||||
Groups = new List<PropertyGroupBasic<PropertyTypeBasic>>();
|
||||
AllowedContentTypes = new List<int>();
|
||||
CompositeContentTypes = new List<string>();
|
||||
}
|
||||
|
||||
//Compositions
|
||||
[DataMember(Name = "compositeContentTypes")]
|
||||
public IEnumerable<string> CompositeContentTypes { get; set; }
|
||||
|
||||
[DataMember(Name = "isContainer")]
|
||||
public bool IsContainer { get; set; }
|
||||
|
||||
[DataMember(Name = "allowAsRoot")]
|
||||
public bool AllowAsRoot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of allowed templates to assign (template alias)
|
||||
/// </summary>
|
||||
[DataMember(Name = "allowedTemplates")]
|
||||
public IEnumerable<string> AllowedTemplates { get; set; }
|
||||
|
||||
//Allowed child types
|
||||
[DataMember(Name = "allowedContentTypes")]
|
||||
public IEnumerable<int> AllowedContentTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The default template to assign (template alias)
|
||||
/// </summary>
|
||||
[DataMember(Name = "defaultTemplate")]
|
||||
public string DefaultTemplate { get; set; }
|
||||
|
||||
//Tabs
|
||||
[DataMember(Name = "groups")]
|
||||
public IEnumerable<PropertyGroupBasic<PropertyTypeBasic>> Groups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Custom validation
|
||||
/// </summary>
|
||||
/// <param name="validationContext"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (AllowedTemplates.Any(x => x.IsNullOrWhiteSpace()))
|
||||
yield return new ValidationResult("Template value cannot be null", new[] {"AllowedTemplates"});
|
||||
|
||||
if (CompositeContentTypes.Any(x => x.IsNullOrWhiteSpace()))
|
||||
yield return new ValidationResult("Composite Content Type value cannot be null", new[] { "CompositeContentTypes" });
|
||||
|
||||
var duplicateGroups = Groups.GroupBy(x => x.Name).Where(x => x.Count() > 1).ToArray();
|
||||
if (duplicateGroups.Any())
|
||||
{
|
||||
//we need to return the field name with an index so it's wired up correctly
|
||||
var firstIndex = Groups.IndexOf(duplicateGroups.First().First());
|
||||
yield return new ValidationResult("Duplicate group names not allowed", new[]
|
||||
{
|
||||
string.Format("Groups[{0}].Name", firstIndex)
|
||||
});
|
||||
}
|
||||
|
||||
var duplicateProperties = Groups.SelectMany(x => x.Properties).Where(x => x.Inherited == false).GroupBy(x => x.Alias).Where(x => x.Count() > 1).ToArray();
|
||||
if (duplicateProperties.Any())
|
||||
{
|
||||
//we need to return the field name with an index so it's wired up correctly
|
||||
var firstProperty = duplicateProperties.First().First();
|
||||
var propertyGroup = Groups.Single(x => x.Properties.Contains(firstProperty));
|
||||
var groupIndex = Groups.IndexOf(propertyGroup);
|
||||
var propertyIndex = propertyGroup.Properties.IndexOf(firstProperty);
|
||||
|
||||
yield return new ValidationResult("Duplicate property aliases not allowed", new[]
|
||||
{
|
||||
string.Format("Groups[{0}].Properties[{1}].Alias", groupIndex, propertyIndex)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -29,6 +30,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
public string Icon { get; set; }
|
||||
|
||||
[DataMember(Name = "trashed")]
|
||||
[ReadOnly(true)]
|
||||
public bool Trashed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -44,8 +46,11 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// <summary>
|
||||
/// This will only be populated for some entities like macros
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is overrideable to specify different validation attributes if required
|
||||
/// </remarks>
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
public virtual string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the entity
|
||||
@@ -57,6 +62,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// A collection of extra data that is available for this specific entity/entity type
|
||||
/// </summary>
|
||||
[DataMember(Name = "metaData")]
|
||||
[ReadOnly(true)]
|
||||
public IDictionary<string, object> AdditionalData { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "propertyGroup", Namespace = "")]
|
||||
public class PropertyGroupBasic<TPropertyType>
|
||||
where TPropertyType: PropertyTypeBasic
|
||||
{
|
||||
public PropertyGroupBasic()
|
||||
{
|
||||
Properties = new List<TPropertyType>();
|
||||
}
|
||||
|
||||
//Indicate if this tab was inherited
|
||||
[DataMember(Name = "inherited")]
|
||||
public bool Inherited { get; set; }
|
||||
|
||||
//TODO: Required ?
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[DataMember(Name = "properties")]
|
||||
public IEnumerable<TPropertyType> Properties { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
@@ -8,7 +9,7 @@ using System.Threading.Tasks;
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "propertyGroup", Namespace = "")]
|
||||
public class PropertyGroupDisplay
|
||||
public class PropertyGroupDisplay : PropertyGroupBasic<PropertyTypeDisplay>
|
||||
{
|
||||
public PropertyGroupDisplay()
|
||||
{
|
||||
@@ -16,33 +17,22 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
ParentTabContentTypeNames = new List<string>();
|
||||
ParentTabContentTypes = new List<int>();
|
||||
}
|
||||
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
|
||||
[DataMember(Name = "parentGroupId")]
|
||||
[ReadOnly(true)]
|
||||
public int ParentGroupId { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "properties")]
|
||||
public IEnumerable<PropertyTypeDisplay> Properties { get; set; }
|
||||
|
||||
//Indicate if this tab was inherited
|
||||
[DataMember(Name = "inherited")]
|
||||
public bool Inherited { get; set; }
|
||||
|
||||
|
||||
//SD: Seems strange that this is required
|
||||
[DataMember(Name = "contentTypeId")]
|
||||
[ReadOnly(true)]
|
||||
public int ContentTypeId { get; set; }
|
||||
|
||||
[DataMember(Name = "parentTabContentTypes")]
|
||||
[ReadOnly(true)]
|
||||
public IEnumerable<int> ParentTabContentTypes { get; set; }
|
||||
|
||||
[DataMember(Name = "parentTabContentTypeNames")]
|
||||
[ReadOnly(true)]
|
||||
public IEnumerable<string> ParentTabContentTypeNames { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "propertyType")]
|
||||
public class PropertyTypeBasic
|
||||
{
|
||||
//indicates if this property was inherited
|
||||
[DataMember(Name = "inherited")]
|
||||
public bool Inherited { get; set; }
|
||||
|
||||
//TODO: Required ?
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[RegularExpression(@"^([a-zA-Z]\w.*)$", ErrorMessage = "Invalid alias")]
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[DataMember(Name = "validation")]
|
||||
public PropertyTypeValidation Validation { get; set; }
|
||||
|
||||
[DataMember(Name = "label")]
|
||||
[Required]
|
||||
public string Label { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
[DataMember(Name = "dataTypeId")]
|
||||
[Required]
|
||||
public int DataTypeId { get; set; }
|
||||
|
||||
//SD: Is this really needed ?
|
||||
[DataMember(Name = "groupId")]
|
||||
public int GroupId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
@@ -8,52 +9,28 @@ using System.Threading.Tasks;
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "propertyType")]
|
||||
public class PropertyTypeDisplay
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
public class PropertyTypeDisplay : PropertyTypeBasic
|
||||
{
|
||||
[DataMember(Name = "editor")]
|
||||
[ReadOnly(true)]
|
||||
public string Editor { get; set; }
|
||||
|
||||
[DataMember(Name = "validation")]
|
||||
public PropertyTypeValidation Validation { get; set; }
|
||||
|
||||
[DataMember(Name = "label")]
|
||||
public string Label { get; set; }
|
||||
|
||||
|
||||
[DataMember(Name = "view")]
|
||||
[ReadOnly(true)]
|
||||
public string View { get; set; }
|
||||
|
||||
[DataMember(Name = "config")]
|
||||
[ReadOnly(true)]
|
||||
public IDictionary<string, object> Config { get; set; }
|
||||
|
||||
[DataMember(Name = "value")]
|
||||
public string Value { get; set; }
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
//indicates if this property was inherited
|
||||
[DataMember(Name = "inherited")]
|
||||
public bool Inherited { get; set; }
|
||||
|
||||
[DataMember(Name = "dataTypeId")]
|
||||
public int DataTypeId { get; set; }
|
||||
|
||||
[DataMember(Name = "groupId")]
|
||||
public int GroupId { get; set; }
|
||||
|
||||
|
||||
//SD: Seems strange that this is needed
|
||||
[DataMember(Name = "contentTypeId")]
|
||||
[ReadOnly(true)]
|
||||
public int ContentTypeId { get; set; }
|
||||
|
||||
//SD: Seems strange that this is needed
|
||||
[DataMember(Name = "contentTypeName")]
|
||||
[ReadOnly(true)]
|
||||
public string ContentTypeName { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,11 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class AvailableCompositeContentTypesResolver : ValueResolver<IContentTypeComposition, IEnumerable<EntityBasic>>
|
||||
{
|
||||
private ApplicationContext _context;
|
||||
private bool _mediaType;
|
||||
internal AvailableCompositeContentTypesResolver(ApplicationContext context, bool mediaType = false)
|
||||
private readonly ApplicationContext _context;
|
||||
|
||||
internal AvailableCompositeContentTypesResolver(ApplicationContext context)
|
||||
{
|
||||
_context = context;
|
||||
_mediaType = mediaType;
|
||||
}
|
||||
|
||||
protected override IEnumerable<EntityBasic> ResolveCore(IContentTypeComposition source)
|
||||
@@ -27,19 +26,19 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
var s = source;
|
||||
var type = _context.Services.EntityService.GetObjectType(source.Id);
|
||||
IContentTypeComposition[] allContentTypes = new IContentTypeComposition[0];
|
||||
var allContentTypes = new IContentTypeComposition[0];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case UmbracoObjectTypes.DocumentType:
|
||||
case UmbracoObjectTypes.DocumentType:
|
||||
allContentTypes = _context.Services.ContentTypeService.GetAllContentTypes().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
allContentTypes = _context.Services.ContentTypeService.GetAllMediaTypes().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
allContentTypes = _context.Services.MemberTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
}
|
||||
@@ -56,42 +55,40 @@ namespace Umbraco.Web.Models.Mapping
|
||||
//if already in use a composition, do not allow any composited types
|
||||
return new List<EntityBasic>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it is not used then composition is possible
|
||||
// hashset guarantees unicity on Id
|
||||
var list = new HashSet<IContentTypeComposition>(new DelegateEqualityComparer<IContentTypeComposition>(
|
||||
(x, y) => x.Id == y.Id,
|
||||
x => x.Id));
|
||||
|
||||
// usable types are those that are top-level
|
||||
var usableContentTypes = allContentTypes
|
||||
.Where(x => x.ContentTypeComposition.Any() == false).ToArray();
|
||||
foreach (var x in usableContentTypes)
|
||||
list.Add(x);
|
||||
// if it is not used then composition is possible
|
||||
// hashset guarantees unicity on Id
|
||||
var list = new HashSet<IContentTypeComposition>(new DelegateEqualityComparer<IContentTypeComposition>(
|
||||
(x, y) => x.Id == y.Id,
|
||||
x => x.Id));
|
||||
|
||||
// indirect types are those that we use, directly or indirectly
|
||||
var indirectContentTypes = GetIndirect(source).ToArray();
|
||||
foreach (var x in indirectContentTypes)
|
||||
list.Add(x);
|
||||
// usable types are those that are top-level
|
||||
var usableContentTypes = allContentTypes
|
||||
.Where(x => x.ContentTypeComposition.Any() == false).ToArray();
|
||||
foreach (var x in usableContentTypes)
|
||||
list.Add(x);
|
||||
|
||||
// directContentTypes are those we use directly
|
||||
// they are already in indirectContentTypes, no need to add to the list
|
||||
var directContentTypes = source.ContentTypeComposition.ToArray();
|
||||
// indirect types are those that we use, directly or indirectly
|
||||
var indirectContentTypes = GetIndirect(source).ToArray();
|
||||
foreach (var x in indirectContentTypes)
|
||||
list.Add(x);
|
||||
|
||||
var enabled = usableContentTypes.Select(x => x.Id) // those we can use
|
||||
.Except(indirectContentTypes.Select(x => x.Id)) // except those that are indirectly used
|
||||
.Union(directContentTypes.Select(x => x.Id)) // but those that are directly used
|
||||
.Where(x => x != source.ParentId) // but not the parent
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
//// directContentTypes are those we use directly
|
||||
//// they are already in indirectContentTypes, no need to add to the list
|
||||
//var directContentTypes = source.ContentTypeComposition.ToArray();
|
||||
|
||||
var wtf = new List<EntityBasic>();
|
||||
foreach (var contentType in list.OrderBy(x => x.Name).Where(x => x.Id != source.Id))
|
||||
wtf.Add(Mapper.Map<IContentTypeComposition, EntityBasic>(contentType));
|
||||
//var enabled = usableContentTypes.Select(x => x.Id) // those we can use
|
||||
// .Except(indirectContentTypes.Select(x => x.Id)) // except those that are indirectly used
|
||||
// .Union(directContentTypes.Select(x => x.Id)) // but those that are directly used
|
||||
// .Where(x => x != source.ParentId) // but not the parent
|
||||
// .Distinct()
|
||||
// .ToArray();
|
||||
|
||||
return wtf;
|
||||
}
|
||||
return list
|
||||
.Where(x => x.Id != source.Id)
|
||||
.OrderBy(x => x.Name)
|
||||
.Select(Mapper.Map<IContentTypeComposition, EntityBasic>)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using AutoMapper;
|
||||
@@ -20,7 +19,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
internal class ContentTypeModelMapper : MapperConfiguration
|
||||
{
|
||||
private readonly Lazy<PropertyEditorResolver> _propertyEditorResolver;
|
||||
|
||||
|
||||
//default ctor
|
||||
public ContentTypeModelMapper()
|
||||
{
|
||||
@@ -35,207 +34,69 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<ContentTypeCompositionDisplay, IContentTypeComposition>()
|
||||
.Include<ContentTypeDisplay, IContentType>()
|
||||
.Include<ContentTypeCompositionDisplay, IMemberType>()
|
||||
.Include<ContentTypeCompositionDisplay, IMediaType>()
|
||||
|
||||
config.CreateMap<PropertyTypeBasic, PropertyType>()
|
||||
.ConstructUsing(basic => new PropertyType(applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(basic.DataTypeId)))
|
||||
.ForMember(type => type.ValidationRegExp, expression => expression.ResolveUsing(basic => basic.Validation.Pattern))
|
||||
.ForMember(type => type.Mandatory, expression => expression.ResolveUsing(basic => basic.Validation.Mandatory))
|
||||
.ForMember(type => type.Name, expression => expression.ResolveUsing(basic => basic.Label))
|
||||
.ForMember(type => type.DataTypeDefinitionId, expression => expression.ResolveUsing(basic => basic.DataTypeId))
|
||||
.ForMember(type => type.DataTypeId, expression => expression.Ignore())
|
||||
.ForMember(type => type.PropertyEditorAlias, expression => expression.Ignore())
|
||||
.ForMember(type => type.HelpText, expression => expression.Ignore())
|
||||
.ForMember(type => type.Key, expression => expression.Ignore())
|
||||
.ForMember(type => type.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(type => type.UpdateDate, expression => expression.Ignore())
|
||||
.ForMember(type => type.HasIdentity, expression => expression.Ignore());
|
||||
|
||||
//only map id if set to something higher then zero
|
||||
.ForMember(dto => dto.Id, expression => expression.Condition(display => (Convert.ToInt32(display.Id) > 0)))
|
||||
.ForMember(dto => dto.Id, expression => expression.MapFrom(display => Convert.ToInt32(display.Id)))
|
||||
|
||||
.ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot))
|
||||
.ForMember(dto => dto.CreatorId, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.Level, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.SortOrder, expression => expression.Ignore())
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AllowedContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select( (t, i) => new ContentTypeSort(t, i) )))
|
||||
|
||||
//ignore, we'll do this in after map
|
||||
.ForMember(dto => dto.PropertyGroups, expression => expression.Ignore())
|
||||
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
|
||||
var addedProperties = new List<string>();
|
||||
|
||||
//get all properties from groups that are not generic properties or inhertied (-666 id)
|
||||
var selfNonGenericGroups = source.Groups.Where(x => x.Inherited == false && x.Id != -666).ToArray();
|
||||
|
||||
foreach (var groupDisplay in selfNonGenericGroups)
|
||||
{
|
||||
//use underlying logic to add the property group which should wire most things up for us
|
||||
dest.AddPropertyGroup(groupDisplay.Name);
|
||||
|
||||
//now update that group with the values from the display object
|
||||
Mapper.Map(groupDisplay, dest.PropertyGroups[groupDisplay.Name]);
|
||||
|
||||
foreach (var propertyTypeDisplay in groupDisplay.Properties.Where(x => x.Inherited == false))
|
||||
{
|
||||
//update existing
|
||||
if(propertyTypeDisplay.Id > 0)
|
||||
{
|
||||
var currentPropertyType = dest.PropertyTypes.FirstOrDefault(x => x.Id == propertyTypeDisplay.Id);
|
||||
Mapper.Map(propertyTypeDisplay, currentPropertyType);
|
||||
}else
|
||||
{//add new
|
||||
var mapped = Mapper.Map<PropertyType>(propertyTypeDisplay);
|
||||
dest.AddPropertyType(mapped, groupDisplay.Name);
|
||||
}
|
||||
|
||||
addedProperties.Add(propertyTypeDisplay.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
//Groups to remove
|
||||
var groupsToRemove = dest.PropertyGroups.Select(x => x.Name).Except(selfNonGenericGroups.Select(x => x.Name)).ToArray();
|
||||
foreach (var toRemove in groupsToRemove)
|
||||
{
|
||||
dest.RemovePropertyGroup(toRemove);
|
||||
}
|
||||
|
||||
//add generic properties
|
||||
var genericProperties = source.Groups.FirstOrDefault(x => x.Id == -666);
|
||||
if(genericProperties != null)
|
||||
{
|
||||
foreach (var propertyTypeDisplay in genericProperties.Properties.Where(x => x.Inherited == false))
|
||||
{
|
||||
dest.AddPropertyType(Mapper.Map<PropertyType>(propertyTypeDisplay));
|
||||
addedProperties.Add(propertyTypeDisplay.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
//remove deleted types
|
||||
foreach(var removedType in dest.PropertyTypes
|
||||
.Where(x => addedProperties.Contains(x.Alias) == false).ToList())
|
||||
{
|
||||
dest.RemovePropertyType(removedType.Alias);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
config.CreateMap<ContentTypeDisplay, IContentType>()
|
||||
config.CreateMap<ContentTypeSave, IContentType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity(applicationContext)
|
||||
.ConstructUsing((source) => new ContentType(source.ParentId))
|
||||
.ForMember(dto => dto.Id, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore())
|
||||
.ForMember(source => source.AllowedTemplates, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore())
|
||||
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
//sync templates
|
||||
dest.AllowedTemplates = source.AllowedTemplates.Where(x => x != null).Select(x => Mapper.Map<ITemplate>(x));
|
||||
dest.AllowedTemplates = source.AllowedTemplates
|
||||
.Where(x => x != null)
|
||||
.Select(s => applicationContext.Services.FileService.GetTemplate(s))
|
||||
.ToArray();
|
||||
|
||||
if (source.DefaultTemplate != null)
|
||||
dest.SetDefaultTemplate(Mapper.Map<ITemplate>(source.DefaultTemplate));
|
||||
dest.SetDefaultTemplate(applicationContext.Services.FileService.GetTemplate(source.DefaultTemplate));
|
||||
|
||||
|
||||
//sync compositions
|
||||
var current = dest.CompositionAliases().ToArray();
|
||||
var proposed = source.CompositeContentTypes;
|
||||
|
||||
var remove = current.Where(x => proposed.Contains(x) == false);
|
||||
var add = proposed.Where(x => current.Contains(x) == false);
|
||||
|
||||
foreach (var rem in remove)
|
||||
{
|
||||
dest.RemoveContentType(rem);
|
||||
}
|
||||
|
||||
foreach (var a in add)
|
||||
{
|
||||
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = applicationContext.Services.ContentTypeService.GetContentType(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
|
||||
});
|
||||
|
||||
config.CreateMap<ContentTypeCompositionDisplay, IMemberType>()
|
||||
config.CreateMap<ContentTypeSave, IMediaType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity(applicationContext)
|
||||
.ConstructUsing((source) => new MediaType(source.ParentId))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
|
||||
//sync compositions
|
||||
var current = dest.CompositionAliases().ToArray();
|
||||
var proposed = source.CompositeContentTypes;
|
||||
{
|
||||
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
|
||||
});
|
||||
|
||||
var remove = current.Where(x => proposed.Contains(x) == false);
|
||||
var add = proposed.Where(x => current.Contains(x) == false);
|
||||
|
||||
foreach (var rem in remove)
|
||||
dest.RemoveContentType(rem);
|
||||
|
||||
foreach (var a in add)
|
||||
{
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = applicationContext.Services.MemberTypeService.Get(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
config.CreateMap<ContentTypeCompositionDisplay, IMediaType>()
|
||||
config.CreateMap<ContentTypeSave, IMemberType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity(applicationContext)
|
||||
.ConstructUsing((source) => new MemberType(source.ParentId))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
//sync compositions
|
||||
var current = dest.CompositionAliases().ToArray();
|
||||
var proposed = source.CompositeContentTypes;
|
||||
{
|
||||
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
|
||||
});
|
||||
|
||||
var remove = current.Where(x => proposed.Contains(x) == false);
|
||||
var add = proposed.Where(x => current.Contains(x) == false);
|
||||
|
||||
foreach (var rem in remove)
|
||||
dest.RemoveContentType(rem);
|
||||
|
||||
foreach (var a in add)
|
||||
{
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = applicationContext.Services.ContentTypeService.GetMediaType(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
config.CreateMap<IContentTypeComposition, string>().ConvertUsing(x => x.Alias);
|
||||
|
||||
config.CreateMap<IMemberType, ContentTypeCompositionDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver);
|
||||
|
||||
config.CreateMap<IContentTypeComposition, ContentTypeCompositionDisplay>()
|
||||
.Include<IContentType, ContentTypeDisplay>()
|
||||
.Include<IMemberType, ContentTypeCompositionDisplay>()
|
||||
.Include<IMediaType, ContentTypeCompositionDisplay>()
|
||||
|
||||
.ForMember(display => display.AllowAsRoot, expression => expression.MapFrom(type => type.AllowedAsRoot))
|
||||
.ForMember(display => display.ListViewEditorName, expression => expression.Ignore())
|
||||
//Ignore because this is not actually used for content types
|
||||
.ForMember(display => display.Trashed, expression => expression.Ignore())
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AllowedContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select(x => x.Id.Value)))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AvailableCompositeContentTypes,
|
||||
expression => expression.ResolveUsing(new AvailableCompositeContentTypesResolver(applicationContext)))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.CompositeContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.ContentTypeComposition))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.Groups,
|
||||
expression => expression.ResolveUsing(new PropertyTypeGroupResolver(applicationContext, _propertyEditorResolver)));
|
||||
|
||||
|
||||
config.CreateMap<IMemberType, ContentTypeCompositionDisplay>();
|
||||
config.CreateMap<IMediaType, ContentTypeCompositionDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver)
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
|
||||
//default listview
|
||||
dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media";
|
||||
|
||||
@@ -248,20 +109,22 @@ namespace Umbraco.Web.Models.Mapping
|
||||
});
|
||||
|
||||
config.CreateMap<IContentType, ContentTypeDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver)
|
||||
.ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore())
|
||||
|
||||
.ForMember(display => display.Notifications, expression => expression.Ignore())
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
//sync templates
|
||||
dest.AllowedTemplates = source.AllowedTemplates.Select(Mapper.Map<EntityBasic>);
|
||||
dest.AllowedTemplates = source.AllowedTemplates.Select(Mapper.Map<EntityBasic>).ToArray();
|
||||
|
||||
if (source.DefaultTemplate != null)
|
||||
dest.DefaultTemplate = Mapper.Map<EntityBasic>(source.DefaultTemplate);
|
||||
|
||||
//default listview
|
||||
dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Content";
|
||||
|
||||
|
||||
if (string.IsNullOrEmpty(source.Name) == false)
|
||||
{
|
||||
var name = Constants.Conventions.DataTypes.ListViewPrefix + source.Name;
|
||||
@@ -275,26 +138,13 @@ namespace Umbraco.Web.Models.Mapping
|
||||
config.CreateMap<IMediaType, ContentTypeBasic>();
|
||||
config.CreateMap<IContentType, ContentTypeBasic>();
|
||||
|
||||
config.CreateMap<PropertyTypeBasic, PropertyType>()
|
||||
|
||||
config.CreateMap<PropertyGroupDisplay, PropertyGroup>()
|
||||
.ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0))
|
||||
.ForMember(g => g.Key, expression => expression.Ignore())
|
||||
.ForMember(g => g.HasIdentity, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
||||
//only map if a parent is actually set
|
||||
.ForMember(g => g.ParentId, expression => expression.Condition(display => display.ParentGroupId > 0))
|
||||
.ForMember(g => g.ParentId, expression => expression.MapFrom(display => display.ParentGroupId))
|
||||
//ignore these, this is handled with IContentType.AddPropertyType
|
||||
.ForMember(g => g.PropertyTypes, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<PropertyTypeDisplay, PropertyType>()
|
||||
|
||||
.ConstructUsing((PropertyTypeDisplay propertyTypeDisplay) =>
|
||||
.ConstructUsing((PropertyTypeBasic propertyTypeBasic) =>
|
||||
{
|
||||
var dataType = applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(propertyTypeDisplay.DataTypeId);
|
||||
if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeDisplay.DataTypeId);
|
||||
return new PropertyType(dataType, propertyTypeDisplay.Alias);
|
||||
var dataType = applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(propertyTypeBasic.DataTypeId);
|
||||
if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeBasic.DataTypeId);
|
||||
return new PropertyType(dataType, propertyTypeBasic.Alias);
|
||||
})
|
||||
|
||||
//only map if it is actually set
|
||||
@@ -314,11 +164,75 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(type => type.DataTypeId, expression => expression.Ignore())
|
||||
.ForMember(type => type.Mandatory, expression => expression.MapFrom(display => display.Validation.Mandatory))
|
||||
.ForMember(type => type.ValidationRegExp, expression => expression.MapFrom(display => display.Validation.Pattern))
|
||||
.ForMember(type => type.PropertyEditorAlias, expression => expression.MapFrom(display => display.Editor))
|
||||
.ForMember(type => type.DataTypeDefinitionId, expression => expression.MapFrom(display => display.DataTypeId))
|
||||
.ForMember(type => type.Name, expression => expression.MapFrom(display => display.Label));
|
||||
|
||||
#region *** Used for mapping on top of an existing display object from a save object ***
|
||||
|
||||
config.CreateMap<ContentTypeSave, ContentTypeCompositionDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay();
|
||||
|
||||
config.CreateMap<ContentTypeSave, ContentTypeDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay()
|
||||
.ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore())
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
//sync templates
|
||||
var destAllowedTemplateAliases = dest.AllowedTemplates.Select(x => x.Alias);
|
||||
//if the dest is set and it's the same as the source, then don't change
|
||||
if (destAllowedTemplateAliases.SequenceEqual(source.AllowedTemplates) == false)
|
||||
{
|
||||
var templates = applicationContext.Services.FileService.GetTemplates(source.AllowedTemplates.ToArray());
|
||||
dest.AllowedTemplates = source.AllowedTemplates.Select(x => Mapper.Map<EntityBasic>(templates.Single(t => t.Alias == x))).ToArray();
|
||||
}
|
||||
|
||||
if (source.DefaultTemplate.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//if the dest is set and it's the same as the source, then don't change
|
||||
if (dest.DefaultTemplate == null || source.DefaultTemplate != dest.DefaultTemplate.Alias)
|
||||
{
|
||||
var template = applicationContext.Services.FileService.GetTemplate(source.DefaultTemplate);
|
||||
dest.DefaultTemplate = template == null ? null : Mapper.Map<EntityBasic>(template);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dest.DefaultTemplate = null;
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroup>()
|
||||
.ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0))
|
||||
.ForMember(g => g.Key, expression => expression.Ignore())
|
||||
.ForMember(g => g.HasIdentity, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
||||
.ForMember(g => g.ParentId, expression => expression.Ignore())
|
||||
.ForMember(g => g.PropertyTypes, expression => expression.MapFrom(basic => basic.Properties.Select(Mapper.Map<PropertyType>)));
|
||||
|
||||
config.CreateMap<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroupDisplay>()
|
||||
.ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0))
|
||||
.ForMember(g => g.ParentGroupId, expression => expression.Ignore())
|
||||
.ForMember(g => g.ContentTypeId, expression => expression.Ignore())
|
||||
.ForMember(g => g.ParentTabContentTypes, expression => expression.Ignore())
|
||||
.ForMember(g => g.ParentTabContentTypeNames, expression => expression.Ignore())
|
||||
.ForMember(g => g.Properties, expression => expression.MapFrom(display => display.Properties.Select(Mapper.Map<PropertyTypeDisplay>)));
|
||||
|
||||
config.CreateMap<PropertyTypeBasic, PropertyTypeDisplay>()
|
||||
.ForMember(g => g.Editor, expression => expression.Ignore())
|
||||
.ForMember(g => g.View, expression => expression.Ignore())
|
||||
.ForMember(g => g.Config, expression => expression.Ignore())
|
||||
.ForMember(g => g.ContentTypeId, expression => expression.Ignore())
|
||||
.ForMember(g => g.ContentTypeName, expression => expression.Ignore());
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Used as a shared way to do the underlying mapping for content types base classes
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We used to use 'Include' Automapper inheritance functionality and although this works, the unit test
|
||||
/// to assert mappings fails which is an Automapper bug. So instead we will use an extension method for the mappings
|
||||
/// to re-use mappings.
|
||||
/// </remarks>
|
||||
internal static class ContentTypeModelMapperExtensions
|
||||
{
|
||||
|
||||
public static void AfterMapContentTypeSaveToEntity<TSource, TDestination>(
|
||||
TSource source, TDestination dest,
|
||||
ApplicationContext applicationContext)
|
||||
where TSource : ContentTypeSave
|
||||
where TDestination : IContentTypeComposition
|
||||
{
|
||||
//sync compositions
|
||||
var current = dest.CompositionAliases().ToArray();
|
||||
var proposed = source.CompositeContentTypes;
|
||||
|
||||
var remove = current.Where(x => proposed.Contains(x) == false);
|
||||
var add = proposed.Where(x => current.Contains(x) == false);
|
||||
|
||||
foreach (var rem in remove)
|
||||
{
|
||||
dest.RemoveContentType(rem);
|
||||
}
|
||||
|
||||
foreach (var a in add)
|
||||
{
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = applicationContext.Services.ContentTypeService.GetContentType(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
}
|
||||
|
||||
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeSaveToDisplay<TSource, TDestination>(
|
||||
this IMappingExpression<TSource, TDestination> mapping)
|
||||
where TSource : ContentTypeSave
|
||||
where TDestination : ContentTypeCompositionDisplay
|
||||
{
|
||||
return mapping
|
||||
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.ListViewEditorName, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.AvailableCompositeContentTypes, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.Notifications, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.Errors, expression => expression.Ignore());
|
||||
}
|
||||
|
||||
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeEntityToDisplay<TSource, TDestination>(
|
||||
this IMappingExpression<TSource, TDestination> mapping, ApplicationContext applicationContext, Lazy<PropertyEditorResolver> propertyEditorResolver)
|
||||
where TSource : IContentTypeComposition
|
||||
where TDestination : ContentTypeCompositionDisplay
|
||||
{
|
||||
return mapping
|
||||
.ForMember(display => display.Notifications, expression => expression.Ignore())
|
||||
.ForMember(display => display.Errors, expression => expression.Ignore())
|
||||
.ForMember(display => display.AllowAsRoot, expression => expression.MapFrom(type => type.AllowedAsRoot))
|
||||
.ForMember(display => display.ListViewEditorName, expression => expression.Ignore())
|
||||
//Ignore because this is not actually used for content types
|
||||
.ForMember(display => display.Trashed, expression => expression.Ignore())
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AllowedContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select(x => x.Id.Value)))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AvailableCompositeContentTypes,
|
||||
expression => expression.ResolveUsing(new AvailableCompositeContentTypesResolver(applicationContext)))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.CompositeContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.ContentTypeComposition))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.Groups,
|
||||
expression => expression.ResolveUsing(new PropertyTypeGroupResolver(applicationContext, propertyEditorResolver)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display -> Entity class base mapping logic
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <typeparam name="TDestination"></typeparam>
|
||||
/// <param name="mapping"></param>
|
||||
/// <param name="applicationContext"></param>
|
||||
/// <returns></returns>
|
||||
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeSaveToEntity<TSource, TDestination>(
|
||||
this IMappingExpression<TSource, TDestination> mapping, ApplicationContext applicationContext)
|
||||
//where TSource : ContentTypeCompositionDisplay
|
||||
where TSource : ContentTypeSave
|
||||
where TDestination : IContentTypeComposition
|
||||
{
|
||||
return mapping
|
||||
//only map id if set to something higher then zero
|
||||
.ForMember(dto => dto.Id, expression => expression.Condition(display => (Convert.ToInt32(display.Id) > 0)))
|
||||
.ForMember(dto => dto.Id, expression => expression.MapFrom(display => Convert.ToInt32(display.Id)))
|
||||
|
||||
//These get persisted as part of the saving procedure, nothing to do with the display model
|
||||
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
||||
|
||||
.ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot))
|
||||
.ForMember(dto => dto.CreatorId, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.Level, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.SortOrder, expression => expression.Ignore())
|
||||
//ignore, we'll do this in after map
|
||||
.ForMember(dto => dto.PropertyGroups, expression => expression.Ignore())
|
||||
|
||||
.ForMember(
|
||||
dto => dto.AllowedContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i))))
|
||||
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
|
||||
var addedProperties = new List<string>();
|
||||
|
||||
//get all properties from groups that are not generic properties or inhertied (-666 id)
|
||||
var selfNonGenericGroups = source.Groups.Where(x => x.Inherited == false && x.Id != -666).ToArray();
|
||||
|
||||
foreach (var group in selfNonGenericGroups)
|
||||
{
|
||||
//use underlying logic to add the property group which should wire most things up for us
|
||||
dest.AddPropertyGroup(group.Name);
|
||||
|
||||
//now update that group with the values from the display object
|
||||
Mapper.Map(group, dest.PropertyGroups[group.Name]);
|
||||
|
||||
foreach (var propType in group.Properties.Where(x => x.Inherited == false))
|
||||
{
|
||||
//update existing
|
||||
if (propType.Id > 0)
|
||||
{
|
||||
var currentPropertyType = dest.PropertyTypes.FirstOrDefault(x => x.Id == propType.Id);
|
||||
Mapper.Map(propType, currentPropertyType);
|
||||
}
|
||||
else
|
||||
{
|
||||
//add new
|
||||
var mapped = Mapper.Map<PropertyType>(propType);
|
||||
dest.AddPropertyType(mapped, group.Name);
|
||||
}
|
||||
|
||||
addedProperties.Add(propType.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
//Groups to remove
|
||||
var groupsToRemove = dest.PropertyGroups.Select(x => x.Name).Except(selfNonGenericGroups.Select(x => x.Name)).ToArray();
|
||||
foreach (var toRemove in groupsToRemove)
|
||||
{
|
||||
dest.RemovePropertyGroup(toRemove);
|
||||
}
|
||||
|
||||
//add generic properties
|
||||
var genericProperties = source.Groups.FirstOrDefault(x => x.Id == -666);
|
||||
if (genericProperties != null)
|
||||
{
|
||||
foreach (var propertyTypeBasic in genericProperties.Properties.Where(x => x.Inherited == false))
|
||||
{
|
||||
dest.AddPropertyType(Mapper.Map<PropertyType>(propertyTypeBasic));
|
||||
addedProperties.Add(propertyTypeBasic.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
//remove deleted types
|
||||
foreach (var removedType in dest.PropertyTypes
|
||||
.Where(x => addedProperties.Contains(x.Alias) == false).ToList())
|
||||
{
|
||||
dest.RemovePropertyType(removedType.Alias);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
};
|
||||
|
||||
config.CreateMap<PropertyEditor, DataTypeBasic>()
|
||||
.ForMember(x => x.HasPrevalues, expression => expression.Ignore())
|
||||
.ForMember(x => x.IsSystemDataType, expression => expression.Ignore())
|
||||
.ForMember(x => x.Id, expression => expression.Ignore())
|
||||
.ForMember(x => x.Trashed, expression => expression.Ignore())
|
||||
@@ -44,6 +45,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(x => x.AdditionalData, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<IDataTypeDefinition, DataTypeBasic>()
|
||||
.ForMember(x => x.HasPrevalues, expression => expression.Ignore())
|
||||
.ForMember(x => x.Icon, expression => expression.Ignore())
|
||||
.ForMember(x => x.Alias, expression => expression.Ignore())
|
||||
.ForMember(x => x.Group, expression => expression.Ignore())
|
||||
@@ -65,6 +67,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
new PreValueDisplayResolver(lazyDataTypeService)))
|
||||
.ForMember(display => display.SelectedEditor, expression => expression.MapFrom(
|
||||
definition => definition.PropertyEditorAlias.IsNullOrWhiteSpace() ? null : definition.PropertyEditorAlias))
|
||||
.ForMember(x => x.HasPrevalues, expression => expression.Ignore())
|
||||
.ForMember(x => x.Notifications, expression => expression.Ignore())
|
||||
.ForMember(x => x.Icon, expression => expression.Ignore())
|
||||
.ForMember(x => x.Alias, expression => expression.Ignore())
|
||||
|
||||
@@ -52,18 +52,18 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(dto => dto.Trashed, expression => expression.Ignore())
|
||||
.ForMember(x => x.AdditionalData, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<EntityBasic, ITemplate>()
|
||||
.ConstructUsing(basic => new Template(basic.Name, basic.Alias)
|
||||
{
|
||||
Id = Convert.ToInt32(basic.Id),
|
||||
Key = basic.Key
|
||||
})
|
||||
.ForMember(t => t.Path, expression => expression.Ignore())
|
||||
.ForMember(t => t.Id, expression => expression.MapFrom(template => Convert.ToInt32(template.Id)))
|
||||
.ForMember(x => x.VirtualPath, expression => expression.Ignore())
|
||||
.ForMember(x => x.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(x => x.UpdateDate, expression => expression.Ignore())
|
||||
.ForMember(x => x.Content, expression => expression.Ignore());
|
||||
//config.CreateMap<EntityBasic, ITemplate>()
|
||||
// .ConstructUsing(basic => new Template(basic.Name, basic.Alias)
|
||||
// {
|
||||
// Id = Convert.ToInt32(basic.Id),
|
||||
// Key = basic.Key
|
||||
// })
|
||||
// .ForMember(t => t.Path, expression => expression.Ignore())
|
||||
// .ForMember(t => t.Id, expression => expression.MapFrom(template => Convert.ToInt32(template.Id)))
|
||||
// .ForMember(x => x.VirtualPath, expression => expression.Ignore())
|
||||
// .ForMember(x => x.CreateDate, expression => expression.Ignore())
|
||||
// .ForMember(x => x.UpdateDate, expression => expression.Ignore())
|
||||
// .ForMember(x => x.Content, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<EntityBasic, ContentTypeSort>()
|
||||
.ForMember(x => x.Id, expression => expression.MapFrom(entity => new Lazy<int>(() => Convert.ToInt32(entity.Id))))
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
Label = p.Name,
|
||||
View = editor.ValueEditor.View,
|
||||
Config = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals) ,
|
||||
Value = "",
|
||||
//Value = "",
|
||||
ContentTypeId = contentType.Id,
|
||||
ContentTypeName = contentType.Name,
|
||||
GroupId = groupId,
|
||||
|
||||
@@ -304,7 +304,11 @@
|
||||
<Compile Include="Models\ContentEditing\ContentTypeDisplay.cs" />
|
||||
<Compile Include="Media\EmbedProviders\Flickr.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentTypeCompositionDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentTypeSave.cs" />
|
||||
<Compile Include="Models\ContentEditing\PropertyGroupBasic.cs" />
|
||||
<Compile Include="Models\ContentEditing\PropertyTypeBasic.cs" />
|
||||
<Compile Include="Models\ContentEditing\SimpleNotificationModel.cs" />
|
||||
<Compile Include="Models\Mapping\ContentTypeModelMapperExtensions.cs" />
|
||||
<Compile Include="Models\PublishedContentWithKeyBase.cs" />
|
||||
<Compile Include="PropertyEditors\DatePreValueEditor.cs" />
|
||||
<Compile Include="PropertyEditors\DecimalPropertyEditor.cs" />
|
||||
@@ -609,7 +613,9 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\GenericProperties\GenericProperty.ascx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\create.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\create.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\create\script.ascx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
@@ -623,8 +629,12 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\create\xslt.ascx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\Installer.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\cruds.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\Installer.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\cruds.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\Trees\loadPackager.cs" />
|
||||
<Compile Include="PublishedCache\XmlPublishedCache\XmlCacheFilePersister.cs" />
|
||||
<Compile Include="UmbracoComponentRenderer.cs" />
|
||||
@@ -830,14 +840,30 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\PasswordChanger.ascx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\ContentTypeControlNew.ascx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\Tree\TreeControl.ascx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\create\simple.ascx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\directoryBrowser.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\ProtectPage.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\emptyTrashcan.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\create.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\AssignDomain.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\ContentTypeControlNew.ascx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\Tree\TreeControl.ascx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\create\simple.ascx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\directoryBrowser.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\ProtectPage.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\emptyTrashcan.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\create.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\AssignDomain.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\AssignDomain2.aspx.cs">
|
||||
<DependentUpon>AssignDomain2.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
@@ -845,10 +871,16 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\AssignDomain2.aspx.designer.cs">
|
||||
<DependentUpon>AssignDomain2.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\users\EditUser.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\users\EditUser.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UmbracoContextExtensions.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\stylesheet\editstylesheet.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\stylesheet\property\EditStyleSheetProperty.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\stylesheet\editstylesheet.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\stylesheet\property\EditStyleSheetProperty.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Controllers\UmbLoginController.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\Trees\TranslateTreeNames.cs" />
|
||||
<Compile Include="UrlHelperExtensions.cs" />
|
||||
@@ -873,6 +905,8 @@
|
||||
<Compile Include="WebApi\MemberAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\MvcVersionCheck.cs" />
|
||||
<Compile Include="WebApi\NamespaceHttpControllerSelector.cs" />
|
||||
<Compile Include="WebApi\PrefixlessBodyModelValidator.cs" />
|
||||
<Compile Include="WebApi\PrefixlessBodyModelValidatorAttribute.cs" />
|
||||
<Compile Include="WebApi\UmbracoApiController.cs" />
|
||||
<Compile Include="WebApi\UmbracoApiControllerBase.cs" />
|
||||
<Compile Include="WebApi\UmbracoApiControllerResolver.cs" />
|
||||
@@ -984,37 +1018,57 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\channels\rsd.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\moveOrCopy.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\moveOrCopy.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\sort.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\publish.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\publish.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\helpRedirect.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\Help.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\macroResultWrapper.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\macroResultWrapper.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\masterpages\default.Master.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\publishStatus.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\publishStatus.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\Default.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\tree.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\tree.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\ping.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\create\PartialViewMacrosTasks.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\create\PartialViewTasks.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Macros\editMacro.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Python\editPython.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Xslt\editXslt.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Macros\editMacro.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Python\editPython.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Xslt\editXslt.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\editMacro.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\umbracoField.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\treePicker.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\umbracoField.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\treePicker.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\masterpages\umbracoPage.master.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
@@ -1025,9 +1079,15 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\Search\QuickSearch.ascx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\EditNodeTypeNew.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\editTemplate.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\modals\ShowUmbracoTags.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\EditNodeTypeNew.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\editTemplate.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\modals\ShowUmbracoTags.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\templateControls\Image.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\uQuery\IGetProperty.cs" />
|
||||
<Compile Include="umbraco.presentation\XsltExtensionAttribute.cs" />
|
||||
@@ -1056,7 +1116,9 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\nodeFactory\Nodes.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\nodeFactory\Property.cs" />
|
||||
<Compile Include="Routing\UmbracoRequestEventArgs.cs" />
|
||||
<Compile Include="UmbracoUserControl.cs" />
|
||||
<Compile Include="UmbracoUserControl.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="WebApi\UmbracoAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\UmbracoAuthorizedApiController.cs" />
|
||||
<Compile Include="WebApi\Filters\ValidationFilterAttribute.cs" />
|
||||
@@ -1145,24 +1207,28 @@
|
||||
<Compile Include="umbraco.presentation\UmbracoServerUtility.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\actions\delete.aspx.cs">
|
||||
<DependentUpon>delete.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\actions\delete.aspx.designer.cs">
|
||||
<DependentUpon>delete.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\actions\editContent.aspx.cs">
|
||||
<DependentUpon>editContent.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\actions\editContent.aspx.designer.cs">
|
||||
<DependentUpon>editContent.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\actions\preview.aspx.cs">
|
||||
<DependentUpon>preview.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\actions\preview.aspx.designer.cs">
|
||||
<DependentUpon>preview.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\actions\publish.aspx.cs">
|
||||
<DependentUpon>publish.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\actions\publish.aspx.designer.cs">
|
||||
<DependentUpon>publish.aspx</DependentUpon>
|
||||
@@ -1205,7 +1271,9 @@
|
||||
<DependentUpon>ProgressBar.ascx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\SaveClickEventArgs.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\Tree\CustomTreeControl.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\Tree\CustomTreeControl.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\controls\Tree\CustomTreeService.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
@@ -1236,12 +1304,14 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\create\XsltTasks.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dashboard\FeedProxy.aspx.cs">
|
||||
<DependentUpon>FeedProxy.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dashboard\FeedProxy.aspx.designer.cs">
|
||||
<DependentUpon>FeedProxy.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\RelationTypes\EditRelationType.aspx.cs">
|
||||
<DependentUpon>EditRelationType.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\RelationTypes\EditRelationType.aspx.designer.cs">
|
||||
<DependentUpon>EditRelationType.aspx</DependentUpon>
|
||||
@@ -1262,6 +1332,7 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\RelationTypes\TreeMenu\ActionNewRelationType.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\Preview.aspx.cs">
|
||||
<DependentUpon>Preview.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\Preview.aspx.designer.cs">
|
||||
<DependentUpon>Preview.aspx</DependentUpon>
|
||||
@@ -1278,42 +1349,49 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\Search\ExamineEvents.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Xslt\xsltVisualize.aspx.cs">
|
||||
<DependentUpon>xsltVisualize.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Xslt\xsltVisualize.aspx.designer.cs">
|
||||
<DependentUpon>xsltVisualize.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\insertMasterpageContent.aspx.cs">
|
||||
<DependentUpon>insertMasterpageContent.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\insertMasterpageContent.aspx.designer.cs">
|
||||
<DependentUpon>insertMasterpageContent.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\insertMasterpagePlaceholder.aspx.cs">
|
||||
<DependentUpon>insertMasterpagePlaceholder.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\insertMasterpagePlaceholder.aspx.designer.cs">
|
||||
<DependentUpon>insertMasterpagePlaceholder.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\mediaPicker.aspx.cs">
|
||||
<DependentUpon>mediaPicker.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\mediaPicker.aspx.designer.cs">
|
||||
<DependentUpon>mediaPicker.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\republish.aspx.cs">
|
||||
<DependentUpon>republish.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\republish.aspx.designer.cs">
|
||||
<DependentUpon>republish.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\search.aspx.cs">
|
||||
<DependentUpon>search.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\search.aspx.designer.cs">
|
||||
<DependentUpon>search.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\SendPublish.aspx.cs">
|
||||
<DependentUpon>SendPublish.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\SendPublish.aspx.designer.cs">
|
||||
<DependentUpon>SendPublish.aspx</DependentUpon>
|
||||
@@ -1330,6 +1408,7 @@
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\create\media.ascx.cs">
|
||||
<DependentUpon>media.ascx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\create\media.ascx.designer.cs">
|
||||
<DependentUpon>media.ascx</DependentUpon>
|
||||
@@ -1360,30 +1439,35 @@
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Macros\assemblyBrowser.aspx.cs">
|
||||
<DependentUpon>assemblyBrowser.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Macros\assemblyBrowser.aspx.designer.cs">
|
||||
<DependentUpon>assemblyBrowser.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\autoDoc.aspx.cs">
|
||||
<DependentUpon>autoDoc.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\autoDoc.aspx.designer.cs">
|
||||
<DependentUpon>autoDoc.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\BrowseRepository.aspx.cs">
|
||||
<DependentUpon>BrowseRepository.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\BrowseRepository.aspx.designer.cs">
|
||||
<DependentUpon>BrowseRepository.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\editPackage.aspx.cs">
|
||||
<DependentUpon>editPackage.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\editPackage.aspx.designer.cs">
|
||||
<DependentUpon>editPackage.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\installedPackage.aspx.cs">
|
||||
<DependentUpon>installedPackage.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\installedPackage.aspx.designer.cs">
|
||||
<DependentUpon>installedPackage.aspx</DependentUpon>
|
||||
@@ -1415,90 +1499,106 @@
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Xslt\xsltInsertValueOf.aspx.cs">
|
||||
<DependentUpon>xsltInsertValueOf.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Xslt\xsltInsertValueOf.aspx.designer.cs">
|
||||
<DependentUpon>xsltInsertValueOf.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\about.aspx.cs">
|
||||
<DependentUpon>about.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\about.aspx.designer.cs">
|
||||
<DependentUpon>about.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\exportDocumenttype.aspx.cs">
|
||||
<DependentUpon>exportDocumenttype.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\imageViewer.aspx.cs">
|
||||
<DependentUpon>imageViewer.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\imageViewer.aspx.designer.cs">
|
||||
<DependentUpon>imageViewer.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\importDocumenttype.aspx.cs">
|
||||
<DependentUpon>importDocumenttype.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\insertMacro.aspx.cs">
|
||||
<DependentUpon>insertMacro.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\insertMacro.aspx.designer.cs">
|
||||
<DependentUpon>insertMacro.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\insertTable.aspx.cs">
|
||||
<DependentUpon>insertTable.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\insertTable.aspx.designer.cs">
|
||||
<DependentUpon>insertTable.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\notifications.aspx.cs">
|
||||
<DependentUpon>notifications.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\notifications.aspx.designer.cs">
|
||||
<DependentUpon>notifications.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\RegexWs.aspx.cs">
|
||||
<DependentUpon>RegexWs.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\RegexWs.aspx.designer.cs">
|
||||
<DependentUpon>RegexWs.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\rollBack.aspx.cs">
|
||||
<DependentUpon>rollBack.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\rollBack.aspx.designer.cs">
|
||||
<DependentUpon>rollBack.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\sendToTranslation.aspx.cs">
|
||||
<DependentUpon>sendToTranslation.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\sendToTranslation.aspx.designer.cs">
|
||||
<DependentUpon>sendToTranslation.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\uploadImage.aspx.cs">
|
||||
<DependentUpon>uploadImage.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\uploadImage.aspx.designer.cs">
|
||||
<DependentUpon>uploadImage.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\viewAuditTrail.aspx.cs">
|
||||
<DependentUpon>viewAuditTrail.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\viewAuditTrail.aspx.designer.cs">
|
||||
<DependentUpon>viewAuditTrail.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\js\language.aspx.cs">
|
||||
<DependentUpon>language.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\js\language.aspx.designer.cs">
|
||||
<DependentUpon>language.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\members\EditMemberGroup.aspx.cs">
|
||||
<DependentUpon>EditMemberGroup.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\members\EditMemberGroup.aspx.designer.cs">
|
||||
<DependentUpon>EditMemberGroup.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\members\EditMemberType.aspx.cs">
|
||||
<DependentUpon>EditMemberType.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\members\EditMemberType.aspx.designer.cs">
|
||||
<DependentUpon>EditMemberType.aspx</DependentUpon>
|
||||
@@ -1512,6 +1612,7 @@
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\members\ViewMembers.aspx.cs">
|
||||
<DependentUpon>ViewMembers.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\members\ViewMembers.aspx.designer.cs">
|
||||
<DependentUpon>ViewMembers.aspx</DependentUpon>
|
||||
@@ -1525,30 +1626,35 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\IModule.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\InsertAnchor.aspx.cs">
|
||||
<DependentUpon>InsertAnchor.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\InsertAnchor.aspx.designer.cs">
|
||||
<DependentUpon>InsertAnchor.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\insertChar.aspx.cs">
|
||||
<DependentUpon>insertChar.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\insertChar.aspx.designer.cs">
|
||||
<DependentUpon>insertChar.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\insertImage.aspx.cs">
|
||||
<DependentUpon>insertImage.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\insertImage.aspx.designer.cs">
|
||||
<DependentUpon>insertImage.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\insertLink.aspx.cs">
|
||||
<DependentUpon>insertLink.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\insertLink.aspx.designer.cs">
|
||||
<DependentUpon>insertLink.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\insertMacro.aspx.cs">
|
||||
<DependentUpon>insertMacro.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\plugins\tinymce3\insertMacro.aspx.designer.cs">
|
||||
<DependentUpon>insertMacro.aspx</DependentUpon>
|
||||
@@ -1568,6 +1674,7 @@
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\DictionaryItemList.aspx.cs">
|
||||
<DependentUpon>DictionaryItemList.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\DictionaryItemList.aspx.designer.cs">
|
||||
<DependentUpon>DictionaryItemList.aspx</DependentUpon>
|
||||
@@ -1581,18 +1688,21 @@
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\editLanguage.aspx.cs">
|
||||
<DependentUpon>editLanguage.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\editLanguage.aspx.designer.cs">
|
||||
<DependentUpon>editLanguage.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\EditMediaType.aspx.cs">
|
||||
<DependentUpon>EditMediaType.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\EditMediaType.aspx.designer.cs">
|
||||
<DependentUpon>EditMediaType.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\settings\scripts\editScript.aspx.cs">
|
||||
<DependentUpon>editScript.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\templateControls\Script.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\Trees\BaseMediaTree.cs" />
|
||||
@@ -1647,23 +1757,28 @@
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\translation\details.aspx.cs">
|
||||
<DependentUpon>details.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\translation\details.aspx.designer.cs">
|
||||
<DependentUpon>details.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\translation\preview.aspx.cs">
|
||||
<DependentUpon>preview.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\translation\preview.aspx.designer.cs">
|
||||
<DependentUpon>preview.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\translation\xml.aspx.cs">
|
||||
<DependentUpon>xml.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\translation\xml.aspx.designer.cs">
|
||||
<DependentUpon>xml.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\treeInit.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\treeInit.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\Trees\BaseContentTree.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\Trees\BaseTree.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\Trees\ContentRecycleBin.cs" />
|
||||
@@ -1706,6 +1821,7 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\urlRewriter\UrlRewriterFormWriter.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\users\EditUserType.aspx.cs">
|
||||
<DependentUpon>EditUserType.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\users\EditUserType.aspx.designer.cs">
|
||||
<DependentUpon>EditUserType.aspx</DependentUpon>
|
||||
@@ -1719,6 +1835,7 @@
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\users\PermissionEditor.aspx.cs">
|
||||
<DependentUpon>PermissionEditor.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\users\PermissionEditor.aspx.designer.cs">
|
||||
<DependentUpon>PermissionEditor.aspx</DependentUpon>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.Metadata;
|
||||
using System.Web.Http.Validation;
|
||||
|
||||
namespace Umbraco.Web.WebApi
|
||||
{
|
||||
/// <summary>
|
||||
/// By default WebApi always appends a prefix to any ModelState error but we don't want this,
|
||||
/// so this is a custom validator that ensures there is no prefix set.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We were already doing this with the content/media/members validation since we had to manually validate because we
|
||||
/// were posting multi-part values. We were always passing in an empty prefix so it worked. However for other editors we
|
||||
/// are validating with normal data annotations (for the most part) and we don't want the prefix there either.
|
||||
/// </remarks>
|
||||
internal class PrefixlessBodyModelValidator : IBodyModelValidator
|
||||
{
|
||||
private readonly IBodyModelValidator _innerValidator;
|
||||
|
||||
public PrefixlessBodyModelValidator(IBodyModelValidator innerValidator)
|
||||
{
|
||||
if (innerValidator == null)
|
||||
{
|
||||
throw new ArgumentNullException("innerValidator");
|
||||
}
|
||||
|
||||
_innerValidator = innerValidator;
|
||||
}
|
||||
|
||||
public bool Validate(object model, Type type, ModelMetadataProvider metadataProvider, HttpActionContext actionContext, string keyPrefix)
|
||||
{
|
||||
// Remove the keyPrefix but otherwise let innerValidator do what it normally does.
|
||||
return _innerValidator.Validate(model, type, metadataProvider, actionContext, string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.Validation;
|
||||
|
||||
namespace Umbraco.Web.WebApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Applying this attribute to any webapi controller will ensure that it only contains one json formatter compatible with the angular json vulnerability prevention.
|
||||
/// </summary>
|
||||
internal class PrefixlessBodyModelValidatorAttribute : Attribute, IControllerConfiguration
|
||||
{
|
||||
public virtual void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
|
||||
{
|
||||
//replace the normal validator with our custom one for this controller
|
||||
controllerSettings.Services.Replace(typeof(IBodyModelValidator),
|
||||
new PrefixlessBodyModelValidator(controllerSettings.Services.GetBodyModelValidator()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Web.Configuration;
|
||||
using System.Web.Http;
|
||||
@@ -103,10 +104,10 @@ namespace Umbraco.Web
|
||||
public override IBootManager Initialize()
|
||||
{
|
||||
//This is basically a hack for this item: http://issues.umbraco.org/issue/U4-5976
|
||||
// when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's
|
||||
// event handlers will be assigned during bootup when the rebuilding starts which is a problem. So with the examine 0.1.58.2941 build
|
||||
// it has an event we can subscribe to in order to cancel this rebuilding process, but what we'll do is cancel it and postpone the rebuilding until the
|
||||
// boot process has completed. It's a hack but it works.
|
||||
// when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's
|
||||
// event handlers will be assigned during bootup when the rebuilding starts which is a problem. So with the examine 0.1.58.2941 build
|
||||
// it has an event we can subscribe to in order to cancel this rebuilding process, but what we'll do is cancel it and postpone the rebuilding until the
|
||||
// boot process has completed. It's a hack but it works.
|
||||
ExamineManager.Instance.BuildingEmptyIndexOnStartup += OnInstanceOnBuildingEmptyIndexOnStartup;
|
||||
|
||||
base.Initialize();
|
||||
|
||||
Reference in New Issue
Block a user