Content, examples and notification-channel findings from the pre-beta audit

Part of #3620619, covering the content, examples, notification-channel and maintainer-script findings. The rest of the issue is a separate branch, so nothing here touches src/ or the other submodules. Two files outside modules/ do change: translations/untranslated.txt gains the one log message added here, and tests/src/Unit/MetricsWriteTest.php is new.

A commit per finding. Every behaviour change carries a test that fails without it; the documentation-only ones do not.

orchestra_content_eca

The shipped example ECA model declared a plain config dependency on the workflow it starts. ECA recalculates an ECA config entity's dependencies from nothing on every save, putting back only the modules providing the plugins it holds and what its events name in entity types and fields, so that dependency lasted until the first save from the modeller, and deleting the workflow then left the model behind starting nothing. It is enforced now, which the install gate reads the same way, so the example still waits for the workflow to exist and still goes when it goes.

The README names the example and the four modules it spans, since it is optional configuration and so is the workflow it starts: eca_content for the entity event, this module for the action, orchestra_content_moderation for the workflow, and orchestra_interaction_task for the review step inside it.

orchestra_content

The README omitted orchestra_interaction, a hard dependency, and said orchestra_content_moderation starts a process on create or update. It does not: that is orchestra_content_eca, from an ECA model.

orchestra_content_moderation

It ships example_content_review and said nothing about it, so the workflow either appeared unexplained or did not appear with nothing to read about why.

orchestra_examples

The cron responder never cleared the state key the async responder stashes the waiting task in, so once the round-trip finished the key named a consumed token and every later cron run asked for it to be resumed. It clears the key after answering, and a condition on the key stops the model before the resume action when nothing is waiting, so the example no longer asks the engine for something it knows is not there.

orchestra_inbox_notification

onTaskTimeout() was the one notification handler with no guard. The timeout event is dispatched from inside the sweep's fire transaction, which commits the timeout action and the timer's bookkeeping together, so a throw rolled that back and the sweep pushed the deadline forward before retrying: the notification layer decided when the task was asked about again. Not a mail server being down, which neither shipped channel lets out; what throws is resolving the audience or the label, and a subscriber of a site's own that does not guard its send. Those are deterministic, so retrying them hourly for ever is the worse of the two outcomes. Guarded and logged, like the assignment path and the arrival path, and the README says a reminder can be dropped that way.

orchestra_easy_email

The README and a docblock both promised [user:*] resolved per recipient. Easy Email only reaches its per-recipient rendering for a template carrying one of its own one-time-URL tokens; on every other template those tokens were left with nothing. The channel already knows who it is addressing, so it answers them itself whenever the message has a single reader, which is every message it sends one per recipient: with no Cc or Bcc, and with Bcc alone, since blind copies go out individually. Only a message shared under To and Cc has no single account, and the README now describes all three groupings rather than two.

Only a user entity is passed on: a recipient carries an AccountInterface, and AudienceBase::convertAccountsToRecipients() lets an audience of a site's own hand over a session, which the user module's token replacements would call entity methods on, costing the whole notification rather than one empty token. drupal:user is declared now, since the channel resolves that module's tokens on purpose.

orchestra_mail

The generic key looked for a composed subject and body that nothing in Orchestra sets and no node has a field for, and ignored the message param every other key honours, so the one type meant for a site's own words said the same sentence as an unrecognized one. It reads the label and the message now, each falling back to the neutral wording. A Notify node sends neither, so that is what it mails, and the README says so instead of promising author-composed content.

scripts/generate-metrics.php

The generator printed "Wrote docs/metrics.md" and exited zero whether or not the write landed, over a page that still parsed as Markdown either way.

It had nowhere to put a test for that, being a single file of functions with an entry point at the bottom, so requiring it ran it. check-translations already solved this here: a runner over an .inc, split because the .inc is the half that can be wrong quietly. The generator has the same shape now, which is most of the diff and moves no code otherwise: the moved text is identical line for line, and the two scripts generate a byte-identical page. The writing reaches a verdict and hands it back rather than announcing one, and MetricsWriteTest asserts that verdict, not what file_put_contents() does.

Edited by Frank Mably

Merge request reports

Loading
Loading