Skip to content

Annotations Workflows — Developer Reference

Developer-focused reference. For module overview, workflow states, and permissions see README.md.


Bundle attachment — how it works

The workflow YAML ships with entity_types: {} — no bundles attached. Annotation types are user-defined and cannot be hardcoded at install time. Attachment is handled in three places:

  • annotations_workflows_install() (procedural, in .install) — runs after the workflow config is imported; iterates all existing annotation_type entities and attaches each one. Covers the case where the module is enabled after types already exist. Must remain procedural — Drupal's HookCollectorPass forbids hook_install as an OOP attribute hook.
  • AnnotationsWorkflowHooks::entityInsert() — fires on every new annotation_type save and attaches the workflow immediately.
  • AnnotationsWorkflowHooks::entityDelete() — fires on annotation_type deletion and detaches the workflow to avoid orphaned bundle references.

How moderation integrates with the annotation edit form

annotation extends EditorialContentEntityBase. When a workflow is attached, content_moderation automatically injects the content_moderation_control pseudo-field widget into AnnotationEditForm. One form save = one revision = value change and state change together. No custom form altering is needed or present in this module.

Draft saves write only to annotation_field_revision (not the live annotation_field_data table). Published saves become the default revision and update the live table. Coverage reports read only published (default) revisions; the annotation edit form shows the latest revision regardless of state.


DIY without this module

You do not need annotations_workflows to have content moderation on annotations. To set it up yourself:

  1. Enable content_moderation and workflows directly.
  2. Go to /admin/config/workflow/workflows and create a new workflow.
  3. Set the entity type to Annotation and select the bundles (annotation types) to moderate.
  4. Configure states and transitions to match your editorial process.
  5. Run drush cex to export the workflow config into config/sync.

From that point forward, config sync manages the workflow. No module is required to keep it alive.

The only thing skipped is automatic bundle attachment: when a new annotation type is created in future, you will need to visit /admin/config/workflow/workflows and add the new bundle manually.


Skipping the module entirely in a recipe

workflows.workflow.annotations.yml ships with dependencies.enforced.module: [annotations_workflows] (see "Why the enforced dependency matters" below). If you include the exported YAML in a recipe or profile that does not also install annotations_workflows, Drupal's config-dependency validation (ConfigInstaller::getMissingDependencies(), which explicitly folds enforced deps into the check) treats the module as a missing dependency and silently skips importing the config — no error, the workflow just never gets created.

To actually skip the module, delete the enforced block (and, if annotations_workflows is no longer listed anywhere else in dependencies.module, drop that too) from the exported YAML before shipping it. Bundle attachment is then handled at recipe-apply time by whatever bundles the shipped entity_types key already lists — there is no dynamic attachment without the module's hooks, so the recipe's config must already include every bundle it wants moderated.

The module exists to make interactive setup easier, not to be a required runtime dependency of the config it ships.


Why the enforced dependency matters — and what it costs you at uninstall

The enforced dependency (config/install/workflows.workflow.annotations.ymldependencies.enforced.module: annotations_workflows) exists so drush cim never treats the workflow as orphaned config and strips it. That protection has a corollary: ConfigManager::uninstall() treats an enforced dependency the same as any other module dependency when deciding what to delete. Uninstalling annotations_workflows deletes the active workflows.workflow.annotations config entity outright — verified live (2026-07-23): drush pm:uninstall annotations_workflows immediately made drush config:get workflows.workflow.annotations report the config does not exist. A config/sync/workflows.workflow.annotations.yml file exported earlier is untouched on disk, but it does not "continue to apply" — moderation stops the moment the module is uninstalled, and re-running drush cim afterwards will not bring it back while the module stays disabled, for the same missing-dependency reason described above.

To genuinely decouple the workflow before uninstalling: export it (drush cex), edit the exported YAML to remove dependencies.enforced.module: annotations_workflows, re-import it (drush cim or drush config:import --partial) so the active config's dependency block matches, and only then uninstall the module. You lose the automatic bundle-attachment hooks either way — new annotation types will need to be attached manually via /admin/config/workflow/workflows.

Recommended path for recipes: install annotations_workflows, create annotation types, run drush cex, strip the enforced dependency from the exported workflows.workflow.annotations.yml, then include that edited file in the recipe. Drop the module from the recipe's dependencies — it is not needed at runtime, and shipping the un-edited export will cause it to be silently skipped per the section above.