Ensures the whole doc gets unpublished if a mandatory culture is unpublished, updates content service Unpublish to have it's own status result enum since that is needed.
This commit is contained in:
@@ -364,7 +364,7 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Unpublishes a document or optionally unpublishes a culture and/or segment for the document.
|
||||
/// </summary>
|
||||
PublishResult Unpublish(IContent content, string culture = null, string segment = null, int userId = 0);
|
||||
UnpublishResult Unpublish(IContent content, string culture = null, string segment = null, int userId = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a document is path-publishable.
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
private readonly IAuditRepository _auditRepository;
|
||||
private readonly IContentTypeRepository _contentTypeRepository;
|
||||
private readonly IDocumentBlueprintRepository _documentBlueprintRepository;
|
||||
|
||||
private readonly ILanguageRepository _languageRepository;
|
||||
private readonly MediaFileSystem _mediaFileSystem;
|
||||
private IQuery<IContent> _queryNotTrashed;
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
public ContentService(IScopeProvider provider, ILogger logger,
|
||||
IEventMessagesFactory eventMessagesFactory, MediaFileSystem mediaFileSystem,
|
||||
IDocumentRepository documentRepository, IEntityRepository entityRepository, IAuditRepository auditRepository,
|
||||
IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository)
|
||||
IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository)
|
||||
: base(provider, logger, eventMessagesFactory)
|
||||
{
|
||||
_mediaFileSystem = mediaFileSystem;
|
||||
@@ -44,6 +44,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
_auditRepository = auditRepository;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_documentBlueprintRepository = documentBlueprintRepository;
|
||||
_languageRepository = languageRepository;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1025,7 +1026,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public PublishResult Unpublish(IContent content, string culture = null, string segment = null, int userId = 0)
|
||||
public UnpublishResult Unpublish(IContent content, string culture = null, string segment = null, int userId = 0)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
@@ -1038,7 +1039,13 @@ namespace Umbraco.Core.Services.Implement
|
||||
if (tryUnpublishVariation) return tryUnpublishVariation.Result;
|
||||
|
||||
//continue the normal Unpublish operation to unpublish the entire content item
|
||||
return UnpublishInternal(scope, content, evtMsgs, userId);
|
||||
var result = UnpublishInternal(scope, content, evtMsgs, userId);
|
||||
|
||||
//not succesful, so return it
|
||||
if (!result.Success) return result;
|
||||
|
||||
//else check if there was a status returned from TryUnpublishVariationInternal and if so use that
|
||||
return tryUnpublishVariation.Result ?? result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1055,7 +1062,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// A successful attempt if a variant was unpublished and it wasn't the last, else a failed result if it's an invariant document or if it's the last.
|
||||
/// A failed result indicates that a normal unpublish operation will need to be performed.
|
||||
/// </returns>
|
||||
private Attempt<PublishResult> TryUnpublishVariationInternal(IScope scope, IContent content, EventMessages evtMsgs, string culture, string segment, int userId)
|
||||
private Attempt<UnpublishResult> TryUnpublishVariationInternal(IScope scope, IContent content, EventMessages evtMsgs, string culture, string segment, int userId)
|
||||
{
|
||||
if (!culture.IsNullOrWhiteSpace() || !segment.IsNullOrWhiteSpace())
|
||||
{
|
||||
@@ -1064,16 +1071,27 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
//capture before we clear the values
|
||||
var publishedCultureCount = content.PublishedCultures.Count();
|
||||
|
||||
//we need to unpublish everything if this is a mandatory language
|
||||
var unpublishAll = _languageRepository.GetMany().Where(x => x.Mandatory).Select(x => x.IsoCode).Contains(culture, StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
//fixme - this needs to be changed when 11227 is merged!
|
||||
content.ClearPublishedValues(culture, segment);
|
||||
//now we just publish with the name cleared
|
||||
SaveAndPublish(content, userId);
|
||||
Audit(AuditType.UnPublish, $"UnPublish variation culture: {culture ?? string.Empty}, segment: {segment ?? string.Empty} performed by user", userId, content.Id);
|
||||
//This is not the last one, so complete the scope and return
|
||||
if (publishedCultureCount > 1)
|
||||
|
||||
//We don't need to unpublish all and this is not the last one, so complete the scope and return
|
||||
if (!unpublishAll && publishedCultureCount > 1)
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.Succeed(new PublishResult(PublishResultType.SuccessVariant, evtMsgs, content));
|
||||
return Attempt.Succeed(new UnpublishResult(UnpublishResultType.SuccessVariant, evtMsgs, content));
|
||||
}
|
||||
|
||||
if (unpublishAll)
|
||||
{
|
||||
//return a fail with a custom status here so the normal unpublish operation takes place but the result will be this flag
|
||||
return Attempt.Fail(new UnpublishResult(UnpublishResultType.SuccessMandatoryCulture, evtMsgs, content));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1085,8 +1103,8 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
//This is either a non variant document or this is the last one, so return a failed result which indicates that a normal unpublish operation needs to also take place
|
||||
return Attempt.Fail<PublishResult>();
|
||||
//This is either a non variant document or this is the last one or this was a mandatory variant, so return a failed result which indicates that a normal unpublish operation needs to also take place
|
||||
return Attempt.Fail<UnpublishResult>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1097,7 +1115,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// <param name="evtMsgs"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
private PublishResult UnpublishInternal(IScope scope, IContent content, EventMessages evtMsgs, int userId)
|
||||
private UnpublishResult UnpublishInternal(IScope scope, IContent content, EventMessages evtMsgs, int userId)
|
||||
{
|
||||
var newest = GetById(content.Id); // ensure we have the newest version
|
||||
if (content.VersionId != newest.VersionId) // but use the original object if it's already the newest version
|
||||
@@ -1105,7 +1123,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
if (content.Published == false)
|
||||
{
|
||||
scope.Complete();
|
||||
return new PublishResult(PublishResultType.SuccessAlready, evtMsgs, content); // already unpublished
|
||||
return new UnpublishResult(UnpublishResultType.SuccessAlready, evtMsgs, content); // already unpublished
|
||||
}
|
||||
|
||||
// strategy
|
||||
@@ -1123,7 +1141,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
Audit(AuditType.UnPublish, "UnPublish performed by user", userId, content.Id);
|
||||
|
||||
scope.Complete();
|
||||
return new PublishResult(PublishResultType.Success, evtMsgs, content);
|
||||
return new UnpublishResult(evtMsgs, content);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -2142,23 +2160,23 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
|
||||
// ensures that a document can be unpublished
|
||||
internal PublishResult StrategyCanUnpublish(IScope scope, IContent content, int userId, EventMessages evtMsgs)
|
||||
internal UnpublishResult StrategyCanUnpublish(IScope scope, IContent content, int userId, EventMessages evtMsgs)
|
||||
{
|
||||
// raise UnPublishing event
|
||||
if (scope.Events.DispatchCancelable(UnPublishing, this, new PublishEventArgs<IContent>(content, evtMsgs)))
|
||||
{
|
||||
Logger.Info<ContentService>($"Document \"{content.Name}\" (id={content.Id}) cannot be unpublished: unpublishing was cancelled.");
|
||||
return new PublishResult(PublishResultType.FailedCancelledByEvent, evtMsgs, content);
|
||||
return new UnpublishResult(UnpublishResultType.FailedCancelledByEvent, evtMsgs, content);
|
||||
}
|
||||
|
||||
return new PublishResult(evtMsgs, content);
|
||||
return new UnpublishResult(evtMsgs, content);
|
||||
}
|
||||
|
||||
// unpublishes a document
|
||||
internal PublishResult StrategyUnpublish(IScope scope, IContent content, bool canUnpublish, int userId, EventMessages evtMsgs)
|
||||
internal UnpublishResult StrategyUnpublish(IScope scope, IContent content, bool canUnpublish, int userId, EventMessages evtMsgs)
|
||||
{
|
||||
var attempt = canUnpublish
|
||||
? new PublishResult(evtMsgs, content) // already know we can
|
||||
? new UnpublishResult(evtMsgs, content) // already know we can
|
||||
: StrategyCanUnpublish(scope, content, userId, evtMsgs); // else check
|
||||
|
||||
if (attempt.Success == false)
|
||||
|
||||
@@ -4,6 +4,7 @@ using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents the result of publishing a document.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating the result of (un)publishing a content item.
|
||||
/// A value indicating the result of publishing a content item.
|
||||
/// </summary>
|
||||
public enum PublishResultType : byte
|
||||
{
|
||||
@@ -9,20 +10,15 @@
|
||||
// every failure codes as >128 - see OperationResult and OperationResultType for details.
|
||||
|
||||
/// <summary>
|
||||
/// The (un)publishing was successful.
|
||||
/// The publishing was successful.
|
||||
/// </summary>
|
||||
Success = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The item was already (un)published.
|
||||
/// The item was already published.
|
||||
/// </summary>
|
||||
SuccessAlready = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The specified variant was unpublished, the content item itself remains published.
|
||||
/// </summary>
|
||||
SuccessVariant = 2,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The operation failed.
|
||||
/// </summary>
|
||||
|
||||
29
src/Umbraco.Core/Services/UnpublishResult.cs
Normal file
29
src/Umbraco.Core/Services/UnpublishResult.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the result of unpublishing a document.
|
||||
/// </summary>
|
||||
public class UnpublishResult : OperationResult<UnpublishResultType, IContent>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a successful result
|
||||
/// </summary>
|
||||
/// <param name="eventMessages"></param>
|
||||
/// <param name="entity"></param>
|
||||
public UnpublishResult(EventMessages eventMessages, IContent entity) : base(UnpublishResultType.Success, eventMessages, entity)
|
||||
{
|
||||
}
|
||||
|
||||
public UnpublishResult(UnpublishResultType result, EventMessages eventMessages, IContent entity) : base(result, eventMessages, entity)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the document.
|
||||
/// </summary>
|
||||
public IContent Content => Entity;
|
||||
}
|
||||
}
|
||||
39
src/Umbraco.Core/Services/UnpublishResultType.cs
Normal file
39
src/Umbraco.Core/Services/UnpublishResultType.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// A value indicating the result of unpublishing a content item.
|
||||
/// </summary>
|
||||
public enum UnpublishResultType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The unpublishing was successful.
|
||||
/// </summary>
|
||||
Success = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The item was already unpublished.
|
||||
/// </summary>
|
||||
SuccessAlready = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The specified variant was unpublished, the content item itself remains published.
|
||||
/// </summary>
|
||||
SuccessVariant = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The specified variant was a mandatory culture therefore it was unpublished and the content item itself is unpublished
|
||||
/// </summary>
|
||||
SuccessMandatoryCulture = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The operation failed.
|
||||
/// </summary>
|
||||
/// <remarks>All values above this value indicate a failure.</remarks>
|
||||
Failed = 128,
|
||||
|
||||
/// <summary>
|
||||
/// The publish action has been cancelled by an event handler.
|
||||
/// </summary>
|
||||
FailedCancelledByEvent = Failed | 5,
|
||||
}
|
||||
}
|
||||
@@ -1420,6 +1420,8 @@
|
||||
<Compile Include="Services\Implement\TagService.cs" />
|
||||
<Compile Include="Services\Implement\TaskService.cs" />
|
||||
<Compile Include="Services\Implement\UserService.cs" />
|
||||
<Compile Include="Services\UnpublishResult.cs" />
|
||||
<Compile Include="Services\UnpublishResultType.cs" />
|
||||
<Compile Include="Services\UserServiceExtensions.cs" />
|
||||
<Compile Include="Settable.cs" />
|
||||
<Compile Include="Strategies\ManifestWatcherComponent.cs" />
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
|
||||
var userService = GetLazyService<IUserService>(container, c => new UserService(scopeProvider, logger, eventMessagesFactory, runtimeState, GetRepo<IUserRepository>(c), GetRepo<IUserGroupRepository>(c),globalSettings));
|
||||
var dataTypeService = GetLazyService<IDataTypeService>(container, c => new DataTypeService(scopeProvider, logger, eventMessagesFactory, GetRepo<IDataTypeRepository>(c), GetRepo<IDataTypeContainerRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IEntityRepository>(c), GetRepo<IContentTypeRepository>(c)));
|
||||
var contentService = GetLazyService<IContentService>(container, c => new ContentService(scopeProvider, logger, eventMessagesFactory, mediaFileSystem, GetRepo<IDocumentRepository>(c), GetRepo<IEntityRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IContentTypeRepository>(c), GetRepo<IDocumentBlueprintRepository>(c)));
|
||||
var contentService = GetLazyService<IContentService>(container, c => new ContentService(scopeProvider, logger, eventMessagesFactory, mediaFileSystem, GetRepo<IDocumentRepository>(c), GetRepo<IEntityRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IContentTypeRepository>(c), GetRepo<IDocumentBlueprintRepository>(c), GetRepo<ILanguageRepository>(c)));
|
||||
var notificationService = GetLazyService<INotificationService>(container, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, logger, GetRepo<INotificationsRepository>(c),globalSettings));
|
||||
var serverRegistrationService = GetLazyService<IServerRegistrationService>(container, c => new ServerRegistrationService(scopeProvider, logger, eventMessagesFactory, GetRepo<IServerRegistrationRepository>(c)));
|
||||
var memberGroupService = GetLazyService<IMemberGroupService>(container, c => new MemberGroupService(scopeProvider, logger, eventMessagesFactory, GetRepo<IMemberGroupRepository>(c)));
|
||||
|
||||
@@ -1385,7 +1385,8 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="templateErrorText">Please make sure that you do not have 2 templates with the same alias</key>
|
||||
<key alias="templateSavedHeader">Template saved</key>
|
||||
<key alias="templateSavedText">Template saved without any errors!</key>
|
||||
<key alias="contentUnpublished">Content unpublished</key>
|
||||
<key alias="contentUnpublished">Content unpublished</key>
|
||||
<key alias="contentVariationUnpublished">Content variation %0% unpublished</key>
|
||||
<key alias="partialViewSavedHeader">Partial view saved</key>
|
||||
<key alias="partialViewSavedText">Partial view saved without any errors!</key>
|
||||
<key alias="partialViewErrorHeader">Partial view not saved</key>
|
||||
|
||||
@@ -243,10 +243,10 @@ namespace Umbraco.Web.Editors
|
||||
//set a custom path since the tree that renders this has the content type id as the parent
|
||||
content.Path = string.Format("-1,{0},{1}", persistedContent.ContentTypeId, content.Id);
|
||||
|
||||
content.AllowedActions = new[] {"A"};
|
||||
content.AllowedActions = new[] { "A" };
|
||||
content.IsBlueprint = true;
|
||||
|
||||
var excludeProps = new[] {"_umb_urls", "_umb_releasedate", "_umb_expiredate", "_umb_template"};
|
||||
var excludeProps = new[] { "_umb_urls", "_umb_releasedate", "_umb_expiredate", "_umb_template" };
|
||||
var propsTab = content.Tabs.Last();
|
||||
propsTab.Properties = propsTab.Properties
|
||||
.Where(p => excludeProps.Contains(p.Alias) == false);
|
||||
@@ -300,7 +300,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
//Remove all variants except for the default since currently the default must be saved before other variants can be edited
|
||||
//TODO: Allow for editing all variants at once ... this will be a future task
|
||||
mapped.Variants = new[] {mapped.Variants.First(x => x.IsCurrent)};
|
||||
mapped.Variants = new[] { mapped.Variants.First(x => x.IsCurrent) };
|
||||
|
||||
return mapped;
|
||||
}
|
||||
@@ -515,7 +515,7 @@ namespace Umbraco.Web.Editors
|
||||
var notificationModel = new SimpleNotificationModel();
|
||||
notificationModel.AddSuccessNotification(
|
||||
Services.TextService.Localize("blueprints/createdBlueprintHeading"),
|
||||
Services.TextService.Localize("blueprints/createdBlueprintMessage", new[]{ content.Name})
|
||||
Services.TextService.Localize("blueprints/createdBlueprintMessage", new[] { content.Name })
|
||||
);
|
||||
|
||||
return notificationModel;
|
||||
@@ -944,23 +944,30 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
if (foundContent == null)
|
||||
HandleContentNotFound(id);
|
||||
|
||||
var unpublishResult = Services.ContentService.Unpublish(foundContent, culture:culture, userId: Security.CurrentUser.Id);
|
||||
|
||||
var unpublishResult = Services.ContentService.Unpublish(foundContent, culture: culture, userId: Security.CurrentUser.Id);
|
||||
|
||||
var content = MapToDisplay(foundContent, culture);
|
||||
|
||||
if (unpublishResult.Success == false)
|
||||
if (!unpublishResult.Success)
|
||||
{
|
||||
AddCancelMessage(content);
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(content));
|
||||
}
|
||||
else
|
||||
{
|
||||
content.AddSuccessNotification(Services.TextService.Localize("content/unPublish"), Services.TextService.Localize("speechBubbles/contentUnpublished"));
|
||||
{
|
||||
//fixme should have a better localized method for when we have the UnpublishResultType.SuccessMandatoryCulture status
|
||||
|
||||
content.AddSuccessNotification(
|
||||
Services.TextService.Localize("content/unPublish"),
|
||||
unpublishResult.Result == UnpublishResultType.SuccessVariant
|
||||
? Services.TextService.Localize("speechBubbles/contentVariationUnpublished", new[] { culture })
|
||||
: Services.TextService.Localize("speechBubbles/contentUnpublished"));
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the dto property values to the persisted model
|
||||
/// </summary>
|
||||
@@ -1215,7 +1222,7 @@ namespace Umbraco.Web.Editors
|
||||
new Dictionary<string, object> { { ContextMapper.CultureKey, culture } });
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,11 @@ namespace Umbraco.Web.Models.Mapping
|
||||
Mandatory = x.Mandatory,
|
||||
Name = source.GetName(x.IsoCode),
|
||||
Exists = source.IsCultureAvailable(x.IsoCode), // segments ??
|
||||
PublishedState = (source.IsCulturePublished(x.IsoCode) ? PublishedState.Published : PublishedState.Unpublished).ToString(),
|
||||
PublishedState = (source.PublishedState == PublishedState.Unpublished //if the entire document is unpublished, then flag every variant as unpublished
|
||||
? PublishedState.Unpublished
|
||||
: source.IsCulturePublished(x.IsoCode)
|
||||
? PublishedState.Published
|
||||
: PublishedState.Unpublished).ToString(),
|
||||
IsEdited = source.IsCultureEdited(x.IsoCode)
|
||||
//Segment = ?? We'll need to populate this one day when we support segments
|
||||
}).ToList();
|
||||
|
||||
Reference in New Issue
Block a user