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:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user