Osy#betaRun it on Osyrin: osyrin.com

Reference / UI

The RecordTimeline kit — what happened to a record, on its own timeline

use Osysharp.RecordTimeline@0; · policy ReadsActivity => …; · [RollsUpTo] <Record> <Reference>; · ActivityTimeline(record.Id) · ActivityOf(record.Id, take) · app.Activity = new ActivitySetup { Keep = … };

`Osysharp.RecordTimeline` puts what happened to a record on its timeline: every event the app and its kits raise about it, one line each, said in the reader's words when it is read — and, on a record others roll up to, what happened to those too. A line is shown only to somebody who may read the record, and kept for a declared time. The app writes one policy and places one component.

preview2 examples compiled by CIuikiteventshistory

Source on GitHub: Osysharp.RecordTimeline — github.com/osysharp/recordtimeline ↗

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 page

Signature#

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, FromLink

Description#

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#

Related

[Event] — what happens to a record, declared once

An event is a class marked `[Event]`: something that happens to a record, declared once by the code that owns the…

[On] — hear an event, after the commit that raised it

`[On]` on a function makes it a HANDLER of the event its one parameter is. Every handler of an event — in the app and…

Records — what any record is called, where it opens, who may read it

`Records.*` answers facts about a row of ANY type from what the app declared — what it is called (`[Title]`), where it…

The Automations kit — rules that do an app's routine work when something happens

`Osysharp.Automations` runs rules — WHEN something happens to a record (any event about it, or every hour), IF the…

The Notifications kit — telling a person that something needs them

`Osysharp.Notifications` tells a person that something needs them — in the app, a bell with a live count and a list…

[OnDelete]

`[OnDelete]` on an `Entity` member says what deleting the row it references does to the row holding it: Remove deletes…

Security.MayRead — may THIS person read this row, under their own rules

`Security.MayRead(person, row)` answers whether a given person may read a given row under THEIR OWN security — the…