Detecting field changes via global publish events

Aaron Macdonald asked on February 3, 2023 12:25

Hello all

We have a requirement to detect whether changes have occurred to either of two fields at the point of publishing a particular page type, and if so to perform a cache clear.

We have a solution that works for one of the fields, but not the other, and would appreciate any advice on the problem.

Our solution so far is to utilise the global publish before/after events, capturing the before state in a static variable and comparing it to the after state.

This work for one field which is a simple boolean. However it doesn't work for the other field, which is a page selector limited to a single page (pages data type). The selected page is always the same in both the before and after handlers.

Proof-of-concept for the simple code-only module is included below.

[assembly: AssemblyDiscoverable]
[assembly: RegisterModule(typeof(PageEvents))]
public class PageEvents : Module
{
    private class LandingPageSubset
    {
        public int NodeId { get; set; }

        public int? SelectedPageId { get; set; }

        public bool BooleanField { get; set; }

        public LandingPageSubset(LandingPage page)
        {
            NodeId = page.NodeID;
            SelectedPageId = page.Fields.SelectedPage?.FirstOrDefault()?.NodeID;
            BooleanField = page.BooleanField;
        }        
    }   

    private static List<LandingPageSubset> LandingPagesBeforePublish = new List<LandingPageSubset>();

    public PageEvents() : base("PageEvents")
    {
    }

    protected override void OnInit()
    {
        base.OnInit();

        WorkflowEvents.Publish.Before += Publish_Before;

        WorkflowEvents.Publish.After += Publish_After;
    }    

    private void Publish_After(object sender, WorkflowEventArgs e)
    {
        if (e.Document.ClassName == LandingPage.CLASS_NAME)
        {
            var before = LandingPagesBeforePublish.SingleOrDefault(p => p.NodeId == e.Document.NodeID);

            var after = new LandingPageSubset(e.Document as LandingPage);

            if (before != null && (before.SelectedPageId != after.SelectedPageId || before.BooleanField != after.BooleanField))
            {
                //Cache clear here
            }
        }        
    }                  

    private void Publish_Before(object sender, WorkflowEventArgs e)
    {        
        if (e.Document.ClassName == LandingPage.CLASS_NAME)
        {            
            var typedPage = new DocumentQuery<LandingPage>()
                .Path(e.Document.NodeAliasPath, PathTypeEnum.Single)
                .OnCurrentSite()
                .PublishedVersion()
                .FirstOrDefault();

            LandingPagesBeforePublish.RemoveAll(p => p.NodeId == e.Document.NodeID);

            var before = new LandingPageSubset(typedPage);

            LandingPagesBeforePublish.Add(before);
        }
    }      
}

Correct Answer

Juraj Ondrus answered on February 7, 2023 09:37

I would maybe take the idea from Brenden and use the SaveVersion event or use also the Update events as suggested - I can see the original and changed value in that event for the page selector. Then, I would add maybe a hidden field to the page type which can be used as a change flag. So, when you detect a change in the desired field set this flag. Then, in the publish even check the value of this field and do what you want to do.

0 votesVote for this answer Unmark Correct answer

Recent Answers


Not Applicable answered on February 3, 2023 19:28 (last edited on February 3, 2023 19:30)

In the After event type of the WorkflowEvents Publish event, there is a PublishedDocument property as well. You can try that one instead of the Document property you're currently using.

var after = new LandingPageSubset(e.PublishedDocument as LandingPage);
0 votesVote for this answer Mark as a Correct answer

Aaron Macdonald answered on February 6, 2023 00:35

Hi Marcel unfortunately that doesn't solve the problem.

Also note the same problem occurs within the SaveVersion handlers, whereby if one adds or removes a page to the page selector field, the changed state always appears in both Before and After handlers.

Why would this before/after comparison work for a boolean field and not a page selector field? Does it have something to do with the use of GetRelatedDocuments() behind the scenes, and if so how could we detect a change to this field?

0 votesVote for this answer Mark as a Correct answer

Brenden Kehren answered on February 6, 2023 15:51

In the Document.Update.Before event, you can capture the original version AND the new version like so:

e.Node.GetOriginalValue("FirstName", "")
e.Node.GetValue("FirstName", "")

So maybe you're in the wrong event handler

0 votesVote for this answer Mark as a Correct answer

Aaron Macdonald answered on February 7, 2023 02:05

Hi Brendon unfortunately that still doesn't solve the problem.

We need to do the field change detection on publish, so the Publish_Before and Publish_After handlers must be the relevant ones to use.

The methods you've highlighted exist on the Document property, but there's no Node property.

In the following code beforeCol/afterCol are always null regardless of the page selector field's value.

private void Publish_Before(object sender, WorkflowEventArgs e)
{        
   if (e.Document.ClassName == LandingPage.CLASS_NAME)
   {
      string beforeCol = e.Document.GetOriginalValue(nameof(LandingPage.Fields.SelectedPage)) as string;
      string afterCol = e.Document.GetValue(nameof(LandingPage.Fields.SelectedPage)) as string;

      ...
    }
}

private void Publish_After(object sender, WorkflowEventArgs e)
{        
   if (e.Document.ClassName == LandingPage.CLASS_NAME)
   {
      string beforeCol = e.Document.GetOriginalValue(nameof(LandingPage.Fields.SelectedPage)) as string;
      string afterCol = e.Document.GetValue(nameof(LandingPage.Fields.SelectedPage)) as string;

      ...
    }
}
0 votesVote for this answer Mark as a Correct answer

Aaron Macdonald answered on February 8, 2023 01:53

Hi Juraj thanks for suggestion. DocumentEvents.Update.Before/After do register a change in the page selector field, so we should be able to propagate that to the publish handler.

This does appear to be a bug, as one would expect the page selector field change to register in all the handlers, as is the case with a simple boolean field.

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.