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:
Shannon
2015-10-27 10:29:11 +01:00
87 changed files with 2197 additions and 1529 deletions

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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

View File

@@ -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; }
}
}

View File

@@ -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
{

View File

@@ -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)
});
}
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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();
}

View File

@@ -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
}
}
}

View File

@@ -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);
}
});
}
}
}

View File

@@ -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())

View File

@@ -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))))

View File

@@ -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,

View File

@@ -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>

View File

@@ -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);
}
}
}

View File

@@ -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()));
}
}
}

View File

@@ -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();