|
|
|
|
@@ -886,6 +886,8 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
{
|
|
|
|
|
scope.WriteLock(Constants.Locks.ContentTree);
|
|
|
|
|
|
|
|
|
|
var allLangs = _languageRepository.GetMany().ToList();
|
|
|
|
|
|
|
|
|
|
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
|
|
|
|
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
|
|
|
|
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
|
|
|
|
@@ -894,13 +896,13 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
// if culture is '*', then publish them all (including variants)
|
|
|
|
|
|
|
|
|
|
//this will create the correct culture impact even if culture is * or null
|
|
|
|
|
var impact = CultureImpact.Create(culture, _languageRepository.IsDefault(culture), content);
|
|
|
|
|
var impact = CultureImpact.Create(culture, IsDefaultCulture(allLangs, culture), content);
|
|
|
|
|
|
|
|
|
|
// publish the culture(s)
|
|
|
|
|
// we don't care about the response here, this response will be rechecked below but we need to set the culture info values now.
|
|
|
|
|
content.PublishCulture(impact);
|
|
|
|
|
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId, raiseEvents);
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents);
|
|
|
|
|
scope.Complete();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
@@ -921,6 +923,8 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
{
|
|
|
|
|
scope.WriteLock(Constants.Locks.ContentTree);
|
|
|
|
|
|
|
|
|
|
var allLangs = _languageRepository.GetMany().ToList();
|
|
|
|
|
|
|
|
|
|
var evtMsgs = EventMessagesFactory.Get();
|
|
|
|
|
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
|
|
|
|
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
|
|
|
|
@@ -939,14 +943,14 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
if (cultures.Any(x => x == null || x == "*"))
|
|
|
|
|
throw new InvalidOperationException("Only valid cultures are allowed to be used in this method, wildcards or nulls are not allowed");
|
|
|
|
|
|
|
|
|
|
var impacts = cultures.Select(x => CultureImpact.Explicit(x, _languageRepository.IsDefault(x)));
|
|
|
|
|
var impacts = cultures.Select(x => CultureImpact.Explicit(x, IsDefaultCulture(allLangs, x)));
|
|
|
|
|
|
|
|
|
|
// publish the culture(s)
|
|
|
|
|
// we don't care about the response here, this response will be rechecked below but we need to set the culture info values now.
|
|
|
|
|
foreach (var impact in impacts)
|
|
|
|
|
content.PublishCulture(impact);
|
|
|
|
|
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId, raiseEvents);
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents);
|
|
|
|
|
scope.Complete();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
@@ -986,6 +990,8 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
{
|
|
|
|
|
scope.WriteLock(Constants.Locks.ContentTree);
|
|
|
|
|
|
|
|
|
|
var allLangs = _languageRepository.GetMany().ToList();
|
|
|
|
|
|
|
|
|
|
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
|
|
|
|
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
|
|
|
|
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
|
|
|
|
@@ -993,22 +999,39 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
// all cultures = unpublish whole
|
|
|
|
|
if (culture == "*" || (!content.ContentType.VariesByCulture() && culture == null))
|
|
|
|
|
{
|
|
|
|
|
// It's important to understand that when the document varies by culture but the "*" is used,
|
|
|
|
|
// we are just unpublishing the whole document but leaving all of the culture's as-is. This is expected
|
|
|
|
|
// because we don't want to actually unpublish every culture and then the document, we just want everything
|
|
|
|
|
// to be non-routable so that when it's re-published all variants were as they were.
|
|
|
|
|
|
|
|
|
|
content.PublishedState = PublishedState.Unpublishing;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// If the culture we want to unpublish was already unpublished, nothing to do.
|
|
|
|
|
// To check for that we need to lookup the persisted content item
|
|
|
|
|
var persisted = content.HasIdentity ? GetById(content.Id) : null;
|
|
|
|
|
|
|
|
|
|
if (persisted != null && !persisted.IsCulturePublished(culture))
|
|
|
|
|
return new PublishResult(PublishResultType.SuccessUnpublishAlready, evtMsgs, content);
|
|
|
|
|
|
|
|
|
|
// unpublish the culture
|
|
|
|
|
// unpublish the culture, this will change the document state to Publishing! ... which is expected because this will
|
|
|
|
|
// essentially be re-publishing the document with the requested culture removed.
|
|
|
|
|
// The call to CommitDocumentChangesInternal will perform all the checks like if this is a mandatory culture or the last culture being unpublished
|
|
|
|
|
// and will then unpublish the document accordingly.
|
|
|
|
|
content.UnpublishCulture(culture);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//TODO: Move this logic into CommitDocumentChangesInternal, we are already looking up the item there
|
|
|
|
|
|
|
|
|
|
//// We are unpublishing a culture but there's a chance that the user might be trying
|
|
|
|
|
//// to unpublish a culture that is already unpublished so we need to lookup the current
|
|
|
|
|
//// persisted item and check since it would be quicker to do that than continue and re-publish everything.
|
|
|
|
|
//var persisted = content.HasIdentity ? GetById(content.Id) : null;
|
|
|
|
|
|
|
|
|
|
//if (persisted != null && !persisted.IsCulturePublished(culture))
|
|
|
|
|
//{
|
|
|
|
|
// //it was already unpublished
|
|
|
|
|
// //TODO: We then need to check if all cultures are unpublished
|
|
|
|
|
// return new PublishResult(PublishResultType.SuccessUnpublishAlready, evtMsgs, content);
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId);
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId);
|
|
|
|
|
scope.Complete();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
@@ -1047,15 +1070,35 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
|
|
|
|
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
|
|
|
|
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId, raiseEvents);
|
|
|
|
|
var allLangs = _languageRepository.GetMany().ToList();
|
|
|
|
|
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents);
|
|
|
|
|
scope.Complete();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Handles a lot of business logic cases for how the document should be persisted
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="scope"></param>
|
|
|
|
|
/// <param name="content"></param>
|
|
|
|
|
/// <param name="saveEventArgs"></param>
|
|
|
|
|
/// <param name="userId"></param>
|
|
|
|
|
/// <param name="raiseEvents"></param>
|
|
|
|
|
/// <param name="branchOne"></param>
|
|
|
|
|
/// <param name="branchRoot"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// Business logic cases such: as unpublishing a mandatory culture, or unpublishing the last culture, checking for pending scheduled publishing, etc... is dealt with in this method.
|
|
|
|
|
/// There is quite a lot of cases to take into account along with logic that needs to deal with scheduled saving/publishing, branch saving/publishing, etc...
|
|
|
|
|
/// </para>
|
|
|
|
|
/// </remarks>
|
|
|
|
|
private PublishResult CommitDocumentChangesInternal(IScope scope, IContent content,
|
|
|
|
|
ContentSavingEventArgs saveEventArgs,
|
|
|
|
|
int userId = Constants.Security.SuperUserId, bool raiseEvents = true, bool branchOne = false, bool branchRoot = false)
|
|
|
|
|
ContentSavingEventArgs saveEventArgs, IReadOnlyCollection<ILanguage> allLangs,
|
|
|
|
|
int userId = Constants.Security.SuperUserId,
|
|
|
|
|
bool raiseEvents = true, bool branchOne = false, bool branchRoot = false)
|
|
|
|
|
{
|
|
|
|
|
if (scope == null) throw new ArgumentNullException(nameof(scope));
|
|
|
|
|
if (content == null) throw new ArgumentNullException(nameof(content));
|
|
|
|
|
@@ -1070,8 +1113,8 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
if (content.PublishedState != PublishedState.Publishing && content.PublishedState != PublishedState.Unpublishing)
|
|
|
|
|
content.PublishedState = PublishedState.Publishing;
|
|
|
|
|
|
|
|
|
|
// state here is either Publishing or Unpublishing
|
|
|
|
|
// (even though, Publishing to unpublish a culture may end up unpublishing everything)
|
|
|
|
|
// State here is either Publishing or Unpublishing
|
|
|
|
|
// Publishing to unpublish a culture may end up unpublishing everything so these flags can be flipped later
|
|
|
|
|
var publishing = content.PublishedState == PublishedState.Publishing;
|
|
|
|
|
var unpublishing = content.PublishedState == PublishedState.Unpublishing;
|
|
|
|
|
|
|
|
|
|
@@ -1090,6 +1133,9 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
|
|
|
|
|
if (publishing)
|
|
|
|
|
{
|
|
|
|
|
//TODO: Check if it's a culture being unpublished and that's why we are publishing the document. In that case
|
|
|
|
|
// we should check if the culture is already unpublished since it might be the user is trying to unpublish an already unpublished culture?
|
|
|
|
|
|
|
|
|
|
//determine cultures publishing/unpublishing which will be based on previous calls to content.PublishCulture and ClearPublishInfo
|
|
|
|
|
culturesUnpublishing = content.GetCulturesUnpublishing();
|
|
|
|
|
culturesPublishing = variesByCulture
|
|
|
|
|
@@ -1097,11 +1143,21 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
// ensure that the document can be published, and publish handling events, business rules, etc
|
|
|
|
|
publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, saveEventArgs);
|
|
|
|
|
publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, saveEventArgs, allLangs);
|
|
|
|
|
if (publishResult.Success)
|
|
|
|
|
{
|
|
|
|
|
// note: StrategyPublish flips the PublishedState to Publishing!
|
|
|
|
|
publishResult = StrategyPublish(content, culturesPublishing, culturesUnpublishing, evtMsgs);
|
|
|
|
|
|
|
|
|
|
//check if a culture has been unpublished and if there are no cultures left, and then unpublish document as a whole
|
|
|
|
|
if (publishResult.Result == PublishResultType.SuccessUnpublishCulture && content.PublishCultureInfos.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
publishing = false;
|
|
|
|
|
unpublishing = content.Published; // if not published yet, nothing to do
|
|
|
|
|
|
|
|
|
|
// we may end up in a state where we won't publish nor unpublish
|
|
|
|
|
// keep going, though, as we want to save anyways
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
@@ -1186,17 +1242,34 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
|
|
|
|
|
if (culturesUnpublishing != null)
|
|
|
|
|
{
|
|
|
|
|
//If we are here, it means we tried unpublishing a culture but it was mandatory so now everything is unpublished
|
|
|
|
|
var langs = string.Join(", ", _languageRepository.GetMany()
|
|
|
|
|
// This will mean that that we unpublished a mandatory culture or we unpublished the last culture.
|
|
|
|
|
|
|
|
|
|
var langs = string.Join(", ", allLangs
|
|
|
|
|
.Where(x => culturesUnpublishing.InvariantContains(x.IsoCode))
|
|
|
|
|
.Select(x => x.CultureName));
|
|
|
|
|
Audit(AuditType.UnpublishVariant, userId, content.Id, $"Unpublished languages: {langs}", langs);
|
|
|
|
|
//log that the whole content item has been unpublished due to mandatory culture unpublished
|
|
|
|
|
Audit(AuditType.Unpublish, userId, content.Id, "Unpublished (mandatory language unpublished)");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
Audit(AuditType.Unpublish, userId, content.Id);
|
|
|
|
|
|
|
|
|
|
if (publishResult == null)
|
|
|
|
|
throw new PanicException("publishResult == null - should not happen");
|
|
|
|
|
|
|
|
|
|
switch(publishResult.Result)
|
|
|
|
|
{
|
|
|
|
|
case PublishResultType.FailedPublishMandatoryCultureMissing:
|
|
|
|
|
//occurs when a mandatory culture was unpublished (which means we tried publishing the document without a mandatory culture)
|
|
|
|
|
|
|
|
|
|
//log that the whole content item has been unpublished due to mandatory culture unpublished
|
|
|
|
|
Audit(AuditType.Unpublish, userId, content.Id, "Unpublished (mandatory language unpublished)");
|
|
|
|
|
return new PublishResult(PublishResultType.SuccessUnpublishMandatoryCulture, evtMsgs, content);
|
|
|
|
|
case PublishResultType.SuccessUnpublishCulture:
|
|
|
|
|
//occurs when the last culture is unpublished
|
|
|
|
|
|
|
|
|
|
Audit(AuditType.Unpublish, userId, content.Id, "Unpublished (last language unpublished)");
|
|
|
|
|
return new PublishResult(PublishResultType.SuccessUnpublishLastCulture, evtMsgs, content);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Audit(AuditType.Unpublish, userId, content.Id);
|
|
|
|
|
return new PublishResult(PublishResultType.SuccessUnpublish, evtMsgs, content);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1236,7 +1309,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
case PublishResultType.SuccessPublishCulture:
|
|
|
|
|
if (culturesPublishing != null)
|
|
|
|
|
{
|
|
|
|
|
var langs = string.Join(", ", _languageRepository.GetMany()
|
|
|
|
|
var langs = string.Join(", ", allLangs
|
|
|
|
|
.Where(x => culturesPublishing.InvariantContains(x.IsoCode))
|
|
|
|
|
.Select(x => x.CultureName));
|
|
|
|
|
Audit(AuditType.PublishVariant, userId, content.Id, $"Published languages: {langs}", langs);
|
|
|
|
|
@@ -1245,7 +1318,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
case PublishResultType.SuccessUnpublishCulture:
|
|
|
|
|
if (culturesUnpublishing != null)
|
|
|
|
|
{
|
|
|
|
|
var langs = string.Join(", ", _languageRepository.GetMany()
|
|
|
|
|
var langs = string.Join(", ", allLangs
|
|
|
|
|
.Where(x => culturesUnpublishing.InvariantContains(x.IsoCode))
|
|
|
|
|
.Select(x => x.CultureName));
|
|
|
|
|
Audit(AuditType.UnpublishVariant, userId, content.Id, $"Unpublished languages: {langs}", langs);
|
|
|
|
|
@@ -1259,14 +1332,14 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
|
|
|
|
|
// should not happen
|
|
|
|
|
if (branchOne && !branchRoot)
|
|
|
|
|
throw new Exception("panic");
|
|
|
|
|
throw new PanicException("branchOne && !branchRoot - should not happen");
|
|
|
|
|
|
|
|
|
|
//if publishing didn't happen or if it has failed, we still need to log which cultures were saved
|
|
|
|
|
if (!branchOne && (publishResult == null || !publishResult.Success))
|
|
|
|
|
{
|
|
|
|
|
if (culturesChanging != null)
|
|
|
|
|
{
|
|
|
|
|
var langs = string.Join(", ", _languageRepository.GetMany()
|
|
|
|
|
var langs = string.Join(", ", allLangs
|
|
|
|
|
.Where(x => culturesChanging.InvariantContains(x.IsoCode))
|
|
|
|
|
.Select(x => x.CultureName));
|
|
|
|
|
Audit(AuditType.SaveVariant, userId, content.Id, $"Saved languages: {langs}", langs);
|
|
|
|
|
@@ -1297,6 +1370,8 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
{
|
|
|
|
|
scope.WriteLock(Constants.Locks.ContentTree);
|
|
|
|
|
|
|
|
|
|
var allLangs = _languageRepository.GetMany().ToList();
|
|
|
|
|
|
|
|
|
|
foreach (var d in _documentRepository.GetContentForRelease(date))
|
|
|
|
|
{
|
|
|
|
|
PublishResult result;
|
|
|
|
|
@@ -1325,7 +1400,7 @@ 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 impact = CultureImpact.Explicit(culture, _languageRepository.IsDefault(culture));
|
|
|
|
|
var impact = CultureImpact.Explicit(culture, IsDefaultCulture(allLangs, culture));
|
|
|
|
|
var tryPublish = d.PublishCulture(impact) && _propertyValidationService.Value.IsPropertyDataValid(d, out invalidProperties, impact);
|
|
|
|
|
if (invalidProperties != null && invalidProperties.Length > 0)
|
|
|
|
|
Logger.Warn<ContentService>("Scheduled publishing will fail for document {DocumentId} and culture {Culture} because of invalid properties {InvalidProperties}",
|
|
|
|
|
@@ -1340,7 +1415,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
else if (!publishing)
|
|
|
|
|
result = new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, d);
|
|
|
|
|
else
|
|
|
|
|
result = CommitDocumentChangesInternal(scope, d, saveEventArgs, d.WriterId);
|
|
|
|
|
result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs, d.WriterId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (result.Success == false)
|
|
|
|
|
@@ -1390,7 +1465,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
d.UnpublishCulture(c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = CommitDocumentChangesInternal(scope, d, saveEventArgs, d.WriterId);
|
|
|
|
|
result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs, d.WriterId);
|
|
|
|
|
if (result.Success == false)
|
|
|
|
|
Logger.Error<ContentService>(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result);
|
|
|
|
|
yield return result;
|
|
|
|
|
@@ -1416,7 +1491,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// utility 'PublishCultures' func used by SaveAndPublishBranch
|
|
|
|
|
private bool SaveAndPublishBranch_PublishCultures(IContent content, HashSet<string> culturesToPublish)
|
|
|
|
|
private bool SaveAndPublishBranch_PublishCultures(IContent content, HashSet<string> culturesToPublish, IReadOnlyCollection<ILanguage> allLangs)
|
|
|
|
|
{
|
|
|
|
|
//TODO: This does not support being able to return invalid property details to bubble up to the UI
|
|
|
|
|
|
|
|
|
|
@@ -1426,7 +1501,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
{
|
|
|
|
|
return culturesToPublish.All(culture =>
|
|
|
|
|
{
|
|
|
|
|
var impact = CultureImpact.Create(culture, _languageRepository.IsDefault(culture), content);
|
|
|
|
|
var impact = CultureImpact.Create(culture, IsDefaultCulture(allLangs, culture), content);
|
|
|
|
|
return content.PublishCulture(impact) && _propertyValidationService.Value.IsPropertyDataValid(content, out _, impact);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@@ -1535,7 +1610,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
|
|
|
|
|
internal IEnumerable<PublishResult> SaveAndPublishBranch(IContent document, bool force,
|
|
|
|
|
Func<IContent, HashSet<string>> shouldPublish,
|
|
|
|
|
Func<IContent, HashSet<string>, bool> publishCultures,
|
|
|
|
|
Func<IContent, HashSet<string>, IReadOnlyCollection<ILanguage>, bool> publishCultures,
|
|
|
|
|
int userId = Constants.Security.SuperUserId)
|
|
|
|
|
{
|
|
|
|
|
if (shouldPublish == null) throw new ArgumentNullException(nameof(shouldPublish));
|
|
|
|
|
@@ -1549,6 +1624,8 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
{
|
|
|
|
|
scope.WriteLock(Constants.Locks.ContentTree);
|
|
|
|
|
|
|
|
|
|
var allLangs = _languageRepository.GetMany().ToList();
|
|
|
|
|
|
|
|
|
|
if (!document.HasIdentity)
|
|
|
|
|
throw new InvalidOperationException("Cannot not branch-publish a new document.");
|
|
|
|
|
|
|
|
|
|
@@ -1557,7 +1634,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
throw new InvalidOperationException("Cannot mix PublishCulture and SaveAndPublishBranch.");
|
|
|
|
|
|
|
|
|
|
// deal with the branch root - if it fails, abort
|
|
|
|
|
var result = SaveAndPublishBranchItem(scope, document, shouldPublish, publishCultures, true, publishedDocuments, evtMsgs, userId);
|
|
|
|
|
var result = SaveAndPublishBranchItem(scope, document, shouldPublish, publishCultures, true, publishedDocuments, evtMsgs, userId, allLangs);
|
|
|
|
|
if (result != null)
|
|
|
|
|
{
|
|
|
|
|
results.Add(result);
|
|
|
|
|
@@ -1588,7 +1665,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no need to check path here, parent has to be published here
|
|
|
|
|
result = SaveAndPublishBranchItem(scope, d, shouldPublish, publishCultures, false, publishedDocuments, evtMsgs, userId);
|
|
|
|
|
result = SaveAndPublishBranchItem(scope, d, shouldPublish, publishCultures, false, publishedDocuments, evtMsgs, userId, allLangs);
|
|
|
|
|
if (result != null)
|
|
|
|
|
{
|
|
|
|
|
results.Add(result);
|
|
|
|
|
@@ -1620,10 +1697,10 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
// publishValues: a function publishing values (using the appropriate PublishCulture calls)
|
|
|
|
|
private PublishResult SaveAndPublishBranchItem(IScope scope, IContent document,
|
|
|
|
|
Func<IContent, HashSet<string>> shouldPublish,
|
|
|
|
|
Func<IContent, HashSet<string>, bool> publishCultures,
|
|
|
|
|
Func<IContent, HashSet<string>, IReadOnlyCollection<ILanguage>, bool> publishCultures,
|
|
|
|
|
bool isRoot,
|
|
|
|
|
ICollection<IContent> publishedDocuments,
|
|
|
|
|
EventMessages evtMsgs, int userId)
|
|
|
|
|
EventMessages evtMsgs, int userId, IReadOnlyCollection<ILanguage> allLangs)
|
|
|
|
|
{
|
|
|
|
|
var culturesToPublish = shouldPublish(document);
|
|
|
|
|
if (culturesToPublish == null) // null = do not include
|
|
|
|
|
@@ -1636,13 +1713,13 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, document);
|
|
|
|
|
|
|
|
|
|
// publish & check if values are valid
|
|
|
|
|
if (!publishCultures(document, culturesToPublish))
|
|
|
|
|
if (!publishCultures(document, culturesToPublish, allLangs))
|
|
|
|
|
{
|
|
|
|
|
//TODO: Based on this callback behavior there is no way to know which properties may have been invalid if this failed, see other results of FailedPublishContentInvalid
|
|
|
|
|
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, document);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, document, saveEventArgs, userId, branchOne: true, branchRoot: isRoot);
|
|
|
|
|
var result = CommitDocumentChangesInternal(scope, document, saveEventArgs, allLangs, userId, branchOne: true, branchRoot: isRoot);
|
|
|
|
|
if (result.Success)
|
|
|
|
|
publishedDocuments.Add(document);
|
|
|
|
|
return result;
|
|
|
|
|
@@ -2343,6 +2420,9 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.Document), message, parameters));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool IsDefaultCulture(IReadOnlyCollection<ILanguage> langs, string culture) => langs.Any(x => x.IsDefault && x.IsoCode.InvariantEquals(culture));
|
|
|
|
|
private bool IsMandatoryCulture(IReadOnlyCollection<ILanguage> langs, string culture) => langs.Any(x => x.IsMandatory && x.IsoCode.InvariantEquals(culture));
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Event Handlers
|
|
|
|
|
@@ -2497,7 +2577,9 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
/// <param name="culturesPublishing"></param>
|
|
|
|
|
/// <param name="savingEventArgs"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private PublishResult StrategyCanPublish(IScope scope, IContent content, bool checkPath, IReadOnlyList<string> culturesPublishing, IReadOnlyCollection<string> culturesUnpublishing, EventMessages evtMsgs, ContentSavingEventArgs savingEventArgs)
|
|
|
|
|
private PublishResult StrategyCanPublish(IScope scope, IContent content, bool checkPath, IReadOnlyList<string> culturesPublishing,
|
|
|
|
|
IReadOnlyCollection<string> culturesUnpublishing, EventMessages evtMsgs, ContentSavingEventArgs savingEventArgs,
|
|
|
|
|
IReadOnlyCollection<ILanguage> allLangs)
|
|
|
|
|
{
|
|
|
|
|
// raise Publishing event
|
|
|
|
|
if (scope.Events.DispatchCancelable(Publishing, this, savingEventArgs.ToContentPublishingEventArgs()))
|
|
|
|
|
@@ -2510,7 +2592,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
|
|
|
|
|
var impactsToPublish = culturesPublishing == null
|
|
|
|
|
? new[] {CultureImpact.Invariant} //if it's null it's invariant
|
|
|
|
|
: culturesPublishing.Select(x => CultureImpact.Explicit(x, _languageRepository.IsDefault(x))).ToArray();
|
|
|
|
|
: culturesPublishing.Select(x => CultureImpact.Explicit(x, allLangs.Any(lang => lang.IsoCode.InvariantEquals(x) && lang.IsMandatory))).ToArray();
|
|
|
|
|
|
|
|
|
|
// publish the culture(s)
|
|
|
|
|
if (!impactsToPublish.All(content.PublishCulture))
|
|
|
|
|
@@ -2535,7 +2617,7 @@ namespace Umbraco.Core.Services.Implement
|
|
|
|
|
return new PublishResult(PublishResultType.FailedPublishNothingToPublish, evtMsgs, content);
|
|
|
|
|
|
|
|
|
|
// missing mandatory culture = cannot be published
|
|
|
|
|
var mandatoryCultures = _languageRepository.GetMany().Where(x => x.IsMandatory).Select(x => x.IsoCode);
|
|
|
|
|
var mandatoryCultures = allLangs.Where(x => x.IsMandatory).Select(x => x.IsoCode);
|
|
|
|
|
var mandatoryMissing = mandatoryCultures.Any(x => !content.PublishedCultures.Contains(x, StringComparer.OrdinalIgnoreCase));
|
|
|
|
|
if (mandatoryMissing)
|
|
|
|
|
return new PublishResult(PublishResultType.FailedPublishMandatoryCultureMissing, evtMsgs, content);
|
|
|
|
|
|