Gets the correct variant logging put in to the audit trail, cleans up audit trail messages to make sense, makes sure audit trail is reloaded when changing variants

This commit is contained in:
Shannon
2018-10-19 13:24:36 +11:00
parent 71fd47f452
commit cc4ee6527d
11 changed files with 136 additions and 70 deletions

View File

@@ -14,6 +14,10 @@
/// </summary>
Save,
/// <summary>
/// Used when variant(s) are saved
/// </summary>
SaveVariant,
/// <summary>
/// Used when nodes are opened
/// </summary>
Open,
@@ -26,14 +30,26 @@
/// </summary>
Publish,
/// <summary>
/// Used when nodes are send to publishing
/// Used when variant(s) are published
/// </summary>
PublishVariant,
/// <summary>
/// Used when nodes are sent for publishing
/// </summary>
SendToPublish,
/// <summary>
/// Used when variant(s) are sent for publishing
/// </summary>
SendToPublishVariant,
/// <summary>
/// Used when nodes are unpublished
/// </summary>
Unpublish,
/// <summary>
/// Used when variant(s) are unpublished
/// </summary>
UnpublishVariant,
/// <summary>
/// Used when nodes are moved
/// </summary>
Move,
@@ -72,11 +88,7 @@
/// <summary>
/// Used when a package is uninstalled
/// </summary>
PackagerUninstall,
/// <summary>
/// Used when a node is send to translation
/// </summary>
SendToTranslate,
PackagerUninstall,
/// <summary>
/// Use this log action for custom log messages that should be shown in the audit trail
/// </summary>

View File

@@ -119,7 +119,7 @@ namespace Umbraco.Core.Models
/// The Alias of the ContentType
/// </summary>
[DataMember]
public string Alias
public virtual string Alias
{
get => _alias;
set => SetPropertyValueAndDetectChanges(
@@ -195,7 +195,7 @@ namespace Umbraco.Core.Models
/// <summary>
/// Gets or sets the content variation of the content type.
/// </summary>
public ContentVariation Variations
public virtual ContentVariation Variations
{
get => _variations;
set => SetPropertyValueAndDetectChanges(value, ref _variations, Ps.Value.VaryBy);

View File

@@ -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

View File

@@ -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<IContent>(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<string> 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<IContent>(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;
}
/// <summary>
@@ -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

View File

@@ -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',

View File

@@ -46,10 +46,10 @@
<div style="position: relative;">
<div ng-if="loadingAuditTrail" style="background: rgba(255, 255, 255, 0.8); position: absolute; top: 0; left: 0; right: 0; bottom: 0;"></div>
<umb-load-indicator ng-if="loadingAuditTrail"></umb-load-indicator>
<div ng-show="loadingAuditTrail" style="background: rgba(255, 255, 255, 0.8); position: absolute; top: 0; left: 0; right: 0; bottom: 0;"></div>
<umb-load-indicator ng-show="loadingAuditTrail"></umb-load-indicator>
<div ng-if="auditTrail.length === 0" style="padding: 10px;">
<div ng-show="auditTrail.length === 0" style="padding: 10px;">
<umb-empty-state position="center"
size="small">
<localize key="content_noChanges"></localize>
@@ -58,7 +58,7 @@
<div class="history">
<div ng-if="auditTrail.length > 1" class="history-line"></div>
<div ng-show="auditTrail.length > 1" class="history-line"></div>
<div class="history-item" ng-repeat="item in auditTrail">
@@ -88,7 +88,7 @@
<localize key="auditTrails_small{{ item.logType }}">{{ item.logType }}</localize>
</umb-badge>
<span>
<localize key="auditTrails_{{ item.logType | lowercase }}">{{ item.comment }}</localize>
<localize key="auditTrails_{{ item.logType | lowercase }}" tokens="[item.parameters]">{{ item.comment }}</localize>
</span>
@@ -119,14 +119,14 @@
<umb-control-group data-element="node-info-status" label="@general_status">
<div ng-repeat="status in publishStatus" style="margin-bottom: 5px;">
<span ng-if="status.culture"><em>{{status.culture}}: </em></span>
<span ng-show="status.culture"><em>{{status.culture}}: </em></span>
<umb-badge size="xs" color="{{status.color}}">
{{status.label}}
</umb-badge>
</div>
</umb-control-group>
<umb-control-group ng-if="node.id !== 0" data-element="node-info-create-date" label="@template_createdDate">
<umb-control-group ng-show="node.id !== 0" data-element="node-info-create-date" label="@template_createdDate">
{{node.createDateFormatted}} <localize key="general_by">by</localize> {{ node.owner.name }}
</umb-control-group>
@@ -150,13 +150,13 @@
ng-change="updateTemplate(node.template)">
<option value=""><localize key="general_choose">Choose</localize>...</option>
</select>
<a href="" ng-if="allowChangeTemplate" class="umb-node-preview__action" style="margin-left:15px;" ng-click="openTemplate()" ng-if="node.template !== null">
<a href="" ng-show="allowChangeTemplate && node.template !== null" class="umb-node-preview__action" style="margin-left:15px;" ng-click="openTemplate()">
<localize key="general_open">Open</localize>
</a>
</div>
</umb-control-group>
<umb-control-group ng-if="node.id !== 0" data-element="node-info-id" label="Id">
<umb-control-group ng-show="node.id !== 0" data-element="node-info-id" label="Id">
<div>{{ node.id }}</div>
<small>{{ node.key }}</small>
</umb-control-group>

View File

@@ -139,24 +139,28 @@
</area>
<area alias="auditTrails">
<key alias="atViewingFor">Viewing for</key>
<key alias="delete">Delete Content performed by user</key>
<key alias="unpublish">Unpublish performed by user</key>
<key alias="publish">Save and Publish performed by user</key>
<key alias="save">Save Content performed by user</key>
<key alias="move">Move Content performed by user</key>
<key alias="copy">Copy Content performed by user</key>
<key alias="rollback">Content rollback performed by user</key>
<key alias="sendtopublish">Content Send To Publish performed by user</key>
<key alias="sendtotranslate">Content Send To Translation performed by user</key>
<key alias="delete">Content deleted</key>
<key alias="unpublish">Content unpublished</key>
<key alias="publish">Content saved and Published</key>
<key alias="publishvariant">Content cultures %0% saved and published</key>
<key alias="save">Content saved</key>
<key alias="savevariant">Content cultures %0% saved</key>
<key alias="move">Content moved</key>
<key alias="copy">Content copied</key>
<key alias="rollback">Content rolled back</key>
<key alias="sendtopublish">Content sent for publishing</key>
<key alias="sendtopublishvariant">Content cultures %0% sent for publishing</key>
<key alias="smallCopy">Copy</key>
<key alias="smallPublish">Publish</key>
<key alias="smallPublishVariant">Publish</key>
<key alias="smallMove">Move</key>
<key alias="smallSave">Save</key>
<key alias="smallSaveVariant">Save</key>
<key alias="smallDelete">Delete</key>
<key alias="smallUnpublish">Unpublish</key>
<key alias="smallRollBack">Rollback</key>
<key alias="smallSendToPublish">Send To Publish</key>
<key alias="smallSendToTranslate">Send To Translation</key>
<key alias="smallSendToPublishVariant">Send To Publish</key>
</area>
<area alias="changeDocType">
<key alias="changeDocTypeInstruction">To change the document type for the selected content, first select from the list of valid types for this location.</key>

View File

@@ -144,24 +144,28 @@
</area>
<area alias="auditTrails">
<key alias="atViewingFor">Viewing for</key>
<key alias="delete">Delete Content performed by user</key>
<key alias="unpublish">Unpublish performed by user</key>
<key alias="publish">Save and Publish performed by user</key>
<key alias="save">Save Content performed by user</key>
<key alias="move">Move Content performed by user</key>
<key alias="copy">Copy Content performed by user</key>
<key alias="rollback">Content rollback performed by user</key>
<key alias="sendtopublish">Content Send To Publish performed by user</key>
<key alias="sendtotranslate">Content Send To Translation performed by user</key>
<key alias="delete">Content deleted</key>
<key alias="unpublish">Content unpublished</key>
<key alias="publish">Content saved and Published</key>
<key alias="publishvariant">Content cultures %0% saved and published</key>
<key alias="save">Content saved</key>
<key alias="savevariant">Content cultures %0% saved</key>
<key alias="move">Content moved</key>
<key alias="copy">Content copied</key>
<key alias="rollback">Content rolled back</key>
<key alias="sendtopublish">Content sent for publishing</key>
<key alias="sendtopublishvariant">Content cultures %0% sent for publishing</key>
<key alias="smallCopy">Copy</key>
<key alias="smallPublish">Publish</key>
<key alias="smallPublishVariant">Publish</key>
<key alias="smallMove">Move</key>
<key alias="smallSave">Save</key>
<key alias="smallSaveVariant">Save</key>
<key alias="smallDelete">Delete</key>
<key alias="smallUnpublish">Unpublish</key>
<key alias="smallRollBack">Rollback</key>
<key alias="smallSendToPublish">Send To Publish</key>
<key alias="smallSendToTranslate">Send To Translation</key>
<key alias="smallSendToPublishVariant">Send To Publish</key>
</area>
<area alias="changeDocType">
<key alias="changeDocTypeInstruction">To change the document type for the selected content, first select from the list of valid types for this location.</key>

View File

@@ -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<AuditLog> GetPagedEntityLog(int id,
int pageNumber = 1,
int pageSize = 0,

View File

@@ -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; }
}
}

View File

@@ -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));
}
/// <summary>