WIP Getting the content controller a bit further, now need to pull apart the content binder
This commit is contained in:
@@ -376,30 +376,7 @@ namespace Umbraco.Core.Models
|
||||
#endregion
|
||||
|
||||
#region Validation
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsValid(string culture = "*")
|
||||
{
|
||||
culture = culture.NullOrWhiteSpaceAsNull();
|
||||
|
||||
if (culture == null)
|
||||
{
|
||||
if (Name.IsNullOrWhiteSpace()) return false;
|
||||
return ValidateProperties(null).Length == 0;
|
||||
}
|
||||
|
||||
if (culture != "*")
|
||||
{
|
||||
var name = GetCultureName(culture);
|
||||
if (name.IsNullOrWhiteSpace()) return false;
|
||||
return ValidateProperties(culture).Length == 0;
|
||||
}
|
||||
|
||||
// 'all cultures'
|
||||
// those that have a name are ok, those without a name... we don't validate
|
||||
return AvailableCultures.All(c => ValidateProperties(c).Length == 0);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Property[] ValidateProperties(string culture = "*")
|
||||
{
|
||||
|
||||
@@ -137,14 +137,7 @@ namespace Umbraco.Core.Models
|
||||
void CopyFrom(IContent other, string culture = "*");
|
||||
|
||||
// fixme validate published cultures?
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the content and property values are valid in order to be persisted.
|
||||
/// </summary>
|
||||
/// <para>If the content type is variant, then culture can be either '*' or an actual culture, but neither 'null' nor
|
||||
/// 'empty'. If the content type is invariant, then culture can be either '*' or null or empty.</para>
|
||||
bool IsValid(string culture = "*");
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validates the content item's properties.
|
||||
/// </summary>
|
||||
|
||||
@@ -1458,7 +1458,6 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="resendInviteHeader">Invite user</key>
|
||||
<key alias="resendInviteSuccess">Invitation has been re-sent to %0%</key>
|
||||
<key alias="contentReqCulturePublishError">Cannot publish the document since the required '%0%' is not published</key>
|
||||
<key alias="contentCultureValidationError">Validation failed for language '%0%'</key>
|
||||
<key alias="contentCultureUnexpectedValidationError">Unexpected validation failed for language '%0%'</key>
|
||||
<key alias="documentTypeExportedSuccess">Document type was exported to file</key>
|
||||
<key alias="documentTypeExportedError">An error occurred while exporting the document type</key>
|
||||
|
||||
@@ -1,49 +1,28 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.ModelBinding.Binders;
|
||||
using System.Web.Http.Validation;
|
||||
using System.Web.ModelBinding;
|
||||
using System.Web.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Editors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Security;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using IModelBinder = System.Web.Http.ModelBinding.IModelBinder;
|
||||
using ModelBindingContext = System.Web.Http.ModelBinding.ModelBindingContext;
|
||||
using ModelMetadata = System.Web.Http.Metadata.ModelMetadata;
|
||||
using ModelMetadataProvider = System.Web.Http.Metadata.ModelMetadataProvider;
|
||||
using MutableObjectModelBinder = System.Web.Http.ModelBinding.Binders.MutableObjectModelBinder;
|
||||
using Task = System.Threading.Tasks.Task;
|
||||
|
||||
namespace Umbraco.Web.Editors.Binders
|
||||
{
|
||||
/// <inheritdoc />
|
||||
{
|
||||
/// <summary>
|
||||
/// Binds the content model to the controller action for the posted multi-part Post
|
||||
/// </summary>
|
||||
internal abstract class ContentItemBaseBinder<TPersisted, TModelSave> : IModelBinder
|
||||
where TPersisted : class, IContentBase
|
||||
//where TModelSave : ContentBaseItemSave<TPersisted>
|
||||
where TPersisted : class, IContentBase
|
||||
where TModelSave : IContentSave<TPersisted>
|
||||
{
|
||||
protected Core.Logging.ILogger Logger { get; }
|
||||
protected ServiceContext Services { get; }
|
||||
|
||||
@@ -17,6 +17,7 @@ using Umbraco.Web.WebApi.Filters;
|
||||
|
||||
namespace Umbraco.Web.Editors.Binders
|
||||
{
|
||||
/// <inheritdoc />
|
||||
internal class ContentItemBinder : ContentItemBaseBinder<IContent, ContentItemSave>
|
||||
{
|
||||
public ContentItemBinder() : this(Current.Logger, Current.Services, Current.UmbracoContextAccessor)
|
||||
|
||||
@@ -19,7 +19,6 @@ using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Models.Mapping;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Binders;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
@@ -29,9 +28,9 @@ using Umbraco.Web.Models;
|
||||
using Umbraco.Web.WebServices;
|
||||
using Umbraco.Web._Legacy.Actions;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
using ContentVariation = Umbraco.Core.Models.ContentVariation;
|
||||
using Language = Umbraco.Web.Models.ContentEditing.Language;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Editors.Binders;
|
||||
using Umbraco.Web.Editors.Filters;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
@@ -653,7 +652,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
//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 a validation message
|
||||
var forDisplay = MapToDisplay(contentItem.PersistedContent, contentItem.Culture);
|
||||
var forDisplay = MapToDisplay(contentItem.PersistedContent);
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
|
||||
|
||||
@@ -693,7 +692,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
//get the updated model
|
||||
var display = MapToDisplay(contentItem.PersistedContent, contentItem.Culture);
|
||||
var display = MapToDisplay(contentItem.PersistedContent);
|
||||
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
HandleInvalidModelState(display);
|
||||
@@ -757,6 +756,8 @@ namespace Umbraco.Web.Editors
|
||||
/// </remarks>
|
||||
private void PublishInternal(ContentItemSave contentItem, ref PublishResult publishStatus, ref bool wasCancelled)
|
||||
{
|
||||
if (publishStatus == null) throw new ArgumentNullException(nameof(publishStatus));
|
||||
|
||||
if (!contentItem.PersistedContent.ContentType.VariesByCulture())
|
||||
{
|
||||
//its invariant, proceed normally
|
||||
@@ -767,47 +768,35 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
var canPublish = true;
|
||||
|
||||
//All variants in this collection should have a culture if we get here! but we'll double check and filter here
|
||||
var cultureVariants = contentItem.Variants.Where(x => !x.Culture.IsNullOrWhiteSpace()).ToList();
|
||||
|
||||
//check if we are publishing other variants and validate them
|
||||
var allLangs = Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x, StringComparer.InvariantCultureIgnoreCase);
|
||||
var otherVariantsToValidate = contentItem.PublishVariations.Where(x => !x.Culture.InvariantEquals(contentItem.Culture)).ToList();
|
||||
|
||||
|
||||
//validate any mandatory variants that are not in the list
|
||||
var mandatoryLangs = Mapper.Map<IEnumerable<ILanguage>, IEnumerable<Language>>(allLangs.Values)
|
||||
.Where(x => otherVariantsToValidate.All(v => !v.Culture.InvariantEquals(x.IsoCode))) //don't include variants above
|
||||
.Where(x => !x.IsoCode.InvariantEquals(contentItem.Culture)) //don't include the current variant
|
||||
.Where(x => x.Mandatory);
|
||||
var mandatoryLangs = Mapper.Map<IEnumerable<ILanguage>, IEnumerable<Language>>(allLangs.Values).Where(x => x.Mandatory);
|
||||
|
||||
foreach (var lang in mandatoryLangs)
|
||||
{
|
||||
//cannot continue publishing since a required language that is not currently being published isn't published
|
||||
if (!contentItem.PersistedContent.IsCulturePublished(lang.IsoCode))
|
||||
//Check if a mandatory language is missing from being published
|
||||
//TODO: This logic is wrong, we need to also check if this language doesn't already have a published version
|
||||
if (cultureVariants.Any(x => x.Culture == lang.IsoCode && !x.Publish))
|
||||
{
|
||||
var errMsg = Services.TextService.Localize("speechBubbles/contentReqCulturePublishError", new[] { allLangs[lang.IsoCode].CultureName });
|
||||
ModelState.AddModelError("publish_variant_" + lang.IsoCode + "_", errMsg);
|
||||
canPublish = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canPublish)
|
||||
{
|
||||
//validate all other variants to be published
|
||||
foreach (var publishVariation in otherVariantsToValidate)
|
||||
{
|
||||
//validate the content item and the culture property values, we don't need to validate any invariant property values here because they will have
|
||||
//been validated in the post.
|
||||
var valid = contentItem.PersistedContent.IsValid(publishVariation.Culture);
|
||||
if (!valid)
|
||||
//cannot continue publishing since a required language that is not currently being published isn't published
|
||||
if (!contentItem.PersistedContent.IsCulturePublished(lang.IsoCode))
|
||||
{
|
||||
var errMsg = Services.TextService.Localize("speechBubbles/contentCultureValidationError", new[] { allLangs[publishVariation.Culture].CultureName });
|
||||
ModelState.AddModelError("publish_variant_" + publishVariation.Culture + "_", errMsg);
|
||||
var errMsg = Services.TextService.Localize("speechBubbles/contentReqCulturePublishError", new[] { allLangs[lang.IsoCode].CultureName });
|
||||
ModelState.AddModelError("publish_variant_" + lang.IsoCode + "_", errMsg);
|
||||
canPublish = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (canPublish)
|
||||
{
|
||||
//try to publish all the values on the model
|
||||
canPublish = PublishCulture(contentItem, otherVariantsToValidate, allLangs);
|
||||
canPublish = PublishCulture(contentItem.PersistedContent, cultureVariants, allLangs);
|
||||
}
|
||||
|
||||
if (canPublish)
|
||||
@@ -827,25 +816,22 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will call TryPublishValues on the content item for each culture that needs to be published including the invariant culture
|
||||
/// This will call PublishCulture on the content item for each culture that needs to be published including the invariant culture
|
||||
/// </summary>
|
||||
/// <param name="contentItem"></param>
|
||||
/// <param name="otherVariantsToValidate"></param>
|
||||
/// <param name="persistentContent"></param>
|
||||
/// <param name="cultureVariants"></param>
|
||||
/// <param name="allLangs"></param>
|
||||
/// <returns></returns>
|
||||
private bool PublishCulture(ContentItemSave contentItem, IEnumerable<ContentVariationPublish> otherVariantsToValidate, IDictionary<string, ILanguage> allLangs)
|
||||
private bool PublishCulture(IContent persistentContent, IEnumerable<ContentVariantSave> cultureVariants, IDictionary<string, ILanguage> allLangs)
|
||||
{
|
||||
var culturesToPublish = new List<string> { contentItem.Culture };
|
||||
culturesToPublish.AddRange(otherVariantsToValidate.Select(x => x.Culture));
|
||||
|
||||
foreach(var culture in culturesToPublish)
|
||||
foreach(var variant in cultureVariants.Where(x => x.Publish))
|
||||
{
|
||||
// publishing any culture, implies the invariant culture
|
||||
var valid = contentItem.PersistedContent.PublishCulture(culture);
|
||||
var valid = persistentContent.PublishCulture(variant.Culture);
|
||||
if (!valid)
|
||||
{
|
||||
var errMsg = Services.TextService.Localize("speechBubbles/contentCultureUnexpectedValidationError", new[] { allLangs[culture].CultureName });
|
||||
ModelState.AddModelError("publish_variant_" + culture + "_", errMsg);
|
||||
var errMsg = Services.TextService.Localize("speechBubbles/contentCultureUnexpectedValidationError", new[] { allLangs[variant.Culture].CultureName });
|
||||
ModelState.AddModelError("publish_variant_" + variant.Culture + "_", errMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1220,8 +1206,11 @@ namespace Umbraco.Web.Editors
|
||||
/// <param name="contentSave"></param>
|
||||
private void MapPropertyValues(ContentItemSave contentSave)
|
||||
{
|
||||
//set the names for the variants
|
||||
foreach(var variant in contentSave.Variants)
|
||||
//inline method to determine if a property type varies
|
||||
bool Varies(Property property) => property.PropertyType.VariesByCulture();
|
||||
|
||||
//loop through each variant, set the correct name and property values
|
||||
foreach (var variant in contentSave.Variants)
|
||||
{
|
||||
//Don't update the name if it is empty
|
||||
if (!variant.Name.IsNullOrWhiteSpace())
|
||||
@@ -1237,6 +1226,12 @@ namespace Umbraco.Web.Editors
|
||||
contentSave.PersistedContent.Name = variant.Name;
|
||||
}
|
||||
}
|
||||
|
||||
//for each variant, map the property values
|
||||
MapPropertyValues<IContent, ContentItemSave>(
|
||||
contentSave,
|
||||
(save, property) => Varies(property) ? property.GetValue(variant.Culture) : property.GetValue(), //get prop val
|
||||
(save, property, v) => { if (Varies(property)) property.SetValue(v, variant.Culture); else property.SetValue(v); }); //set prop val
|
||||
}
|
||||
|
||||
//TODO: We need to support 'send to publish'
|
||||
@@ -1262,13 +1257,6 @@ namespace Umbraco.Web.Editors
|
||||
contentSave.PersistedContent.Template = template;
|
||||
}
|
||||
}
|
||||
|
||||
bool Varies(Property property) => property.PropertyType.VariesByCulture();
|
||||
|
||||
MapPropertyValues<IContent, ContentItemSave>(
|
||||
contentSave,
|
||||
(save, property) => Varies(property) ? property.GetValue(save.Culture) : property.GetValue(), //get prop val
|
||||
(save, property, v) => { if (Varies(property)) property.SetValue(v, save.Culture); else property.SetValue(v); }); //set prop val
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -44,12 +44,12 @@ namespace Umbraco.Web.Editors
|
||||
/// <param name="contentItem"></param>
|
||||
/// <param name="getPropertyValue"></param>
|
||||
/// <param name="savePropertyValue"></param>
|
||||
protected void MapPropertyValues<TPersisted, TSaved>(
|
||||
internal void MapPropertyValues<TPersisted, TSaved>(
|
||||
TSaved contentItem,
|
||||
Func<TSaved, Property, object> getPropertyValue,
|
||||
Action<TSaved, Property, object> savePropertyValue)
|
||||
where TPersisted : IContentBase
|
||||
where TSaved : ContentBaseSave<TPersisted>
|
||||
where TSaved : IContentSave<TPersisted>
|
||||
{
|
||||
// map the property values
|
||||
foreach (var propertyDto in contentItem.ContentDto.Properties)
|
||||
@@ -69,6 +69,7 @@ namespace Umbraco.Web.Editors
|
||||
// get the property
|
||||
var property = contentItem.PersistedContent.Properties[propertyDto.Alias];
|
||||
|
||||
//TODO: Need to update this API to support variants and/or basically any sort of 'key'
|
||||
// prepare files, if any
|
||||
var files = contentItem.UploadedFiles.Where(x => x.PropertyAlias == propertyDto.Alias).ToArray();
|
||||
foreach (var file in files)
|
||||
@@ -99,9 +100,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleInvalidModelState<T, TPersisted>(ContentItemDisplayBase<T, TPersisted> display)
|
||||
where TPersisted : IContentBase
|
||||
where T : ContentPropertyBasic
|
||||
protected void HandleInvalidModelState(IErrorModel display)
|
||||
{
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
if (!ModelState.IsValid)
|
||||
|
||||
@@ -213,8 +213,6 @@
|
||||
<Compile Include="Models\ContentEditing\ContentRedirectUrl.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentVariantSave.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentVariationDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentVariation.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentVariationPublish.cs" />
|
||||
<Compile Include="Models\ContentEditing\DomainDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\DomainSave.cs" />
|
||||
<Compile Include="Models\ContentEditing\CreatedDocumentTypeCollectionResult.cs" />
|
||||
|
||||
Reference in New Issue
Block a user