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:
Shannon
2013-09-27 16:59:38 +10:00
parent 82c784d560
commit a3d674f574
27 changed files with 341 additions and 84 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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