Reg Exp
Web Design
Notes Client
Basic Edit History
If you've been developing in Notes for any amount of time, one thing you've probably accumulated in your "bag of tricks" is an "Edit History" subform where the dates/times of the most recent edits are shown. Here, I'll talk about our basic edit history subform. This is called "basic" because it only shows overall edits (who and when) instead of tracking changes to specific fields. Tracking changes to fields is a lot more involved, so I'll leave that for another time.

One thing our subform does that's a little different is prevent a bunch of duplicates. Most edit history subforms I've seen track every single save. So if you're showing only the last 10 or even 20 edits, you can go into the document, press CTRL+S a bunch of times, and the edit history is wiped out and replaced with many duplicates of the same entry. Ours prevents that from happening by only allowing a duplicate name if the last time they saved was more than 5 minutes ago.

This is all in a subform to allow reuse. So create a subform called Edit History - Basic. We put our edit history into a section, so you have to expand the section to see the history, but that's up to you. Take a look at our subform through this example picture.

There are three hidden fields and a computed value. The first hidden field actually does all the work and maintains the second and third hidden fields. So I'll talk about that last. The second hidden field is called EditHistoryDates and is a date/time field that's editable and allows multiple values. There is no default value, no input translation, and no input validation. The third hidden field is called EditHistoryPeople and is a names field that's editable and allows multiple values. Again there's no default, translation, or validation for this field.

The first editable field is where all the "magic" happens. This field is called PerformHistoryCapture and is a computed for display number field. It's computed for display, so the value is never actually stored with the document. It's purpose it to maintain both the editable fields, making sure they have the same number of values at all times.

The first thing this field value does is check to see if the document is being saved. If it's not being saved, then nothing needs to be added to the edit history, so we quickly exit. This is done with the following code:

FIELD EditHistoryDates := EditHistoryDates;
FIELD EditHistoryPeople := EditHistoryPeople;
CheckSave := @If(@IsDocBeingSaved; 0; @Return(0));

Next, check to see if this is a brand new document. If so, it sets initial values for both fields so we have some frame of reference for the rest of the formula.

CheckEmpty1 := @If(@Elements(EditHistoryDates) = 0; @SetField("EditHistoryDates"; @Now); 0);
CheckEmpty2 := @If(@Elements(EditHistoryPeople) = 0; @SetField("EditHistoryPeople"; @UserName); 0);

The next block of code is our somewhat unique item of checking to see if this save is by the most recent author within the last 5 minutes. If so, we don't add anything more to our fields. But if it's been either more than 5 minutes since the last save, or the last save was by another author (no matter how long ago) then the formula will continue.

LastEditDate := @Subset(EditHistoryDates; 1);
FiveMins := @Time(12; 5; 0) - @Time(12; 0; 0);
LastEditor := @Subset(EditHistoryPeople; 1);
HowLong := @If(@Now - LastEditDate <= FiveMins & LastEditor = @UserName; @Return(0); 0);

Note how we're getting the FiveMins variable. Knowing that formula language uses seconds, we could have used the constant 300 (60 seconds * 5 minutes) in our formula. But this computation takes a fraction of a second and really tells you what is being accomplished - we're subtracting two time values that are five minutes apart. Just seeing the number 300 in the formula wouldn't necessarily lead you to that conclusion right away.

If we haven't exited the formula by this point, then it's all right to add a new entry to the list. We put the most recent edit at the front of each field, and only keep the last 10 edits. So both of those are done here. And this field needs to have some kind of a value, so that is the purpose of the last line of the formula.

NewDates := @Now : EditHistoryDates;
NewPeople := @UserName : EditHistoryPeople;
@SetField("EditHistoryDates"; @If(@Elements(NewDates) >= 10; @Subset(NewDates; 10); NewDates));
@SetField("EditHistoryPeople"; @If(@Elements(NewPeople) >= 10; @Subset(NewPeople; 10); NewPeople));

The computed value puts everything together for displaying to the user. Our formula is:

List := @Text(EditHistoryDates; "D0T1S2") + " by " + @Name([Abbreviate]; EditHistoryPeople);
@Implode(List; @NewLine)

Since computed values must be single values, the multiple values of the variable List are imploded together with a new line separator.

This subform was created in Notes 5 and has been tested in both Notes 5 and Notes 6. If you're in a purely Notes 6 environment, then some things could be done to make the function a little easier to work with (like removing the @Subset statements and working with the lists as arrays).