WIP - Fixing up validation so that invariant properties are only validated on the default lang or if the item is not published

This commit is contained in:
Shannon
2019-03-27 12:41:02 +11:00
parent e222bf2920
commit 8042405d94
21 changed files with 240 additions and 114 deletions

View File

@@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Collections;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models
{
@@ -173,18 +169,21 @@ namespace Umbraco.Core.Models
/// <param name="culture"></param>
/// <returns>A value indicating whether it was possible to publish the names and values for the specified
/// culture(s). The method may fail if required names are not set, but it does NOT validate property data</returns>
public static bool PublishCulture(this IContent content, string culture = "*")
public static bool PublishCulture(this IContent content, CultureType culture = null)
{
culture = culture.NullOrWhiteSpaceAsNull();
culture = culture ?? CultureType.All;
// the variation should be supported by the content type properties
// if the content type is invariant, only '*' and 'null' is ok
// if the content type varies, everything is ok because some properties may be invariant
if (!content.ContentType.SupportsPropertyVariation(culture, "*", true))
if (!content.ContentType.SupportsPropertyVariation(culture.Culture, "*", true))
throw new NotSupportedException($"Culture \"{culture}\" is not supported by content type \"{content.ContentType.Alias}\" with variation \"{content.ContentType.Variations}\".");
var alsoInvariant = false;
if (culture == "*") // all cultures
switch (culture.CultureBehavior)
{
case CultureType.Behavior.All:
{
foreach (var c in content.AvailableCultures)
{
@@ -193,26 +192,32 @@ namespace Umbraco.Core.Models
return false;
content.SetPublishInfo(c, name, DateTime.Now);
}
break;
}
else if (culture == null) // invariant culture
case CultureType.Behavior.Invariant:
{
if (string.IsNullOrWhiteSpace(content.Name))
return false;
// PublishName set by repository - nothing to do here
break;
}
else // one single culture
case CultureType.Behavior.Explicit:
{
var name = content.GetCultureName(culture);
var name = content.GetCultureName(culture.Culture);
if (string.IsNullOrWhiteSpace(name))
return false;
content.SetPublishInfo(culture, name, DateTime.Now);
alsoInvariant = true; // we also want to publish invariant values
content.SetPublishInfo(culture.Culture, name, DateTime.Now);
alsoInvariant = culture.IsDefaultCulture; // we also want to publish invariant values
break;
}
default:
throw new ArgumentOutOfRangeException();
}
// property.PublishValues only publishes what is valid, variation-wise
foreach (var property in content.Properties)
{
property.PublishValues(culture);
property.PublishValues(culture.Culture);
if (alsoInvariant)
property.PublishValues(null);
}

View File

@@ -0,0 +1,46 @@
namespace Umbraco.Core.Models
{
/// <summary>
/// A <see cref="CultureType"/> represents either All cultures, a Single culture or the Invariant culture
/// </summary>
internal class CultureType
{
/// <summary>
/// Represents All cultures
/// </summary>
public static CultureType All { get; } = new CultureType("*");
/// <summary>
/// Represents the Invariant culture
/// </summary>
public static CultureType Invariant { get; } = new CultureType(null);
/// <summary>
/// Represents a Single culture
/// </summary>
/// <param name="culture"></param>
/// <param name="isDefault"></param>
/// <returns></returns>
public static CultureType Single(string culture, bool isDefault)
{
return new CultureType(culture, isDefault);
}
private CultureType(string culture, bool isDefault = false)
{
Culture = culture;
IsDefaultCulture = isDefault;
}
public string Culture { get; }
public Behavior CultureBehavior => Culture == "*" ? Behavior.All : Culture == null ? Behavior.Invariant : Behavior.Explicit;
public bool IsDefaultCulture { get; }
public enum Behavior
{
All,
Invariant,
Explicit
}
}
}

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core.Persistence.Repositories.Implement
{
internal static class LanguageRepositoryExtensions
{
public static bool IsDefault(this ILanguageRepository repo, string culture)
{
return repo.GetDefaultIsoCode().InvariantEquals(culture);
}
}
}

View File

@@ -10,6 +10,7 @@ using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.Repositories.Implement;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services.Changes;
@@ -886,12 +887,13 @@ namespace Umbraco.Core.Services.Implement
if (!culture.IsNullOrWhiteSpace() && culture != "*")
{
// publish the invariant values
// fixme: really? shouldn't we only publish invariant values if the culture is the default?
var publishInvariant = content.PublishCulture(null);
if (!publishInvariant)
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content);
//validate the property values
if (!_propertyValidationService.Value.IsPropertyDataValid(content, out invalidProperties))
if (!_propertyValidationService.Value.IsPropertyDataValid(content, out invalidProperties, CultureType.Single(culture, _languageRepository.IsDefault(culture))))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content)
{
InvalidProperties = invalidProperties
@@ -899,12 +901,12 @@ namespace Umbraco.Core.Services.Implement
}
// publish the culture(s)
var publishCulture = content.PublishCulture(culture);
var publishCulture = content.PublishCulture(CultureType.All);
if (!publishCulture)
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content);
//validate the property values
if (!_propertyValidationService.Value.IsPropertyDataValid(content, out invalidProperties))
if (!_propertyValidationService.Value.IsPropertyDataValid(content, out invalidProperties, CultureType.All))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content)
{
InvalidProperties = invalidProperties
@@ -941,14 +943,17 @@ namespace Umbraco.Core.Services.Implement
: new PublishResult(PublishResultType.FailedPublishNothingToPublish, evtMsgs, content);
}
//fixme: Shouldn't we makes ure that all string cultures here are valid? i.e. no * or null is allowed when using this method
if (cultures.Select(content.PublishCulture).Any(isValid => !isValid))
var cultureTypes = cultures.ToDictionary(x => x, x => CultureType.Single(x, _languageRepository.IsDefault(x)));
if (cultureTypes.Select(x => content.PublishCulture(x.Value)).Any(isValid => !isValid))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content);
//validate the property values on the cultures trying to be published
foreach (var culture in cultures)
foreach (var culture in cultureTypes)
{
if (!_propertyValidationService.Value.IsPropertyDataValid(content, out var invalidProperties, culture))
if (!_propertyValidationService.Value.IsPropertyDataValid(content, out var invalidProperties, culture.Value))
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, content)
{
InvalidProperties = invalidProperties
@@ -1333,7 +1338,8 @@ namespace Umbraco.Core.Services.Implement
//publish the culture values and validate the property values, if validation fails, log the invalid properties so the develeper has an idea of what has failed
Property[] invalidProperties = null;
var tryPublish = d.PublishCulture(culture) && _propertyValidationService.Value.IsPropertyDataValid(d, out invalidProperties);
var cultureType = CultureType.Single(culture, _languageRepository.IsDefault(culture));
var tryPublish = d.PublishCulture(cultureType) && _propertyValidationService.Value.IsPropertyDataValid(d, out invalidProperties, cultureType);
if (invalidProperties != null && invalidProperties.Length > 0)
Logger.Warn<ContentService>("Scheduled publishing will fail for document {DocumentId} and culture {Culture} because of invalid properties {InvalidProperties}",
d.Id, culture, string.Join(",", invalidProperties.Select(x => x.Alias)));
@@ -1429,9 +1435,16 @@ namespace Umbraco.Core.Services.Implement
// variant content type - publish specified cultures
// invariant content type - publish only the invariant culture
return content.ContentType.VariesByCulture()
? culturesToPublish.All(culture => content.PublishCulture(culture) && _propertyValidationService.Value.IsPropertyDataValid(content, out _))
: content.PublishCulture() && _propertyValidationService.Value.IsPropertyDataValid(content, out _);
if (content.ContentType.VariesByCulture())
{
return culturesToPublish.All(culture =>
{
var cultureType = CultureType.Single(culture, _languageRepository.IsDefault(culture));
return content.PublishCulture(cultureType) && _propertyValidationService.Value.IsPropertyDataValid(content, out _, cultureType);
});
}
return content.PublishCulture(CultureType.Invariant) && _propertyValidationService.Value.IsPropertyDataValid(content, out _, CultureType.Invariant);
}
// utility 'ShouldPublish' func used by SaveAndPublishBranch

View File

@@ -31,9 +31,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Validates the content item's properties pass validation rules
/// </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>
public bool IsPropertyDataValid(IContentBase content, out Property[] invalidProperties, string culture = "*")
public bool IsPropertyDataValid(IContent content, out Property[] invalidProperties, CultureType culture)
{
// select invalid properties
invalidProperties = content.Properties.Where(x =>
@@ -44,17 +42,35 @@ namespace Umbraco.Core.Services
var varies = x.PropertyType.VariesByCulture();
if (culture == null)
switch (culture.CultureBehavior)
{
case CultureType.Behavior.Invariant:
return !(varies || IsPropertyValid(x, null)); // validate invariant property, invariant culture
case CultureType.Behavior.All:
return !IsPropertyValid(x, culture.Culture); // validate property, all cultures
case CultureType.Behavior.Explicit:
if (varies)
{
return !IsPropertyValid(x, culture.Culture); // validate variant property, explicit culture
}
else
{
//We only want to validate the invariant property against an explicit culture if:
// * The culture is the default OR
// * The content item isn't published
if (culture == "*")
return !IsPropertyValid(x, culture); // validate property, all cultures
//This is because an invariant property is only edited on the default culture, but if the
//content item isn't published, we can't allow publishing of the specific non default culture
//if the invariant property data is invalid.
return varies
? !IsPropertyValid(x, culture) // validate variant property, explicit culture
: !IsPropertyValid(x, null); // validate invariant property, explicit culture
})
.ToArray();
return (culture.IsDefaultCulture || !content.Published)
&& !IsPropertyValid(x, null); // validate invariant property, explicit culture
}
default:
throw new ArgumentOutOfRangeException();
}
}).ToArray();
return invalidProperties.Length == 0;
}

View File

@@ -209,7 +209,9 @@
<Compile Include="IO\MediaPathSchemes\UniqueMediaPathScheme.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\MergeDateAndDateTimePropertyEditor.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_1\ChangeNuCacheJsonFormat.cs" />
<Compile Include="Models\CultureType.cs" />
<Compile Include="Models\PublishedContent\ILivePublishedModelFactory.cs" />
<Compile Include="Persistence\Repositories\Implement\LanguageRepositoryExtensions.cs" />
<Compile Include="PropertyEditors\DateTimeConfiguration.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\RenameLabelAndRichTextPropertyEditorAliases.cs" />
<Compile Include="PublishedModelFactoryExtensions.cs" />

View File

@@ -104,7 +104,7 @@ namespace Umbraco.Tests.Models
Thread.Sleep(500); //The "Date" wont be dirty if the test runs too fast since it will be the same date
content.SetCultureName("name-fr", langFr);
content.PublishCulture(langFr); //we've set the name, now we're publishing it
content.PublishCulture(CultureType.Single(langFr, false)); //we've set the name, now we're publishing it
Assert.IsTrue(content.IsPropertyDirty("PublishCultureInfos")); //now it will be changed since the collection has changed
var frCultureName = content.PublishCultureInfos[langFr];
Assert.IsTrue(frCultureName.IsPropertyDirty("Date"));
@@ -116,7 +116,7 @@ namespace Umbraco.Tests.Models
Thread.Sleep(500); //The "Date" wont be dirty if the test runs too fast since it will be the same date
content.SetCultureName("name-fr", langFr);
content.PublishCulture(langFr); //we've set the name, now we're publishing it
content.PublishCulture(CultureType.Single(langFr, false)); //we've set the name, now we're publishing it
Assert.IsTrue(frCultureName.IsPropertyDirty("Date"));
Assert.IsTrue(content.IsPropertyDirty("PublishCultureInfos")); //it's true now since we've updated a name
}
@@ -303,7 +303,7 @@ namespace Umbraco.Tests.Models
content.SetCultureName("Hello", "en-US");
content.SetCultureName("World", "es-ES");
content.PublishCulture("en-US");
content.PublishCulture(CultureType.All);
// should not try to clone something that's not Published or Unpublished
// (and in fact it will not work)
@@ -414,7 +414,7 @@ namespace Umbraco.Tests.Models
content.SetCultureName("Hello", "en-US");
content.SetCultureName("World", "es-ES");
content.PublishCulture("en-US");
content.PublishCulture(CultureType.All);
var i = 200;
foreach (var property in content.Properties)

View File

@@ -275,7 +275,7 @@ namespace Umbraco.Tests.Models
// can publish value
// and get edited and published values
Assert.IsTrue(content.PublishCulture());
Assert.IsTrue(content.PublishCulture(CultureType.Invariant));
Assert.AreEqual("a", content.GetValue("prop"));
Assert.AreEqual("a", content.GetValue("prop", published: true));
@@ -305,9 +305,9 @@ namespace Umbraco.Tests.Models
// can publish value
// and get edited and published values
Assert.IsFalse(content.PublishCulture(langFr)); // no name
Assert.IsFalse(content.PublishCulture(CultureType.Single(langFr, false))); // no name
content.SetCultureName("name-fr", langFr);
Assert.IsTrue(content.PublishCulture(langFr));
Assert.IsTrue(content.PublishCulture(CultureType.Single(langFr, false)));
Assert.IsNull(content.GetValue("prop"));
Assert.IsNull(content.GetValue("prop", published: true));
Assert.AreEqual("c", content.GetValue("prop", langFr));
@@ -321,7 +321,7 @@ namespace Umbraco.Tests.Models
Assert.IsNull(content.GetValue("prop", langFr, published: true));
// can publish all
Assert.IsTrue(content.PublishCulture("*"));
Assert.IsTrue(content.PublishCulture(CultureType.All));
Assert.IsNull(content.GetValue("prop"));
Assert.IsNull(content.GetValue("prop", published: true));
Assert.AreEqual("c", content.GetValue("prop", langFr));
@@ -331,14 +331,14 @@ namespace Umbraco.Tests.Models
content.UnpublishCulture(langFr);
Assert.AreEqual("c", content.GetValue("prop", langFr));
Assert.IsNull(content.GetValue("prop", langFr, published: true));
content.PublishCulture(langFr);
content.PublishCulture(CultureType.Single(langFr, false));
Assert.AreEqual("c", content.GetValue("prop", langFr));
Assert.AreEqual("c", content.GetValue("prop", langFr, published: true));
content.UnpublishCulture(); // clears invariant props if any
Assert.IsNull(content.GetValue("prop"));
Assert.IsNull(content.GetValue("prop", published: true));
content.PublishCulture(); // publishes invariant props if any
content.PublishCulture(CultureType.Invariant); // publishes invariant props if any
Assert.IsNull(content.GetValue("prop"));
Assert.IsNull(content.GetValue("prop", published: true));
@@ -384,15 +384,19 @@ namespace Umbraco.Tests.Models
content.SetCultureName("hello", langFr);
Assert.IsTrue(content.PublishCulture(langFr)); // succeeds because names are ok (not validating properties here)
Assert.IsFalse(propertyValidationService.IsPropertyDataValid(content, out _, langFr));// fails because prop1 is mandatory
var langFrCultureType = CultureType.Single(langFr, false);
Assert.IsTrue(content.PublishCulture(CultureType.Single(langFr, false))); // succeeds because names are ok (not validating properties here)
Assert.IsFalse(propertyValidationService.IsPropertyDataValid(content, out _, langFrCultureType));// fails because prop1 is mandatory
content.SetValue("prop1", "a", langFr);
Assert.IsTrue(content.PublishCulture(langFr)); // succeeds because names are ok (not validating properties here)
Assert.IsFalse(propertyValidationService.IsPropertyDataValid(content, out _, langFr));// fails because prop2 is mandatory and invariant
Assert.IsTrue(content.PublishCulture(CultureType.Single(langFr, false))); // succeeds because names are ok (not validating properties here)
// fails because prop2 is mandatory and invariant and the item isn't published.
// Invariant is validated against the default language except when there isn't a published version, in that case it's always validated.
Assert.IsFalse(propertyValidationService.IsPropertyDataValid(content, out _, langFrCultureType));
content.SetValue("prop2", "x");
Assert.IsTrue(content.PublishCulture(langFr)); // still ok...
Assert.IsTrue(propertyValidationService.IsPropertyDataValid(content, out _, langFr));// now it's ok
Assert.IsTrue(content.PublishCulture(CultureType.Single(langFr, false))); // still ok...
Assert.IsTrue(propertyValidationService.IsPropertyDataValid(content, out _, langFrCultureType));// now it's ok
Assert.AreEqual("a", content.GetValue("prop1", langFr, published: true));
Assert.AreEqual("x", content.GetValue("prop2", published: true));
@@ -423,12 +427,12 @@ namespace Umbraco.Tests.Models
content.SetValue("prop", "a-es", langEs);
// cannot publish without a name
Assert.IsFalse(content.PublishCulture(langFr));
Assert.IsFalse(content.PublishCulture(CultureType.Single(langFr, false)));
// works with a name
// and then FR is available, and published
content.SetCultureName("name-fr", langFr);
Assert.IsTrue(content.PublishCulture(langFr));
Assert.IsTrue(content.PublishCulture(CultureType.Single(langFr, false)));
// now UK is available too
content.SetCultureName("name-uk", langUk);

View File

@@ -181,13 +181,13 @@ namespace Umbraco.Tests.Persistence.NPocoTests
contentTypeService.Save(contentType);
var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1);
content1.AssignTags("tags", new[] { "hello", "world", "some", "tags" });
content1.PublishCulture();
content1.PublishCulture(CultureType.Invariant);
contentService.SaveAndPublish(content1);
id2 = content1.Id;
var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1);
content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" });
content2.PublishCulture();
content2.PublishCulture(CultureType.Invariant);
contentService.SaveAndPublish(content2);
id3 = content2.Id;

View File

@@ -141,8 +141,8 @@ namespace Umbraco.Tests.Persistence.Repositories
// publish = new edit version
content1.SetValue("title", "title");
((Content)content1).PublishCulture();
((Content)content1).PublishedState = PublishedState.Publishing;
content1.PublishCulture(CultureType.Invariant);
content1.PublishedState = PublishedState.Publishing;
repository.Save(content1);
versions.Add(content1.VersionId); // NEW VERSION
@@ -203,8 +203,8 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.AreEqual(false, scope.Database.ExecuteScalar<bool>($"SELECT published FROM {Constants.DatabaseSchema.Tables.Document} WHERE nodeId=@id", new { id = content1.Id }));
// publish = version
((Content)content1).PublishCulture();
((Content)content1).PublishedState = PublishedState.Publishing;
content1.PublishCulture(CultureType.Invariant);
content1.PublishedState = PublishedState.Publishing;
repository.Save(content1);
versions.Add(content1.VersionId); // NEW VERSION
@@ -239,8 +239,8 @@ namespace Umbraco.Tests.Persistence.Repositories
// publish = new version
content1.Name = "name-4";
content1.SetValue("title", "title-4");
((Content)content1).PublishCulture();
((Content)content1).PublishedState = PublishedState.Publishing;
content1.PublishCulture(CultureType.Invariant);
content1.PublishedState = PublishedState.Publishing;
repository.Save(content1);
versions.Add(content1.VersionId); // NEW VERSION
@@ -654,7 +654,7 @@ namespace Umbraco.Tests.Persistence.Repositories
// publish them all
foreach (var content in result)
{
content.PublishCulture();
content.PublishCulture(CultureType.Invariant);
repository.Save(content);
}

View File

@@ -517,7 +517,7 @@ namespace Umbraco.Tests.Services
allTags = tagService.GetAllContentTags();
Assert.AreEqual(0, allTags.Count());
content1.PublishCulture();
content1.PublishCulture(CultureType.Invariant);
contentService.SaveAndPublish(content1);
Assert.IsTrue(content1.Published);
@@ -601,7 +601,7 @@ namespace Umbraco.Tests.Services
var allTags = tagService.GetAllContentTags();
Assert.AreEqual(0, allTags.Count());
content1.PublishCulture();
content1.PublishCulture(CultureType.Invariant);
contentService.SaveAndPublish(content1);
tags = tagService.GetTagsForEntity(content2.Id);

View File

@@ -732,8 +732,8 @@ namespace Umbraco.Tests.Services
IContent content = new Content("content", Constants.System.Root, contentType);
content.SetCultureName("content-fr", langFr.IsoCode);
content.SetCultureName("content-en", langUk.IsoCode);
content.PublishCulture(langFr.IsoCode);
content.PublishCulture(langUk.IsoCode);
content.PublishCulture(CultureType.Single(langFr.IsoCode, langFr.IsDefault));
content.PublishCulture(CultureType.Single(langUk.IsoCode, langUk.IsDefault));
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
@@ -988,7 +988,7 @@ namespace Umbraco.Tests.Services
// content cannot publish values because they are invalid
var propertyValidationService = new PropertyValidationService(Factory.GetInstance<PropertyEditorCollection>(), ServiceContext.DataTypeService);
var isValid = propertyValidationService.IsPropertyDataValid(content, out var invalidProperties);
var isValid = propertyValidationService.IsPropertyDataValid(content, out var invalidProperties, CultureType.Invariant);
Assert.IsFalse(isValid);
Assert.IsNotEmpty(invalidProperties);
@@ -1018,7 +1018,7 @@ namespace Umbraco.Tests.Services
content.SetCultureName("name-fr", langFr.IsoCode);
content.SetCultureName("name-da", langDa.IsoCode);
content.PublishCulture(langFr.IsoCode);
content.PublishCulture(CultureType.Single(langFr.IsoCode, langFr.IsDefault));
var result = ((ContentService)ServiceContext.ContentService).CommitDocumentChanges(content);
Assert.IsTrue(result.Success);
content = ServiceContext.ContentService.GetById(content.Id);
@@ -1026,7 +1026,7 @@ namespace Umbraco.Tests.Services
Assert.IsFalse(content.IsCulturePublished(langDa.IsoCode));
content.UnpublishCulture(langFr.IsoCode);
content.PublishCulture(langDa.IsoCode);
content.PublishCulture(CultureType.Single(langDa.IsoCode, langDa.IsDefault));
result = ((ContentService)ServiceContext.ContentService).CommitDocumentChanges(content);
Assert.IsTrue(result.Success);

View File

@@ -216,7 +216,7 @@ namespace Umbraco.Tests.Services
var result = new List<IContent>();
ServiceContext.ContentTypeService.Save(contentType1);
IContent lastParent = MockedContent.CreateSimpleContent(contentType1);
lastParent.PublishCulture();
lastParent.PublishCulture(CultureType.Invariant);
ServiceContext.ContentService.SaveAndPublish(lastParent);
result.Add(lastParent);
//create 20 deep
@@ -230,7 +230,7 @@ namespace Umbraco.Tests.Services
//only publish evens
if (j % 2 == 0)
{
content.PublishCulture();
content.PublishCulture(CultureType.Invariant);
ServiceContext.ContentService.SaveAndPublish(content);
}
else

View File

@@ -0,0 +1,22 @@
(function () {
'use strict';
function umbNotificationList() {
var vm = this;
}
var umbNotificationListComponent = {
templateUrl: 'views/components/content/umb-notification-list.html',
bindings: {
notifications: "<"
},
controllerAs: 'vm',
controller: umbNotificationList
};
angular.module("umbraco.directives")
.component('umbNotificationList', umbNotificationListComponent);
})();

View File

@@ -0,0 +1,8 @@
<span class="db" ng-repeat="notification in vm.notifications">
<!-- These are server side notifications for messages, generally success/warning, validation messages handled separately -->
<!-- Success = 3, Error = 2, Warning = 4 -->
<span class="db umb-permission__description"
ng-class="{'text-success': notification.type === 3, 'text-error': notification.type === 2 || notification.type === 4}">
{{notification.message}}
</span>
</span>

View File

@@ -36,10 +36,8 @@
<span class="db umb-permission__description text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<!-- These are server side notifications for successful messages, non successful messages are returned via validation errors -->
<span class="db umb-permission__description text-success">{{notification.message}}</span>
</span>
<umb-notification-list notifications="variant.notifications" />
</label>
</div>
</div>

View File

@@ -64,10 +64,8 @@
<span class="db umb-permission__description text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<!-- These are server side notifications for successful messages, non successful messages are returned via validation errors -->
<span class="db umb-permission__description text-success">{{notification.message}}</span>
</span>
<umb-notification-list notifications="variant.notifications" />
</label>
</div>
</div>

View File

@@ -41,10 +41,8 @@
<span class="db umb-permission__description text-error" ng-message="valServerField">{{saveVariantSelectorForm.saveVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<!-- These are server side notifications for successful messages, non successful messages are returned via validation errors -->
<span class="db umb-permission__description text-success">{{notification.message}}</span>
</span>
<umb-notification-list notifications="variant.notifications" />
</label>
</div>
</div>

View File

@@ -170,10 +170,7 @@
<div class="umb-permission__description text-error" ng-message="valServerField">{{scheduleSelectorForm.saveVariantReleaseDate.errorMsg}}</div>
</div>
<div ng-repeat="notification in variant.notifications">
<!-- These are server side notifications for successful messages, non successful messages are returned via validation errors -->
<div class="umb-permission__description text-success">{{notification.message}}</div>
</div>
<umb-notification-list notifications="variant.notifications" />
</div>
</div>

View File

@@ -34,9 +34,8 @@
<span class="db umb-permission__description text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<span class="db umb-permission__description text-success">{{notification.message}}</span>
</span>
<umb-notification-list notifications="variant.notifications" />
</label>
</div>
</div>

View File

@@ -728,7 +728,7 @@ namespace Umbraco.Web.Editors
break;
}
var publishStatus = PublishBranchInternal(contentItem, false, out wasCancelled, out var successfulCultures);
var publishStatus = PublishBranchInternal(contentItem, false, out wasCancelled, out var successfulCultures).ToList();
//global notifications
AddMessageForPublishStatus(publishStatus, globalNotifications, successfulCultures);
@@ -749,7 +749,7 @@ namespace Umbraco.Web.Editors
break;
}
var publishStatus = PublishBranchInternal(contentItem, true, out wasCancelled, out var successfulCultures);
var publishStatus = PublishBranchInternal(contentItem, true, out wasCancelled, out var successfulCultures).ToList();
//global notifications
AddMessageForPublishStatus(publishStatus, globalNotifications, successfulCultures);
@@ -1341,7 +1341,7 @@ namespace Umbraco.Web.Editors
foreach (var variant in cultureVariants.Where(x => x.Publish))
{
// publishing any culture, implies the invariant culture
var valid = persistentContent.PublishCulture(variant.Culture);
var valid = persistentContent.PublishCulture(CultureType.Single(variant.Culture, IsDefaultCulture(variant.Culture)));
if (!valid)
{
AddCultureValidationError(variant.Culture, "speechBubbles/contentCultureValidationError");
@@ -1942,12 +1942,12 @@ namespace Umbraco.Web.Editors
/// <summary>
/// Adds notification messages to the outbound display model for a given published status
/// </summary>
/// <param name="status"></param>
/// <param name="statuses"></param>
/// <param name="display"></param>
/// <param name="successfulCultures">
/// This is null when dealing with invariant content, else it's the cultures that were successfully published
/// </param>
private void AddMessageForPublishStatus(IEnumerable<PublishResult> statuses, INotificationModel display, string[] successfulCultures = null)
private void AddMessageForPublishStatus(IReadOnlyCollection<PublishResult> statuses, INotificationModel display, string[] successfulCultures = null)
{
var totalStatusCount = statuses.Count();
@@ -2046,7 +2046,7 @@ namespace Umbraco.Web.Editors
break;
case PublishResultType.FailedPublishPathNotPublished:
{
var names = string.Join(", ", status.Select(x => $"{x.Content.Name} ({x.Content.Id})"));
var names = string.Join(", ", status.Select(x => x.Content.Name));
display.AddWarningNotification(
Services.TextService.Localize("publish"),
Services.TextService.Localize("publish/contentPublishedFailedByParent",
@@ -2055,13 +2055,13 @@ namespace Umbraco.Web.Editors
break;
case PublishResultType.FailedPublishCancelledByEvent:
{
var names = string.Join(", ", status.Select(x => $"{x.Content.Name} ({x.Content.Id})"));
var names = string.Join(", ", status.Select(x => x.Content.Name));
AddCancelMessage(display, message: "publish/contentPublishedFailedByEvent", messageParams: new[] { names });
}
break;
case PublishResultType.FailedPublishAwaitingRelease:
{
var names = string.Join(", ", status.Select(x => $"{x.Content.Name} ({x.Content.Id})"));
var names = string.Join(", ", status.Select(x => x.Content.Name));
display.AddWarningNotification(
Services.TextService.Localize("publish"),
Services.TextService.Localize("publish/contentPublishedFailedAwaitingRelease",
@@ -2070,7 +2070,7 @@ namespace Umbraco.Web.Editors
break;
case PublishResultType.FailedPublishHasExpired:
{
var names = string.Join(", ", status.Select(x => $"{x.Content.Name} ({x.Content.Id})"));
var names = string.Join(", ", status.Select(x => x.Content.Name));
display.AddWarningNotification(
Services.TextService.Localize("publish"),
Services.TextService.Localize("publish/contentPublishedFailedExpired",
@@ -2079,7 +2079,7 @@ namespace Umbraco.Web.Editors
break;
case PublishResultType.FailedPublishIsTrashed:
{
var names = string.Join(", ", status.Select(x => $"{x.Content.Name} ({x.Content.Id})"));
var names = string.Join(", ", status.Select(x => x.Content.Name));
display.AddWarningNotification(
Services.TextService.Localize("publish"),
Services.TextService.Localize("publish/contentPublishedFailedIsTrashed",
@@ -2088,7 +2088,7 @@ namespace Umbraco.Web.Editors
break;
case PublishResultType.FailedPublishContentInvalid:
{
var names = string.Join(", ", status.Select(x => $"{x.Content.Name} ({x.Content.Id})"));
var names = string.Join(", ", status.Select(x => x.Content.Name));
display.AddWarningNotification(
Services.TextService.Localize("publish"),
Services.TextService.Localize("publish/contentPublishedFailedInvalid",
@@ -2106,6 +2106,16 @@ namespace Umbraco.Web.Editors
}
}
/// <summary>
/// Returns true if the culture specified is the default culture
/// </summary>
/// <param name="culture"></param>
/// <returns></returns>
private bool IsDefaultCulture(string culture)
{
return _allLangs.Value.Any(x => x.Value.IsDefault && x.Key.InvariantEquals(culture));
}
/// <summary>
/// Used to map an <see cref="IContent"/> instance to a <see cref="ContentItemDisplay"/> and ensuring a language is present if required
/// </summary>