Summary#
use Osysharp.RecordTimeline@0; gives every record a timeline, and the app names no event and no record to get it. What
happens is already said by the app's events — and every kit's — so the kit hears them all and keeps
one line for each on the record it is about:
policy ReadsActivity => IsRep; // who reads a record's activity
ActivityTimeline(quote.Id); // on the record's pageSignature#
use Osysharp.RecordTimeline@0; // with Osysharp.Identity@0, Osysharp.Workflow and Osysharp.Ui
policy ReadsActivity => <who reads a record's activity>; // REQUIRED
[RollsUpTo] Opportunity Opportunity; // on a reference: this record's lines are on that one's timeline too
app.Activity = new ActivitySetup { Keep = TimeSpan.FromDays(365) }; // optional — how long a line is kept
ActivityTimeline(Guid record, int take = 50) // the timeline, newest first, on whatever page shows the record
List<ActivityLine> ActivityOf(Guid record, int take = 50) // its lines — Id, At, Said, From, FromLinkDescription#
One line for every event about the record#
The kit hears every event ([[function-on#every]]) and writes a line on the record the event is about — its first record-typed member — after the commit that raised it. An event about no record is not on any timeline.
Rolled up#
A reference marked [RollsUpTo] ([[stdlib-records#rolls-up-to]]) says that what happens to this record happened to
that one too: a quote's lines are also on its opportunity's timeline, each naming the quote it came from.
Said when read#
A line keeps the EVENT, and says it with the event's own Sentence() when the timeline is read — in the reader's
language, with the event's records read as the reader may read them. A renamed record reads with its new name, and a
record the reader may not read is never named: the event says its label instead.
Only for somebody who may read the record#
ActivityOf reads the record as its reader, through the line: a record they may not read has an empty timeline,
whatever ReadsActivity says. SECURITY-INVARIANT activity-is-shown-only-on-a-record-its-reader-may-read.
Kept for a declared time#
A line is kept a year by default — a record's history is read while it is worked and when it is looked back on — and an
hourly sweep removes older ones; set Keep to shorten it. A line goes with its record. What an event carried can be
personal, so a line keeps it classified as content. The platform's own copy of a delivered event is gone after a week
([[function-raise#retention]]): the timeline is where a record's history lives.
Examples#
A quote's story, on its opportunity#
app Pipeline {
use Osysharp.Identity@0;
use Osysharp.RecordTimeline@0;
use Osysharp.Workflow;
use Osysharp.Ui;
model "model/**/*.osy";
}
using Osysharp.RecordTimeline;
[Role] enum PipelineRole { Authenticator, Rep }
// The example runs signed out, so anybody reads.
policy ReadsActivity => IsAnonymous || IsAuthenticated;
[Title(Name)]
entity Opportunity {
[Required, MaxLength(120)] string Name;
security { allow read, create, update when IsAnonymous || IsAuthenticated; }
}
[Title(Title)]
entity Quote {
[Required, MaxLength(120)] string Title;
[RollsUpTo] Opportunity Opportunity;
security { allow read, create, update when IsAnonymous || IsAuthenticated; }
}
[Event("Quote sent")]
public sealed class QuoteSent { public Quote Quote; }
[Event("Quote accepted")]
public sealed class QuoteAccepted {
public Quote Quote;
public override string Sentence() { return $"{Quote.Title} was accepted."; }
}
/// An opportunity's page, with everything that happened to it and its quotes.
[Route("/opportunities/{id}")] [Authorize] [Render(CSR)]
component OpportunityPage(Guid id) {
render { ActivityTimeline(id); }
}[Test]
void A_quotes_events_are_on_its_opportunity_too() {
var o = new Opportunity { Name = "Fleet renewal" };
var q = new Quote { Title = "Twelve vans", Opportunity = o };
Raise(new QuoteSent { Quote = q });
Raise(new QuoteAccepted { Quote = q });
Background.Settle();
Assert.Equal(["Quote sent", "Twelve vans was accepted."], ActivityOf(o.Id).Select(l => l.Said).OrderBy(s => s).ToList());
Assert.Equal(2, ActivityOf(q.Id).Count);
}See also#
- [Event] — what happens to a record, declared once — what a line is: an event, and the sentence it says
- [On] — hear an event, after the commit that raised it — how the kit hears every event
- Records — what any record is called, where it opens, who may read it —
[RollsUpTo], and what any record is called - The Automations kit — rules that do an app's routine work when something happens — every rule's run is an event, so it is on the timeline too
- The Notifications kit — telling a person that something needs them — telling a person, rather than keeping the record's story
- [OnDelete] — why a line goes with its record
kits/record-timeline/tests/— every behaviour above as a test, the timeline under--pixels