Repository navigation
feat(content-freshness): unpublished changes and expired entries - #14
Merged
Merged
Conversation
Two rules that need nothing the content API does not already return. - Unpublished changes: a published entry whose saved changes have waited in a draft revision for longer than a threshold (14 days by default). A save on a published entry leaves `updatedAt` alone, but revision ids are ULIDs, so the draft revision's id says when the changes were saved. Changes saved within the stale threshold also keep the entry from counting as stale: someone is working on it. "Mark as reviewed" holds the finding back for the threshold. - Expired: a published entry whose expiry date has passed. The field is the collection's first `datetime` field named like valid_until, end_date, expires_at or deadline; each collection's settings can name another field or switch expiry off. A date without a time lasts until the end of that day. "Ignore" holds it back for a page kept online as an archive. The sweep reads the collections' `datetime` fields with the collection list it already fetches; a check of one entry reads its collection once.
- EmDash stores every datetime value as a UTC instant, reading one without a time as midnight in the site's timezone; a datetime field has no date-only mode. The "bare date lasts to the end of the day" branch therefore never ran, and the finding named the UTC day, a day early for sites east of UTC. A plugin cannot read the site's timezone, so the instant is compared as it is and named to the minute in UTC. - Expiry is opt-in per collection. Detected automatically, it would have given a site that keeps past events online a finding for each of them on install. The report suggests a field named like an expiry date until the collection has one chosen or is set to "Never expires". - An ignored expiry remembers its date, so moving the date forward and letting it pass again is reported. - Unpublished changes count only in collections that keep revisions, where a draft revision means staged changes, and not while they are scheduled to go live, which is overdue-schedule's to report. - Stale drafts are dated by their last save: in a collection with revisions a save leaves `updatedAt` alone, so a draft edited yesterday looked untouched since its creation. - A sweep carries a version; one started by an earlier version of the plugin starts over instead of auditing without the collection details. - More expiry field names (expiration, valid_to, valid_through, *_datetime), and one helper for a collection's datetime fields.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 3 of the content-freshness plan adds two rules. The package stays
"private": true: no changeset, no publish.The rules
updatedAtuntouched. Revision IDs are ULIDs, though, so the draft revision's ID gives the time of the save, without an extra call.draft_revision_id(checked at theemdash@0.39.0tag).Changed from the plan: expiry is opt-in
The plan had the expiry field detected from its name and switched on automatically. The review showed what that does on a site that keeps past events online: a finding for every one of them, straight after installing.
So expiry is now chosen per collection. When a collection has a
datetimefield named like an expiry date (valid_until,end_date,expires_at,valid_through,deadline, …), the report says "Expiry dates not checked: Offers (Valid until)" and links to the settings. There you choose the field, another field, or "Never expires", which also hides the suggestion.If you'd rather have it automatic, it's a one-line change in
expiryFor.Also in this PR
updatedAtalone, so a draft edited yesterday looked untouched since it was created.Code review
/code-review highran before this PR and found 10 issues. 9 are fixed; each has a test that fails with the fix removed, checked one by one.datetimevalue as a UTC instant. A value without a time counts as midnight in the site's timezone; there is no date-only mode. My "bare date lasts until the end of the day" path therefore never ran on real data; only the test host keeps bare strings. The plugin can't read the site timezone, so it compares the instant as stored and names it to the minute in UTC: "Valid until was 2026-01-30 23:00 UTC".updatedAt. Now by the last save.supports: revisions.expiration,valid_to,valid_through,*_datetime).DAY_MSin one place.Not changed:
schema.getCollectionon every check of an entry. It runs in parallel with the other reads, the worst case is 7 of 10 calls, and the revisions check needs it anyway.Verified
119 tests,
emdash-plugin validate, typecheck, build. The bundle is 31.3 KB.scripts/compat-matrix.sh 0.39.0 0.39.1 0.40.0on top of the 0.40.1 baseline: content-freshness 119, analytics 312 on every version.Bridge calls per invocation (limit 10, the budget test checks each):
A live run and screenshots come with the release (plan Phase 4).