diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs
index 21a9c9ad15..a08f2291b4 100644
--- a/src/Umbraco.Core/Services/IContentService.cs
+++ b/src/Umbraco.Core/Services/IContentService.cs
@@ -364,7 +364,7 @@ namespace Umbraco.Core.Services
///
/// Unpublishes a document or optionally unpublishes a culture and/or segment for the document.
///
- 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);
///
/// Gets a value indicating whether a document is path-publishable.
diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs
index 0047c53982..6531e0081f 100644
--- a/src/Umbraco.Core/Services/Implement/ContentService.cs
+++ b/src/Umbraco.Core/Services/Implement/ContentService.cs
@@ -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 _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
}
///
- 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.
///
- private Attempt TryUnpublishVariationInternal(IScope scope, IContent content, EventMessages evtMsgs, string culture, string segment, int userId)
+ private Attempt 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();
+ //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();
}
///
@@ -1097,7 +1115,7 @@ namespace Umbraco.Core.Services.Implement
///
///
///
- 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);
}
///
@@ -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(content, evtMsgs)))
{
Logger.Info($"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)
diff --git a/src/Umbraco.Core/Services/PublishResult.cs b/src/Umbraco.Core/Services/PublishResult.cs
index aeb981d74b..073d7ce1cb 100644
--- a/src/Umbraco.Core/Services/PublishResult.cs
+++ b/src/Umbraco.Core/Services/PublishResult.cs
@@ -4,6 +4,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Services
{
+
///
/// Represents the result of publishing a document.
///
diff --git a/src/Umbraco.Core/Services/PublishResultType.cs b/src/Umbraco.Core/Services/PublishResultType.cs
index e3c34b8439..b4bfe078b7 100644
--- a/src/Umbraco.Core/Services/PublishResultType.cs
+++ b/src/Umbraco.Core/Services/PublishResultType.cs
@@ -1,7 +1,8 @@
namespace Umbraco.Core.Services
{
+
///
- /// A value indicating the result of (un)publishing a content item.
+ /// A value indicating the result of publishing a content item.
///
public enum PublishResultType : byte
{
@@ -9,20 +10,15 @@
// every failure codes as >128 - see OperationResult and OperationResultType for details.
///
- /// The (un)publishing was successful.
+ /// The publishing was successful.
///
Success = 0,
///
- /// The item was already (un)published.
+ /// The item was already published.
///
SuccessAlready = 1,
-
- ///
- /// The specified variant was unpublished, the content item itself remains published.
- ///
- SuccessVariant = 2,
-
+
///
/// The operation failed.
///
diff --git a/src/Umbraco.Core/Services/UnpublishResult.cs b/src/Umbraco.Core/Services/UnpublishResult.cs
new file mode 100644
index 0000000000..7cd1506e6c
--- /dev/null
+++ b/src/Umbraco.Core/Services/UnpublishResult.cs
@@ -0,0 +1,29 @@
+using Umbraco.Core.Events;
+using Umbraco.Core.Models;
+
+namespace Umbraco.Core.Services
+{
+ ///
+ /// Represents the result of unpublishing a document.
+ ///
+ public class UnpublishResult : OperationResult
+ {
+ ///
+ /// Creates a successful result
+ ///
+ ///
+ ///
+ public UnpublishResult(EventMessages eventMessages, IContent entity) : base(UnpublishResultType.Success, eventMessages, entity)
+ {
+ }
+
+ public UnpublishResult(UnpublishResultType result, EventMessages eventMessages, IContent entity) : base(result, eventMessages, entity)
+ {
+ }
+
+ ///
+ /// Gets the document.
+ ///
+ public IContent Content => Entity;
+ }
+}
diff --git a/src/Umbraco.Core/Services/UnpublishResultType.cs b/src/Umbraco.Core/Services/UnpublishResultType.cs
new file mode 100644
index 0000000000..010c37d7a5
--- /dev/null
+++ b/src/Umbraco.Core/Services/UnpublishResultType.cs
@@ -0,0 +1,39 @@
+namespace Umbraco.Core.Services
+{
+ ///
+ /// A value indicating the result of unpublishing a content item.
+ ///
+ public enum UnpublishResultType : byte
+ {
+ ///
+ /// The unpublishing was successful.
+ ///
+ Success = 0,
+
+ ///
+ /// The item was already unpublished.
+ ///
+ SuccessAlready = 1,
+
+ ///
+ /// The specified variant was unpublished, the content item itself remains published.
+ ///
+ SuccessVariant = 2,
+
+ ///
+ /// The specified variant was a mandatory culture therefore it was unpublished and the content item itself is unpublished
+ ///
+ SuccessMandatoryCulture = 3,
+
+ ///
+ /// The operation failed.
+ ///
+ /// All values above this value indicate a failure.
+ Failed = 128,
+
+ ///
+ /// The publish action has been cancelled by an event handler.
+ ///
+ FailedCancelledByEvent = Failed | 5,
+ }
+}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 456d7d667a..92854ea74c 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -1420,6 +1420,8 @@
+
+
diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs
index 91002739d1..485cce4c0a 100644
--- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs
+++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs
@@ -161,7 +161,7 @@ namespace Umbraco.Tests.TestHelpers
var userService = GetLazyService(container, c => new UserService(scopeProvider, logger, eventMessagesFactory, runtimeState, GetRepo(c), GetRepo(c),globalSettings));
var dataTypeService = GetLazyService(container, c => new DataTypeService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c)));
- var contentService = GetLazyService(container, c => new ContentService(scopeProvider, logger, eventMessagesFactory, mediaFileSystem, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c)));
+ var contentService = GetLazyService(container, c => new ContentService(scopeProvider, logger, eventMessagesFactory, mediaFileSystem, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c)));
var notificationService = GetLazyService(container, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, logger, GetRepo(c),globalSettings));
var serverRegistrationService = GetLazyService(container, c => new ServerRegistrationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c)));
var memberGroupService = GetLazyService(container, c => new MemberGroupService(scopeProvider, logger, eventMessagesFactory, GetRepo(c)));
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
index 85cb96864e..1bf64d524c 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
@@ -1385,7 +1385,8 @@ To manage your website, simply open the Umbraco back office and start adding con
Please make sure that you do not have 2 templates with the same alias
Template saved
Template saved without any errors!
- Content unpublished
+ Content unpublished
+ Content variation %0% unpublished
Partial view saved
Partial view saved without any errors!
Partial view not saved
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index 4f8051640a..e7bd4a6942 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -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;
}
- }
-
+ }
+
///
/// Maps the dto property values to the persisted model
///
@@ -1215,7 +1222,7 @@ namespace Umbraco.Web.Editors
new Dictionary { { ContextMapper.CultureKey, culture } });
return display;
- }
-
+ }
+
}
}
diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs
index e0b2746aa2..968a2a08e3 100644
--- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs
@@ -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();