public virtual void Process <T>(EActionType type, EActionScope scope, Mutator mutator, T current, T source) where T : Data <T> { var client = new AmazonSimpleNotificationServiceClient(Region); var payload = RenderPayload(type, scope, mutator, current, source); SendMessage(client, payload).Wait(); }
public virtual PublishRequest RenderPayload <T>(EActionType type, EActionScope scope, Mutator mutator, T current, T source) where T : Data <T> { return(new PublishRequest { Message = new { type = type.ToString(), scope = scope.ToString(), payload = current ?? source }.ToJson(), Subject = $"{(current ?? source).GetFullIdentifier()}", TopicArn = Topic }); }
private static void ProcAfterPipeline(EActionType action, EActionScope scope, Mutator mutator, T currentModel, T originalModel) { if (Info <T> .Settings?.Pipelines?.After == null) { return; } foreach (var afterActionPipeline in Info <T> .Settings.Pipelines.After) { try { afterActionPipeline.Process(action, scope, mutator, currentModel, originalModel); } catch (Exception e) { if (!Info <T> .Settings.Silent) { Current.Log.Add <T>(e); } } } }
private static T ProcBeforePipeline(EActionType action, EActionScope scope, Mutator mutator, T currentModel, T originalModel) { if (Info <T> .Settings?.Pipelines?.Before == null) { return(currentModel); } foreach (var beforeActionPipeline in Info <T> .Settings.Pipelines.Before) { try { currentModel = beforeActionPipeline.Process(action, scope, mutator, currentModel, originalModel); } catch (Exception e) { if (!Info <T> .Settings.Silent) { Current.Log.Add <T>(e); } } } return(currentModel); }
public virtual Dictionary <string, object> Headers <T>(ref DataAccessControl accessControl, Dictionary <string, StringValues> requestHeaders, EActionScope scope, T model) where T : Data <T> { var moderationSetup = ModerationHelper.Setup <T>(); var ctx = new Dictionary <string, object> { { "moderated", true } }; var customModerationPipeline = moderationSetup.CustomModerationPipeline; var allowedActions = moderationSetup.ModerationActions; if (customModerationPipeline != null && scope == EActionScope.Model) { allowedActions = customModerationPipeline.GetModerationActions(EActionType.Read, scope, null, model, null) ?? allowedActions; } if (allowedActions.Moderate) { ctx.Add("moderator", true); } if (allowedActions.Whitelisted) { ctx.Add("whiteListed", true); } if (allowedActions.Author) { ctx.Add("canPost", true); } if (customModerationPipeline != null) { var headerPayloadDictionary = new Dictionary <string, List <string> >(); var onInsert = customModerationPipeline.OnInsertAbstracts(); if (onInsert?.Any() == true) { headerPayloadDictionary.Add("add", onInsert); } var onUpdate = customModerationPipeline.OnUpdateAbstracts(); if (onUpdate?.Any() == true) { headerPayloadDictionary.Add("edit", onUpdate); } var onRemove = customModerationPipeline.OnRemoveAbstracts(); if (onRemove?.Any() == true) { headerPayloadDictionary.Add("del", onRemove); } if (headerPayloadDictionary.Any()) { ctx.Add("abstract", headerPayloadDictionary); } } var canShow = allowedActions.Read; ctx.Add("canShow", canShow); if (canShow) { var path = Current.Context.Request.Path.ToUriComponent(); path = path.Remove(path.Length - 1); if (path.Contains("moderation/task")) { path = path.Replace("moderation/task", ""); } ctx.Add("baseUrl", path); } var ret = new Dictionary <string, object> { { "x-zen-moderation", ctx } }; return(ret); }
public T Process <T>(EActionType type, EActionScope scope, Mutator mutator, T current, T source) where T : Data <T> { // This is the main Moderation control flow. Let's start by setting up some helper classes: var moderationSetup = ModerationHelper.Setup <T>(); var customModerationPipeline = moderationSetup.CustomModerationPipeline; var allowedActions = customModerationPipeline?.GetModerationActions(type, scope, mutator, current, source) ?? moderationSetup.ModerationActions; var currentPerson = App.Current.Orchestrator.Person?.Locator; //Log.Info<T>($"MOD {type} | {currentPerson}"); //Log.Info<T>($"MOD {type} | {allowedActions.ToJson()}"); //Log.Info<T>($"MOD {type} | {current.ToReference()}"); var currentKey = current.GetDataKey(); // What can this user do? // Before anything, check if the target entry is marked as Imported. In that case the whole Moderation pipeline is sidestepped. var importModelMember = moderationSetup.Setup.ImportModelMember; if (importModelMember != null) { if (!current.HasMember(importModelMember)) { var msg = $"Moderation Pipeline: Invalid configuration - missing [{importModelMember}] ImportModelMember"; Base.Current.Log.Warn <T>(msg); throw new InvalidOperationException(msg); } switch (type) { case EActionType.Insert: var status = current.GetMemberValue <bool>(importModelMember); if (status) { return(current); // Imported record, nothing to be done here. } break; case EActionType.Update: var newStatus = current.GetMemberValue <bool>(importModelMember); var oldStatus = source.GetMemberValue <bool>(importModelMember); if (source != null) { if (newStatus != oldStatus) { throw new ArgumentException($"Moderation Pipeline: Import flag [{importModelMember}] cannot be changed."); } } if (newStatus) { return(current); } break; } } // Let's determine the Creator of this record. string creatorLocator = null; var creatorLocatorModelMember = moderationSetup.Setup.CreatorLocatorModelMember; if (creatorLocatorModelMember != null) { if (!current.HasMember(creatorLocatorModelMember)) { var msg = $"Moderation Pipeline: Invalid configuration - missing [{creatorLocatorModelMember}] CreatorLocatorModelMember"; Base.Current.Log.Warn <T>(msg); throw new InvalidOperationException(msg); } creatorLocator = current.GetMemberValue <string>(creatorLocatorModelMember); } var isCreator = creatorLocator == currentPerson; string oRationale = null; // There are two kinds of Moderation action that this pipeline can handle. One is a Moderation Task.... var controlPayload = ModerationHelper.GetModerationControlPayload(); if (controlPayload != null) { // This is a Moderation Task operation - say, like an User trying to delete their own entry, // or a Mod changing things. We can use some extra data to help with the process. var oTaskId = controlPayload?.___ModerationTaskId; oRationale = controlPayload?.___ModerationRationale; if (oTaskId != null) { var oTask = ModerationTask <T> .Get(oTaskId); switch (type) { case EActionType.Insert: case EActionType.Update: // Let's change the payload to the new content. var _os = oTask.Entry.ToJson(); var _ns = current.ToJson(); if (_ns != _os) { oTask.Entry = current; oTask.Save(); new ModerationLog <T> { SourceId = currentKey, Entry = current, Action = type.ToString() }.Save(); Base.Current.Log.Info <T>($"Moderation Pipeline: PATCH {oTaskId}"); } // Moderator/Whitelisted? Accept it. if (allowedActions.Moderate || allowedActions.Whitelisted || moderationSetup.Setup.CreatorCanWithdraw && isCreator) { ModerationTask <T> .Approve(oTaskId); } break; case EActionType.Remove: if (allowedActions.Moderate || allowedActions.Whitelisted) { ModerationTask <T> .Withdraw(oTaskId); } else { ModerationTask <T> .Reject(oTaskId); } break; } } } // The second kind is regular models posted by users. Let's determine their fate: var mustCreateTask = false; var clearModel = false; if (!(allowedActions.Author || allowedActions.Whitelisted)) { throw new InvalidOperationException("Moderation Pipeline: Not authorized."); } switch (type) { case EActionType.Insert: case EActionType.Update: if (!(allowedActions.Moderate || allowedActions.Whitelisted)) { mustCreateTask = true; } if (allowedActions.Whitelisted) // User is Whitelisted { if (type == EActionType.Update) // If it's an update, { if (!isCreator) // Whitelisted user isn't the creator { mustCreateTask = true; } } } break; case EActionType.Remove: if (allowedActions.Moderate) { break; } if (allowedActions.Whitelisted && isCreator) { break; } if (moderationSetup.Setup.CreatorCanWithdraw && isCreator) { break; } mustCreateTask = true; break; default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } if (mustCreateTask) // { var statusModelMember = moderationSetup.Setup.StatusModelMember; if (statusModelMember != null) { if (current.HasMember(statusModelMember)) { if (!current.SetMemberValue(statusModelMember, null)) // Mark as 'Waiting Moderation'. { Base.Current.Log.Warn <T>($"Could NOT change moderation status on [{statusModelMember}] | {type}, entry {{{currentKey}}}"); } } } var moderationTask = new ModerationTask <T> { SourceId = currentKey, Entry = current, Action = type.ToString(), AuthorLocator = currentPerson, Rationale = oRationale }; moderationTask.Save(); clearModel = true; } new ModerationLog <T> { SourceId = currentKey, Entry = current, Action = type.ToString(), AuthorLocator = currentPerson, Result = mustCreateTask ? States.EResult.TaskCreated : States.EResult.Approved }.Save(); if (moderationSetup.Setup.NotifyChanges) { var ePer = new Email(); var emailContent = "A new entry has been submitted.<br/><br/>" + "Entry: " + current.ToReference() + "<br/>" + "Creator: " + App.Current.Orchestrator.Person + "<br/><br/>"; ePer.AddTo(App.Current.Orchestrator.Application.GetGroup("CUR")); ePer.AddCc(App.Current.Orchestrator.Application.GetGroup("MOD")); ePer.Title = $"{currentPerson}: {type} {Info<T>.Settings.FriendlyName}"; emailContent += !mustCreateTask ? "User was whitelisted: Action was automatically approved." : "Action was submitted for moderation."; ePer.Content = emailContent; ePer.Send(); } if (clearModel) { current = null; } return(current); }
public void Process <T>(EActionType type, EActionScope scope, Mutator mutator, T current, T source) where T : Data <T> { if (scope != EActionScope.Model) { return; } var sourceId = current.GetDataKey(); var versionModel = new ModelVersioning <T> { SourceId = sourceId, Entry = current, Action = type }; try { if (type == EActionType.Update) { var diffMap = new List <string>(); string diffExpression = null; var serializedSource = source.ToJson(); var serializedModel = current.ToJson(); if (serializedSource == serializedModel) { return; // Completely similar records. Ignore. } try { var sourcePropertyDictionary = source.ToMemberDictionary(); var modelPropertyDictionary = current.ToMemberDictionary(); var compareMap = new List <string>(); compareMap.AddRange(sourcePropertyDictionary.Keys); foreach (var coKey in modelPropertyDictionary.Keys) { if (!compareMap.Contains(coKey)) { compareMap.Add(coKey); } } foreach (var i in compareMap) { var sourceValue = (sourcePropertyDictionary.ContainsKey(i) ? sourcePropertyDictionary[i] : null).ToJson(); var modelValue = (modelPropertyDictionary.ContainsKey(i) ? modelPropertyDictionary[i] : null).ToJson(); if (!sourceValue.Equals(modelValue)) { diffMap.Add(i); } } if (diffMap.Count > 0) { diffExpression = diffMap.Aggregate((i, j) => i + ", " + j); } } catch (Exception) { } var delta = serializedModel.Length - serializedSource.Length; if (delta > 0) { versionModel.Summary = $"+{Math.Abs(delta)}"; } if (delta < 0) { versionModel.Summary = $"-{Math.Abs(delta)}"; } if (delta == 0) { versionModel.Summary = "!="; } if (diffExpression != null) { versionModel.Summary += " | " + diffExpression; } } } catch (Exception) { } Base.Log.Add <T>($"{versionModel.Id} {type} {versionModel.Summary}"); versionModel.Insert(); }
public Dictionary <string, object> Headers <T>(ref DataAccessControl accessControl, Dictionary <string, StringValues> requestHeaders, EActionScope scope, T model) where T : Data <T> { return(null); }
internal static IHeaderDictionary AddModelHeaders <T>(this IHeaderDictionary responseHeaders, ref DataAccessControl accessControl, IQueryCollection sourceQuery, EActionScope scope, T model = null) where T : Data <T> { var sourceParameters = sourceQuery.ToDictionary(i => i.Key, i => i.Value); if (Info <T> .Settings?.Pipelines?.Before != null) { foreach (var pipelineMember in Info <T> .Settings.Pipelines.Before) { AddHeaders(responseHeaders, pipelineMember.Headers(ref accessControl, sourceParameters, scope, model)); } } if (Info <T> .Settings?.Pipelines?.After != null) { foreach (var pipelineMember in Info <T> .Settings.Pipelines.After) { AddHeaders(responseHeaders, pipelineMember.Headers(ref accessControl, sourceParameters, scope, model)); } } return(responseHeaders); }
public T Process <T>(EActionType type, EActionScope scope, Mutator mutator, T current, T source) where T : Data <T> { return(current); }
public virtual Dictionary <string, object> Headers <T>(ref DataAccessControl accessControl, Dictionary <string, StringValues> requestHeaders, EActionScope scope, T model) where T : Data <T> { var ctx = new List <string>(); if (!CanBrowse()) { accessControl.Read = false; return(new Dictionary <string, object>()); } ctx.Add("browse"); if (requestHeaders?.ContainsKey(Mutator.CommonMetadataKeys.Set) == true) { if (!string.IsNullOrEmpty(requestHeaders[Mutator.CommonMetadataKeys.Set])) { if (requestHeaders[Mutator.CommonMetadataKeys.Set] != Constants.CURRENT_LIVE_WORKSET_TAG) { accessControl.Write = false; accessControl.Remove = false; } } } if (CanModify()) { ctx.Add("modify"); } else { accessControl.Write = false; accessControl.Remove = false; } return(new Dictionary <string, object> { { "x-zen-setversion", ctx } }); }