diff --git a/src/Umbraco.Core/Models/AuditType.cs b/src/Umbraco.Core/Models/AuditType.cs index a5ae34a89d..759aac3bfc 100644 --- a/src/Umbraco.Core/Models/AuditType.cs +++ b/src/Umbraco.Core/Models/AuditType.cs @@ -14,6 +14,10 @@ /// Save, /// + /// Used when variant(s) are saved + /// + SaveVariant, + /// /// Used when nodes are opened /// Open, @@ -26,14 +30,26 @@ /// Publish, /// - /// Used when nodes are send to publishing + /// Used when variant(s) are published + /// + PublishVariant, + /// + /// Used when nodes are sent for publishing /// SendToPublish, /// + /// Used when variant(s) are sent for publishing + /// + SendToPublishVariant, + /// /// Used when nodes are unpublished /// Unpublish, /// + /// Used when variant(s) are unpublished + /// + UnpublishVariant, + /// /// Used when nodes are moved /// Move, @@ -72,11 +88,7 @@ /// /// Used when a package is uninstalled /// - PackagerUninstall, - /// - /// Used when a node is send to translation - /// - SendToTranslate, + PackagerUninstall, /// /// Use this log action for custom log messages that should be shown in the audit trail /// diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index b2ac8610d1..2a57420e9e 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -119,7 +119,7 @@ namespace Umbraco.Core.Models /// The Alias of the ContentType /// [DataMember] - public string Alias + public virtual string Alias { get => _alias; set => SetPropertyValueAndDetectChanges( @@ -195,7 +195,7 @@ namespace Umbraco.Core.Models /// /// Gets or sets the content variation of the content type. /// - public ContentVariation Variations + public virtual ContentVariation Variations { get => _variations; set => SetPropertyValueAndDetectChanges(value, ref _variations, Ps.Value.VaryBy); diff --git a/src/Umbraco.Core/Models/Entities/EntityBase.cs b/src/Umbraco.Core/Models/Entities/EntityBase.cs index 9cd6d1edad..f1ff5f3bf5 100644 --- a/src/Umbraco.Core/Models/Entities/EntityBase.cs +++ b/src/Umbraco.Core/Models/Entities/EntityBase.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Models.Entities [DebuggerDisplay("Id: {" + nameof(Id) + "}")] public abstract class EntityBase : BeingDirtyBase, IEntity { -#if DEBUG +#if ModelDebug public Guid InstanceId = Guid.NewGuid(); #endif @@ -165,7 +165,7 @@ namespace Umbraco.Core.Models.Entities var unused = Key; // ensure that 'this' has a key, before cloning var clone = (EntityBase) MemberwiseClone(); -#if DEBUG +#if ModelDebug clone.InstanceId = Guid.NewGuid(); #endif diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index c4b17e1350..9deadfa5af 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -834,6 +834,15 @@ namespace Umbraco.Core.Services.Implement content.CreatorId = userId; content.WriterId = userId; + //track the cultures that have changed + var culturesChanging = content.ContentType.VariesByCulture() + ? string.Join(",", content.CultureNames.Where(x => x.IsDirty()).Select(x => x.Culture)) + : null; + //TODO: Currently there's no way to change track which variant properties have changed, we only have change + // tracking enabled on all values on the Property which doesn't allow us to know which variants have changed. + // in this particular case, determining which cultures have changed works with the above with names since it will + // have always changed if it's been saved in the back office but that's not really fail safe. + _documentRepository.Save(content); if (raiseEvents) @@ -844,12 +853,8 @@ namespace Umbraco.Core.Services.Implement var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); - var culturesChanging = content.ContentType.VariesByCulture() - ? content.CultureNames.Where(x => x.IsDirty()).Select(x => x.Culture).ToList() - : null; - - if (culturesChanging != null && culturesChanging.Count > 0) - Audit(AuditType.Save, userId, content.Id, $"Saved culture{(culturesChanging.Count > 1 ? "s" : string.Empty)} {string.Join(",", culturesChanging)}"); + if (culturesChanging != null) + Audit(AuditType.SaveVariant, userId, content.Id, $"Saved cultures: {culturesChanging}", culturesChanging); else Audit(AuditType.Save, userId, content.Id); @@ -1002,9 +1007,14 @@ namespace Umbraco.Core.Services.Implement } else { - Audit(AuditType.Unpublish, userId, content.Id, $"Culture \"{culture}\" unpublished"); + //unpublishing a specific culture + Audit(AuditType.UnpublishVariant, userId, content.Id, $"Culture \"{culture}\" unpublished", culture); if (!content.Published) + { + //log that the whole content item has been unpublished due to mandatory culture unpublished Audit(AuditType.Unpublish, userId, content.Id, $"Unpublished (culture \"{culture}\" is mandatory)"); + } + result = content.Published ? UnpublishResultType.SuccessCulture : UnpublishResultType.SuccessMandatoryCulture; } scope.Complete(); @@ -1034,7 +1044,7 @@ namespace Umbraco.Core.Services.Implement var publishing = content.PublishedState == PublishedState.Publishing; var unpublishing = content.PublishedState == PublishedState.Unpublishing; - List culturesChanging = null; + string culturesChanging = null; using (var scope = ScopeProvider.CreateScope()) { @@ -1059,7 +1069,7 @@ namespace Umbraco.Core.Services.Implement } else { - culturesChanging = content.PublishNames.Where(x => x.IsDirty()).Select(x => x.Culture).ToList(); + culturesChanging = string.Join(",", content.PublishNames.Where(x => x.IsDirty()).Select(x => x.Culture)); } } @@ -1169,8 +1179,8 @@ namespace Umbraco.Core.Services.Implement scope.Events.Dispatch(Published, this, new PublishEventArgs(descendants, false, false), "Published"); } - if (culturesChanging != null && culturesChanging.Count > 0) - Audit(AuditType.Publish, userId, content.Id, $"Published culture{(culturesChanging.Count > 1 ? "s" : string.Empty)} {string.Join(",", culturesChanging)}"); + if (culturesChanging != null) + Audit(AuditType.PublishVariant, userId, content.Id, $"Published cultures: {culturesChanging}", culturesChanging); else Audit(AuditType.Publish, userId, content.Id); @@ -1840,24 +1850,32 @@ namespace Umbraco.Core.Services.Implement return false; } + //track the cultures changing for auditing + var culturesChanging = content.ContentType.VariesByCulture() + ? string.Join(",", content.CultureNames.Where(x => x.IsDirty()).Select(x => x.Culture)) + : null; + //TODO: Currently there's no way to change track which variant properties have changed, we only have change + // tracking enabled on all values on the Property which doesn't allow us to know which variants have changed. + // in this particular case, determining which cultures have changed works with the above with names since it will + // have always changed if it's been saved in the back office but that's not really fail safe. + //Save before raising event // fixme - nesting uow? - Save(content, userId); + var saveResult = Save(content, userId); - sendToPublishEventArgs.CanCancel = false; - scope.Events.Dispatch(SentToPublish, this, sendToPublishEventArgs); + if (saveResult.Success) + { + sendToPublishEventArgs.CanCancel = false; + scope.Events.Dispatch(SentToPublish, this, sendToPublishEventArgs); - var culturesChanging = content.ContentType.VariesByCulture() - ? content.CultureNames.Where(x => x.IsDirty()).Select(x => x.Culture).ToList() - : null; + if (culturesChanging != null) + Audit(AuditType.SendToPublishVariant, userId, content.Id, $"Send To Publish for cultures: {culturesChanging}", culturesChanging); + else + Audit(AuditType.SendToPublish, content.WriterId, content.Id); + } - if (culturesChanging != null && culturesChanging.Count > 0) - Audit(AuditType.SendToPublish, userId, content.Id, $"Send To Publish for culture{(culturesChanging.Count > 1 ? "s" : string.Empty)} {string.Join(",", culturesChanging)}"); - else - Audit(AuditType.SendToPublish, content.WriterId, content.Id); + return saveResult.Success; } - - return true; } /// @@ -2007,9 +2025,9 @@ namespace Umbraco.Core.Services.Implement #region Private Methods - private void Audit(AuditType type, int userId, int objectId, string message = null) + private void Audit(AuditType type, int userId, int objectId, string message = null, string parameters = null) { - _auditRepository.Save(new AuditItem(objectId, type, userId, Constants.ObjectTypes.Strings.Document, message)); + _auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.Document), message, parameters)); } #endregion diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js index b2e64983d6..f3dc6c7aa7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js @@ -1,12 +1,13 @@ (function () { 'use strict'; - function ContentNodeInfoDirective($timeout, $location, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource) { + function ContentNodeInfoDirective($timeout, $routeParams, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource) { - function link(scope, element, attrs, ctrl) { + function link(scope, element, attrs, umbVariantContentCtrl) { var evts = []; var isInfoTab = false; + var auditTrailLoaded = false; var labels = {}; scope.publishStatus = []; @@ -63,10 +64,22 @@ if (scope.documentType !== null) { scope.previewOpenUrl = '#/settings/documenttypes/edit/' + scope.documentType.id; } + + //load in the audit trail if we are currently looking at the INFO tab + if (umbVariantContentCtrl) { + var activeApp = _.find(umbVariantContentCtrl.editor.content.apps, a => a.active); + if (activeApp.alias === "umbInfo") { + isInfoTab = true; + loadAuditTrail(); + loadRedirectUrls(); + } + } + } scope.auditTrailPageChange = function (pageNumber) { scope.auditTrailOptions.pageNumber = pageNumber; + auditTrailLoaded = false; loadAuditTrail(); }; @@ -103,6 +116,9 @@ function loadAuditTrail() { + //don't load this if it's already done + if (auditTrailLoaded) { return; }; + scope.loadingAuditTrail = true; logResource.getPagedEntityLog(scope.auditTrailOptions) @@ -124,6 +140,8 @@ setAuditTrailLogTypeColor(scope.auditTrail); scope.loadingAuditTrail = false; + + auditTrailLoaded = true; }); } @@ -230,7 +248,8 @@ if (!newValue) { return; } if (newValue === oldValue) { return; } - if(isInfoTab) { + if (isInfoTab) { + auditTrailLoaded = false; loadAuditTrail(); loadRedirectUrls(); setNodePublishStatus(scope.node); @@ -249,6 +268,7 @@ } var directive = { + require: '^^umbVariantContent', restrict: 'E', replace: true, templateUrl: 'views/components/content/umb-content-node-info.html', diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index 03d4318ba2..bdc4ad6c7f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -46,10 +46,10 @@
-
- +
+ -
+
@@ -58,7 +58,7 @@
-
+
@@ -88,7 +88,7 @@ {{ item.logType }} - {{ item.comment }} + {{ item.comment }} @@ -119,14 +119,14 @@
- {{status.culture}}: + {{status.culture}}: {{status.label}}
- + {{node.createDateFormatted}} by {{ node.owner.name }} @@ -150,13 +150,13 @@ ng-change="updateTemplate(node.template)"> - + Open
- +
{{ node.id }}
{{ node.key }}
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 891e04d3a2..28376bf593 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -139,24 +139,28 @@ Viewing for - Delete Content performed by user - Unpublish performed by user - Save and Publish performed by user - Save Content performed by user - Move Content performed by user - Copy Content performed by user - Content rollback performed by user - Content Send To Publish performed by user - Content Send To Translation performed by user + Content deleted + Content unpublished + Content saved and Published + Content cultures %0% saved and published + Content saved + Content cultures %0% saved + Content moved + Content copied + Content rolled back + Content sent for publishing + Content cultures %0% sent for publishing Copy Publish + Publish Move Save + Save Delete Unpublish Rollback Send To Publish - Send To Translation + Send To Publish To change the document type for the selected content, first select from the list of valid types for this location. 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 f9138f3baf..f466ff699a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -144,24 +144,28 @@ Viewing for - Delete Content performed by user - Unpublish performed by user - Save and Publish performed by user - Save Content performed by user - Move Content performed by user - Copy Content performed by user - Content rollback performed by user - Content Send To Publish performed by user - Content Send To Translation performed by user + Content deleted + Content unpublished + Content saved and Published + Content cultures %0% saved and published + Content saved + Content cultures %0% saved + Content moved + Content copied + Content rolled back + Content sent for publishing + Content cultures %0% sent for publishing Copy Publish + Publish Move Save + Save Delete Unpublish Rollback Send To Publish - Send To Translation + Send To Publish To change the document type for the selected content, first select from the list of valid types for this location. diff --git a/src/Umbraco.Web/Editors/LogController.cs b/src/Umbraco.Web/Editors/LogController.cs index 1205226b8f..dcd69d10b7 100644 --- a/src/Umbraco.Web/Editors/LogController.cs +++ b/src/Umbraco.Web/Editors/LogController.cs @@ -7,6 +7,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors { @@ -16,6 +17,7 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] public class LogController : UmbracoAuthorizedJsonController { + [UmbracoApplicationAuthorize(Core.Constants.Applications.Content, Core.Constants.Applications.Media)] public PagedResult GetPagedEntityLog(int id, int pageNumber = 1, int pageSize = 0, diff --git a/src/Umbraco.Web/Models/ContentEditing/AuditLog.cs b/src/Umbraco.Web/Models/ContentEditing/AuditLog.cs index 2e0dca3fbb..9074accdfe 100644 --- a/src/Umbraco.Web/Models/ContentEditing/AuditLog.cs +++ b/src/Umbraco.Web/Models/ContentEditing/AuditLog.cs @@ -24,7 +24,13 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "logType")] public string LogType { get; set; } + [DataMember(Name = "entityType")] + public string EntityType { get; set; } + [DataMember(Name = "comment")] public string Comment { get; set; } + + [DataMember(Name = "parameters")] + public string Parameters { get; set; } } } diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 709f0d719a..85d7128629 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -33,9 +33,9 @@ namespace Umbraco.Web.Security public WebSecurity(HttpContextBase httpContext, IUserService userService, IGlobalSettings globalSettings) { - _httpContext = httpContext; - _userService = userService; - _globalSettings = globalSettings; + _httpContext = httpContext ?? throw new ArgumentNullException(nameof(httpContext)); + _userService = userService ?? throw new ArgumentNullException(nameof(userService)); + _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); } ///