Updated the saving model and validator inheritance chain to better support members, have the members editor display data and posting/validating data - now need to get the correct fields being displayed and the correct tab/layout but coming along very nicely!
This commit is contained in:
@@ -347,6 +347,35 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IMemberType"/> object by its Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="IMemberType"/> to retrieve</param>
|
||||
/// <returns><see cref="IMemberType"/></returns>
|
||||
public IMemberType GetMemberType(int id)
|
||||
{
|
||||
using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
return repository.Get(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IMemberType"/> object by its Alias
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of the <see cref="IMemberType"/> to retrieve</param>
|
||||
/// <returns><see cref="IMemberType"/></returns>
|
||||
public IMemberType GetMemberType(string alias)
|
||||
{
|
||||
using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IMemberType>.Builder.Where(x => x.Alias == alias);
|
||||
var contentTypes = repository.GetByQuery(query);
|
||||
|
||||
return contentTypes.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IMediaType"/> object by its Id
|
||||
/// </summary>
|
||||
|
||||
@@ -67,6 +67,20 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="userId">Optional Id of the User deleting the ContentTypes</param>
|
||||
void Delete(IEnumerable<IContentType> contentTypes, int userId = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IMemberType"/> object by its Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="IMediaType"/> to retrieve</param>
|
||||
/// <returns><see cref="IMediaType"/></returns>
|
||||
IMemberType GetMemberType(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IMemberType"/> object by its Alias
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of the <see cref="IMediaType"/> to retrieve</param>
|
||||
/// <returns><see cref="IMediaType"/></returns>
|
||||
IMemberType GetMemberType(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IMediaType"/> object by its Id
|
||||
/// </summary>
|
||||
|
||||
@@ -27,11 +27,17 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
|
||||
|
||||
/** internal method process the saving of data and post processing the result */
|
||||
function saveContentItem(content, action, files) {
|
||||
return umbRequestHelper.postSaveContent(
|
||||
umbRequestHelper.getApiUrl(
|
||||
return umbRequestHelper.postSaveContent({
|
||||
restApiUrl: umbRequestHelper.getApiUrl(
|
||||
"contentApiBaseUrl",
|
||||
"PostSave"),
|
||||
content, action, files);
|
||||
content: content,
|
||||
action: action,
|
||||
files: files,
|
||||
dataFormatter: function (c, a) {
|
||||
return umbDataFormatter.formatContentPostData(c, a);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,11 +7,17 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
|
||||
|
||||
/** internal method process the saving of data and post processing the result */
|
||||
function saveMediaItem(content, action, files) {
|
||||
return umbRequestHelper.postSaveContent(
|
||||
umbRequestHelper.getApiUrl(
|
||||
return umbRequestHelper.postSaveContent({
|
||||
restApiUrl: umbRequestHelper.getApiUrl(
|
||||
"mediaApiBaseUrl",
|
||||
"PostSave"),
|
||||
content, action, files);
|
||||
content: content,
|
||||
action: action,
|
||||
files: files,
|
||||
dataFormatter: function (c, a) {
|
||||
return umbDataFormatter.formatMediaPostData(c, a);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,11 +7,18 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
|
||||
|
||||
/** internal method process the saving of data and post processing the result */
|
||||
function saveMember(content, action, files) {
|
||||
return umbRequestHelper.postSaveContent(
|
||||
umbRequestHelper.getApiUrl(
|
||||
|
||||
return umbRequestHelper.postSaveContent({
|
||||
restApiUrl: umbRequestHelper.getApiUrl(
|
||||
"memberApiBaseUrl",
|
||||
"PostSave"),
|
||||
content, action, files);
|
||||
content: content,
|
||||
action: action,
|
||||
files: files,
|
||||
dataFormatter: function(c, a) {
|
||||
return umbDataFormatter.formatMemberPostData(c, a);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -151,26 +151,43 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
|
||||
},
|
||||
|
||||
/** Used for saving media/content specifically */
|
||||
postSaveContent: function (restApiUrl, content, action, files) {
|
||||
postSaveContent: function (args) {
|
||||
|
||||
if (!args.restApiUrl) {
|
||||
throw "args.restApiUrl is a required argument";
|
||||
}
|
||||
if (!args.content) {
|
||||
throw "args.content is a required argument";
|
||||
}
|
||||
if (!args.action) {
|
||||
throw "args.action is a required argument";
|
||||
}
|
||||
if (!args.files) {
|
||||
throw "args.files is a required argument";
|
||||
}
|
||||
if (!args.dataFormatter) {
|
||||
throw "args.dataFormatter is a required argument";
|
||||
}
|
||||
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
//save the active tab id so we can set it when the data is returned.
|
||||
var activeTab = _.find(content.tabs, function (item) {
|
||||
var activeTab = _.find(args.content.tabs, function (item) {
|
||||
return item.active;
|
||||
});
|
||||
var activeTabIndex = (activeTab === undefined ? 0 : _.indexOf(content.tabs, activeTab));
|
||||
var activeTabIndex = (activeTab === undefined ? 0 : _.indexOf(args.content.tabs, activeTab));
|
||||
|
||||
//save the data
|
||||
this.postMultiPartRequest(
|
||||
restApiUrl,
|
||||
{ key: "contentItem", value: umbDataFormatter.formatContentPostData(content, action) },
|
||||
args.restApiUrl,
|
||||
{ key: "contentItem", value: args.dataFormatter(args.content, args.action) },
|
||||
function (data, formData) {
|
||||
//now add all of the assigned files
|
||||
for (var f in files) {
|
||||
for (var f in args.files) {
|
||||
//each item has a property id and the file object, we'll ensure that the id is suffixed to the key
|
||||
// so we know which property it belongs to on the server side
|
||||
formData.append("file_" + files[f].id, files[f].file);
|
||||
formData.append("file_" + args.files[f].id, args.files[f].file);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@@ -196,24 +196,45 @@ function umbDataFormatter() {
|
||||
return saveModel;
|
||||
},
|
||||
|
||||
/** formats the display model used to display the content to the model used to save the content */
|
||||
formatContentPostData: function (displayModel, action) {
|
||||
/** formats the display model used to display the member to the model used to save the member */
|
||||
formatMemberPostData: function(displayModel, action) {
|
||||
//this is basically the same as for media but we need to explicitly add the username,email to the save model
|
||||
var saveModel = this.formatMediaPostData(displayModel, action);
|
||||
|
||||
var genericTab = _.find(displayModel.tabs, function (item) {
|
||||
return item.id === 0;
|
||||
});
|
||||
|
||||
var propLogin = _.find(genericTab.properties, function (item) {
|
||||
return item.alias === "_umb_login";
|
||||
});
|
||||
var propEmail = _.find(genericTab.properties, function (item) {
|
||||
return item.alias === "_umb_email";
|
||||
});
|
||||
saveModel.email = propEmail.value;
|
||||
saveModel.username = propLogin.value;
|
||||
|
||||
return saveModel;
|
||||
},
|
||||
|
||||
/** formats the display model used to display the media to the model used to save the media */
|
||||
formatMediaPostData: function(displayModel, action) {
|
||||
//NOTE: the display model inherits from the save model so we can in theory just post up the display model but
|
||||
// we don't want to post all of the data as it is unecessary.
|
||||
var saveModel = {
|
||||
id: displayModel.id,
|
||||
properties: [],
|
||||
name: displayModel.name,
|
||||
contentTypeAlias : displayModel.contentTypeAlias,
|
||||
contentTypeAlias: displayModel.contentTypeAlias,
|
||||
parentId: displayModel.parentId,
|
||||
//set the action on the save model
|
||||
action: action
|
||||
};
|
||||
|
||||
|
||||
_.each(displayModel.tabs, function (tab) {
|
||||
|
||||
|
||||
_.each(tab.properties, function (prop) {
|
||||
|
||||
|
||||
//don't include the custom generic tab properties
|
||||
if (!prop.alias.startsWith("_umb_")) {
|
||||
saveModel.properties.push({
|
||||
@@ -222,25 +243,36 @@ function umbDataFormatter() {
|
||||
value: prop.value
|
||||
});
|
||||
}
|
||||
else {
|
||||
//here we need to map some of our internal properties to the content save item
|
||||
|
||||
switch (prop.alias) {
|
||||
case "_umb_expiredate":
|
||||
saveModel.expireDate = prop.value;
|
||||
break;
|
||||
case "_umb_releasedate":
|
||||
saveModel.releaseDate = prop.value;
|
||||
break;
|
||||
case "_umb_template":
|
||||
saveModel.templateAlias = prop.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
return saveModel;
|
||||
},
|
||||
|
||||
/** formats the display model used to display the content to the model used to save the content */
|
||||
formatContentPostData: function (displayModel, action) {
|
||||
|
||||
//this is basically the same as for media but we need to explicitly add some extra properties
|
||||
var saveModel = this.formatMediaPostData(displayModel, action);
|
||||
|
||||
var genericTab = _.find(displayModel.tabs, function (item) {
|
||||
return item.id === 0;
|
||||
});
|
||||
|
||||
var propExpireDate = _.find(genericTab.properties, function(item) {
|
||||
return item.alias === "_umb_expiredate";
|
||||
});
|
||||
var propReleaseDate = _.find(genericTab.properties, function (item) {
|
||||
return item.alias === "_umb_releasedate";
|
||||
});
|
||||
var propTemplate = _.find(genericTab.properties, function (item) {
|
||||
return item.alias === "_umb_template";
|
||||
});
|
||||
saveModel.expireDate = propExpireDate.value;
|
||||
saveModel.releaseDate = propReleaseDate.value;
|
||||
saveModel.templateAlias = propTemplate.value;
|
||||
|
||||
return saveModel;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace Umbraco.Web.Editors
|
||||
[ContentPostValidate]
|
||||
public ContentItemDisplay PostSave(
|
||||
[ModelBinder(typeof(ContentItemBinder))]
|
||||
ContentItemSave<IContent> contentItem)
|
||||
ContentItemSave contentItem)
|
||||
{
|
||||
//If we've reached here it means:
|
||||
// * Our model has been bound
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Umbraco.Web.Editors
|
||||
return errorResponse;
|
||||
}
|
||||
|
||||
protected void UpdateName<TPersisted>(ContentItemSave<TPersisted> contentItem)
|
||||
protected void UpdateName<TPersisted>(ContentBaseItemSave<TPersisted> contentItem)
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
//Don't update the name if it is empty
|
||||
@@ -78,7 +78,7 @@ namespace Umbraco.Web.Editors
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void MapPropertyValues<TPersisted>(ContentItemSave<TPersisted> contentItem)
|
||||
protected void MapPropertyValues<TPersisted>(ContentBaseItemSave<TPersisted> contentItem)
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
//Map the property values
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
public override void OnActionExecuting(HttpActionContext actionContext)
|
||||
{
|
||||
var contentItem = (ContentItemSave<IContent>)actionContext.ActionArguments["contentItem"];
|
||||
var contentItem = (ContentItemSave)actionContext.ActionArguments["contentItem"];
|
||||
|
||||
//We now need to validate that the user is allowed to be doing what they are doing.
|
||||
//Based on the action we need to check different permissions.
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Umbraco.Web.Editors
|
||||
switch (dataType.Action)
|
||||
{
|
||||
case ContentSaveAction.Save:
|
||||
persisted = DataTypeService.GetDataTypeDefinitionById(dataType.Id);
|
||||
persisted = DataTypeService.GetDataTypeDefinitionById(Convert.ToInt32(dataType.Id));
|
||||
if (persisted == null)
|
||||
{
|
||||
var message = string.Format("Data type with id: {0} was not found", dataType.Id);
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Umbraco.Web.Editors
|
||||
[MediaPostValidate]
|
||||
public MediaItemDisplay PostSave(
|
||||
[ModelBinder(typeof(MediaItemBinder))]
|
||||
ContentItemSave<IMedia> contentItem)
|
||||
MediaItemSave contentItem)
|
||||
{
|
||||
//If we've reached here it means:
|
||||
// * Our model has been bound
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
public override void OnActionExecuting(HttpActionContext actionContext)
|
||||
{
|
||||
var mediaItem = (ContentItemSave<IMedia>)actionContext.ActionArguments["contentItem"];
|
||||
var mediaItem = (MediaItemSave)actionContext.ActionArguments["contentItem"];
|
||||
|
||||
//We now need to validate that the user is allowed to be doing what they are doing.
|
||||
//Then if it is new, we need to lookup those permissions on the parent.
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using AutoMapper;
|
||||
using Examine.LuceneEngine.SearchCriteria;
|
||||
using Examine.SearchCriteria;
|
||||
@@ -11,7 +12,9 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi.Binders;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using umbraco;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
using Examine;
|
||||
using System.Web.Security;
|
||||
@@ -67,5 +70,65 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves member
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[FileUploadCleanupFilter]
|
||||
public MemberDisplay PostSave(
|
||||
[ModelBinder(typeof(MemberBinder))]
|
||||
MemberSave contentItem)
|
||||
{
|
||||
//If we've reached here it means:
|
||||
// * Our model has been bound
|
||||
// * and validated
|
||||
// * any file attachments have been saved to their temporary location for us to use
|
||||
// * we have a reference to the DTO object and the persisted object
|
||||
// * Permissions are valid
|
||||
|
||||
UpdateName(contentItem);
|
||||
|
||||
MapPropertyValues(contentItem);
|
||||
|
||||
//We need to manually check the validation results here because:
|
||||
// * We still need to save the entity even if there are validation value errors
|
||||
// * Depending on if the entity is new, and if there are non property validation errors (i.e. the name is null)
|
||||
// then we cannot continue saving, we can only display errors
|
||||
// * If there are validation errors and they were attempting to publish, we can only save, NOT publish and display
|
||||
// a message indicating this
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
if (ValidationHelper.ModelHasRequiredForPersistenceErrors(contentItem)
|
||||
&& (contentItem.Action == ContentSaveAction.SaveNew))
|
||||
{
|
||||
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
|
||||
// add the modelstate to the outgoing object and throw validation response
|
||||
var forDisplay = Mapper.Map<IMember, MemberDisplay>(contentItem.PersistedContent);
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
|
||||
}
|
||||
}
|
||||
|
||||
//save the item
|
||||
Services.MemberService.Save(contentItem.PersistedContent);
|
||||
|
||||
//return the updated model
|
||||
var display = Mapper.Map<IMember, MemberDisplay>(contentItem.PersistedContent);
|
||||
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
HandleInvalidModelState(display);
|
||||
|
||||
//put the correct msgs in
|
||||
switch (contentItem.Action)
|
||||
{
|
||||
case ContentSaveAction.Save:
|
||||
case ContentSaveAction.SaveNew:
|
||||
display.AddSuccessNotification(ui.Text("speechBubbles", "editMemberSaved"), ui.Text("speechBubbles", "editMemberSaved"));
|
||||
break;
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
|
||||
[DataMember(Name = "sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
|
||||
protected bool Equals(ContentItemBasic other)
|
||||
{
|
||||
return Id == other.Id;
|
||||
@@ -50,7 +50,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id;
|
||||
return Id.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@ using Umbraco.Core.Models;
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// A model representing a content item to be saved
|
||||
/// A model representing a content base item to be saved
|
||||
/// </summary>
|
||||
[DataContract(Name = "content", Namespace = "")]
|
||||
public class ContentItemSave<TPersisted> : ContentItemBasic<ContentPropertyBasic, TPersisted>, IHaveUploadedFiles
|
||||
where TPersisted : IContentBase
|
||||
public abstract class ContentBaseItemSave<TPersisted> : ContentItemBasic<ContentPropertyBasic, TPersisted>, IHaveUploadedFiles
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
public ContentItemSave()
|
||||
protected ContentBaseItemSave()
|
||||
{
|
||||
UploadedFiles = new List<ContentItemFile>();
|
||||
}
|
||||
@@ -26,6 +26,25 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[Required]
|
||||
public ContentSaveAction Action { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<ContentItemFile> UploadedFiles { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A model representing a media item to be saved
|
||||
/// </summary>
|
||||
[DataContract(Name = "content", Namespace = "")]
|
||||
public class MediaItemSave : ContentBaseItemSave<IMedia>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A model representing a content item to be saved
|
||||
/// </summary>
|
||||
[DataContract(Name = "content", Namespace = "")]
|
||||
public class ContentItemSave : ContentBaseItemSave<IContent>
|
||||
{
|
||||
/// <summary>
|
||||
/// The template alias to save
|
||||
/// </summary>
|
||||
@@ -38,10 +57,5 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataMember(Name = "expireDate")]
|
||||
public DateTime? ExpireDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The collection of files uploaded
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public List<ContentItemFile> UploadedFiles { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
|
||||
[DataMember(Name = "id", IsRequired = true)]
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
public object Id { get; set; }
|
||||
|
||||
[DataMember(Name = "icon")]
|
||||
public string Icon { get; set; }
|
||||
|
||||
20
src/Umbraco.Web/Models/ContentEditing/MemberSave.cs
Normal file
20
src/Umbraco.Web/Models/ContentEditing/MemberSave.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Validation;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// A model representing a member to be saved
|
||||
/// </summary>
|
||||
public class MemberSave : ContentBaseItemSave<IMember>
|
||||
{
|
||||
[DataMember(Name = "username", IsRequired = true)]
|
||||
[RequiredForPersistence(AllowEmptyStrings = false, ErrorMessage = "Required")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[DataMember(Name = "email", IsRequired = true)]
|
||||
[RequiredForPersistence(AllowEmptyStrings = false, ErrorMessage = "Required")]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
expression => expression.MapFrom(content => content.ContentType.Alias));
|
||||
|
||||
//FROM IMember TO ContentItemDto<IMember>
|
||||
config.CreateMap<IMember, ContentItemDto<IMedia>>()
|
||||
config.CreateMap<IMember, ContentItemDto<IMember>>()
|
||||
.ForMember(
|
||||
dto => dto.Owner,
|
||||
expression => expression.ResolveUsing<OwnerResolver<IMember>>());
|
||||
@@ -77,7 +77,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}template", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Alias = string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = ui.Text("general", "email"),
|
||||
Value = display.Email,
|
||||
View = "textbox"
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
Alias = string.Format("{0}id", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = "Id",
|
||||
Value = display.Id.ToInvariantString(),
|
||||
Value = Convert.ToInt32(display.Id).ToInvariantString(),
|
||||
View = labelEditor
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
|
||||
@@ -315,6 +315,7 @@
|
||||
<Compile Include="Models\ContentEditing\AuditLog.cs" />
|
||||
<Compile Include="Models\ContentEditing\MacroParameter.cs" />
|
||||
<Compile Include="Models\ContentEditing\MemberDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\MemberSave.cs" />
|
||||
<Compile Include="Models\ContentEditing\UmbracoEntityTypes.cs" />
|
||||
<Compile Include="Models\ContentEditing\AuditLogType.cs" />
|
||||
<Compile Include="Models\Mapping\LogModelMapper.cs" />
|
||||
@@ -369,6 +370,7 @@
|
||||
<Compile Include="Routing\UrlProviderExtensions.cs" />
|
||||
<Compile Include="Trees\DataTypeTreeController.cs" />
|
||||
<Compile Include="Trees\Menu\CreateChildEntity.cs" />
|
||||
<Compile Include="WebApi\Binders\MemberBinder.cs" />
|
||||
<Compile Include="WebApi\ControllerContextExtensions.cs" />
|
||||
<Compile Include="WebApi\CustomDateTimeConvertor.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentSortOrder.cs" />
|
||||
|
||||
@@ -33,8 +33,9 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
/// <summary>
|
||||
/// Binds the content model to the controller action for the posted multi-part Post
|
||||
/// </summary>
|
||||
internal abstract class ContentItemBaseBinder<TPersisted> : IModelBinder
|
||||
internal abstract class ContentItemBaseBinder<TPersisted, TModelSave> : IModelBinder
|
||||
where TPersisted : class, IContentBase
|
||||
where TModelSave : ContentBaseItemSave<TPersisted>
|
||||
{
|
||||
protected ApplicationContext ApplicationContext { get; private set; }
|
||||
|
||||
@@ -69,7 +70,7 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
}
|
||||
|
||||
//now that everything is binded, validate the properties
|
||||
var contentItemValidator = new ContentItemValidationHelper<TPersisted>(ApplicationContext);
|
||||
var contentItemValidator = new ContentItemValidationHelper<TPersisted, TModelSave>(ApplicationContext);
|
||||
contentItemValidator.ValidateItem(actionContext, x.Result);
|
||||
|
||||
bindingContext.Model = x.Result;
|
||||
@@ -87,7 +88,7 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
/// <param name="bindingContext"></param>
|
||||
/// <param name="provider"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<ContentItemSave<TPersisted>> GetModel(HttpActionContext actionContext, ModelBindingContext bindingContext, MultipartFormDataStreamProvider provider)
|
||||
private async Task<TModelSave> GetModel(HttpActionContext actionContext, ModelBindingContext bindingContext, MultipartFormDataStreamProvider provider)
|
||||
{
|
||||
var request = actionContext.Request;
|
||||
|
||||
@@ -115,13 +116,13 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
var contentItem = result.FormData["contentItem"];
|
||||
|
||||
//deserialize into our model
|
||||
var model = JsonConvert.DeserializeObject<ContentItemSave<TPersisted>>(contentItem);
|
||||
var model = JsonConvert.DeserializeObject<TModelSave>(contentItem);
|
||||
|
||||
//get the default body validator and validate the object
|
||||
var bodyValidator = actionContext.ControllerContext.Configuration.Services.GetBodyModelValidator();
|
||||
var metadataProvider = actionContext.ControllerContext.Configuration.Services.GetModelMetadataProvider();
|
||||
//all validation errors will not contain a prefix
|
||||
bodyValidator.Validate(model, typeof (ContentItemSave<TPersisted>), metadataProvider, actionContext, "");
|
||||
bodyValidator.Validate(model, typeof(TModelSave), metadataProvider, actionContext, "");
|
||||
|
||||
//get the files
|
||||
foreach (var file in result.FileData)
|
||||
@@ -187,7 +188,7 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
/// </summary>
|
||||
/// <param name="saveModel"></param>
|
||||
/// <param name="dto"></param>
|
||||
private static void MapPropertyValuesFromSaved(ContentItemSave<TPersisted> saveModel, ContentItemDto<TPersisted> dto)
|
||||
private static void MapPropertyValuesFromSaved(TModelSave saveModel, ContentItemDto<TPersisted> dto)
|
||||
{
|
||||
foreach (var p in saveModel.Properties)
|
||||
{
|
||||
@@ -195,8 +196,8 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract TPersisted GetExisting(ContentItemSave<TPersisted> model);
|
||||
protected abstract TPersisted CreateNew(ContentItemSave<TPersisted> model);
|
||||
protected abstract ContentItemDto<TPersisted> MapFromPersisted(ContentItemSave<TPersisted> model);
|
||||
protected abstract TPersisted GetExisting(TModelSave model);
|
||||
protected abstract TPersisted CreateNew(TModelSave model);
|
||||
protected abstract ContentItemDto<TPersisted> MapFromPersisted(TModelSave model);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using Umbraco.Web.Models.Mapping;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Binders
|
||||
{
|
||||
internal class ContentItemBinder : ContentItemBaseBinder<IContent>
|
||||
internal class ContentItemBinder : ContentItemBaseBinder<IContent, ContentItemSave>
|
||||
{
|
||||
|
||||
public ContentItemBinder(ApplicationContext applicationContext)
|
||||
@@ -23,12 +23,12 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
{
|
||||
}
|
||||
|
||||
protected override IContent GetExisting(ContentItemSave<IContent> model)
|
||||
protected override IContent GetExisting(ContentItemSave model)
|
||||
{
|
||||
return ApplicationContext.Services.ContentService.GetById(model.Id);
|
||||
return ApplicationContext.Services.ContentService.GetById(Convert.ToInt32(model.Id));
|
||||
}
|
||||
|
||||
protected override IContent CreateNew(ContentItemSave<IContent> model)
|
||||
protected override IContent CreateNew(ContentItemSave model)
|
||||
{
|
||||
var contentType = ApplicationContext.Services.ContentTypeService.GetContentType(model.ContentTypeAlias);
|
||||
if (contentType == null)
|
||||
@@ -38,7 +38,7 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
return new Content(model.Name, model.ParentId, contentType);
|
||||
}
|
||||
|
||||
protected override ContentItemDto<IContent> MapFromPersisted(ContentItemSave<IContent> model)
|
||||
protected override ContentItemDto<IContent> MapFromPersisted(ContentItemSave model)
|
||||
{
|
||||
return Mapper.Map<IContent, ContentItemDto<IContent>>(model.PersistedContent);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using Umbraco.Web.Models.Mapping;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Binders
|
||||
{
|
||||
internal class MediaItemBinder : ContentItemBaseBinder<IMedia>
|
||||
internal class MediaItemBinder : ContentItemBaseBinder<IMedia, MediaItemSave>
|
||||
{
|
||||
public MediaItemBinder(ApplicationContext applicationContext)
|
||||
: base(applicationContext)
|
||||
@@ -22,12 +22,12 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
{
|
||||
}
|
||||
|
||||
protected override IMedia GetExisting(ContentItemSave<IMedia> model)
|
||||
protected override IMedia GetExisting(MediaItemSave model)
|
||||
{
|
||||
return ApplicationContext.Services.MediaService.GetById(model.Id);
|
||||
return ApplicationContext.Services.MediaService.GetById(Convert.ToInt32(model.Id));
|
||||
}
|
||||
|
||||
protected override IMedia CreateNew(ContentItemSave<IMedia> model)
|
||||
protected override IMedia CreateNew(MediaItemSave model)
|
||||
{
|
||||
var contentType = ApplicationContext.Services.ContentTypeService.GetMediaType(model.ContentTypeAlias);
|
||||
if (contentType == null)
|
||||
@@ -37,7 +37,7 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
return new Core.Models.Media(model.Name, model.ParentId, contentType);
|
||||
}
|
||||
|
||||
protected override ContentItemDto<IMedia> MapFromPersisted(ContentItemSave<IMedia> model)
|
||||
protected override ContentItemDto<IMedia> MapFromPersisted(MediaItemSave model)
|
||||
{
|
||||
return Mapper.Map<IMedia, ContentItemDto<IMedia>>(model.PersistedContent);
|
||||
}
|
||||
|
||||
44
src/Umbraco.Web/WebApi/Binders/MemberBinder.cs
Normal file
44
src/Umbraco.Web/WebApi/Binders/MemberBinder.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Binders
|
||||
{
|
||||
internal class MemberBinder : ContentItemBaseBinder<IMember, MemberSave>
|
||||
{
|
||||
public MemberBinder(ApplicationContext applicationContext)
|
||||
: base(applicationContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MemberBinder()
|
||||
: this(ApplicationContext.Current)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IMember GetExisting(MemberSave model)
|
||||
{
|
||||
return ApplicationContext.Services.MemberService.GetByUsername(model.Username);
|
||||
}
|
||||
|
||||
protected override IMember CreateNew(MemberSave model)
|
||||
{
|
||||
var contentType = ApplicationContext.Services.ContentTypeService.GetMemberType(model.ContentTypeAlias);
|
||||
if (contentType == null)
|
||||
{
|
||||
throw new InvalidOperationException("No member type found wth alias " + model.ContentTypeAlias);
|
||||
}
|
||||
return new Member(model.Name, model.ParentId, contentType, new PropertyCollection());
|
||||
}
|
||||
|
||||
protected override ContentItemDto<IMember> MapFromPersisted(MemberSave model)
|
||||
{
|
||||
return Mapper.Map<IMember, ContentItemDto<IMember>>(model.PersistedContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,14 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
/// A validation helper class used with ContentItemValidationFilterAttribute to be shared between content, media, etc...
|
||||
/// </summary>
|
||||
/// <typeparam name="TPersisted"></typeparam>
|
||||
/// <typeparam name="TModelSave"></typeparam>
|
||||
/// <remarks>
|
||||
/// If any severe errors occur then the response gets set to an error and execution will not continue. Property validation
|
||||
/// errors will just be added to the ModelState.
|
||||
/// </remarks>
|
||||
internal class ContentItemValidationHelper<TPersisted>
|
||||
internal class ContentItemValidationHelper<TPersisted, TModelSave>
|
||||
where TPersisted : class, IContentBase
|
||||
where TModelSave : ContentBaseItemSave<TPersisted>
|
||||
{
|
||||
private readonly ApplicationContext _applicationContext;
|
||||
|
||||
@@ -38,10 +40,10 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
|
||||
public void ValidateItem(HttpActionContext actionContext, string argumentName)
|
||||
{
|
||||
var contentItem = actionContext.ActionArguments[argumentName] as ContentItemSave<TPersisted>;
|
||||
var contentItem = actionContext.ActionArguments[argumentName] as TModelSave;
|
||||
if (contentItem == null)
|
||||
{
|
||||
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "No " + typeof(ContentItemSave<IContent>) + " found in request");
|
||||
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "No " + typeof(TModelSave) + " found in request");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,7 +51,7 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
|
||||
}
|
||||
|
||||
public void ValidateItem(HttpActionContext actionContext, ContentItemSave<TPersisted> contentItem)
|
||||
public void ValidateItem(HttpActionContext actionContext, TModelSave contentItem)
|
||||
{
|
||||
//now do each validation step
|
||||
if (ValidateExistingContent(contentItem, actionContext) == false) return;
|
||||
@@ -119,7 +121,7 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
if (editor == null)
|
||||
{
|
||||
var message = string.Format("The property editor with alias: {0} was not found for property with id {1}", p.DataType.PropertyEditorAlias, p.Id);
|
||||
LogHelper.Warn<ContentItemValidationHelper<TPersisted>>(message);
|
||||
LogHelper.Warn<ContentItemValidationHelper<TPersisted, TModelSave>>(message);
|
||||
//actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
|
||||
//return false;
|
||||
continue;
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
var toRemove = new List<dynamic>();
|
||||
foreach(dynamic item in items)
|
||||
{
|
||||
var nodePermission = permissions.Where(x => x.EntityId == item.Id).ToArray();
|
||||
var nodePermission = permissions.Where(x => x.EntityId == Convert.ToInt32(item.Id)).ToArray();
|
||||
//if there are no permissions for this id then we need to check what the user's default
|
||||
// permissions are.
|
||||
if (nodePermission.Any() == false)
|
||||
|
||||
Reference in New Issue
Block a user