Search by

protonsystems / content-reviewer

proton.systems

Reusable Drupal module for plugin-based content issue scanning, admin review reporting, and deterministic fix commands.

Package info

gitlab.com/Proton.Systems/drupal/content-reviewer

Issues

Type:drupal-module

pkg:composer/protonsystems/content-reviewer

Statistics

Installs: 49

Dependents: 0

Suggesters: 0

Stars: 0

v1.3.0 2026-11-09 14:57 UTC

This package is auto-updated.

Last update: 2026-11-09 19:57:58 UTC


README

Reusable Drupal module for plugin-based content issue scanning, admin review reporting, and deterministic fix commands.

Purpose

The module is intended to be reused across Drupal 10 and Drupal 11 projects by installing it as a Composer package and enabling it in the target site.

Requirements

  • PHP 8.3+
  • Drupal 10 or 11
  • ProtonSystems qa package (bundles the qa_audit_core shared submodule)
  • Paragraphs

content-reviewer uses the shared scanner services from the bundled qa_audit_core submodule inside protonsystems/qa and layers UI, reports, and deterministic fix commands on top.

Features

  • Provides an admin review UI at /admin/reports/content-reviewer
  • Lists enabled issue plugins through drush content-reviewer:list
  • Scans issues and exports structured JSON through drush content-reviewer:scan
  • Applies deterministic fixes for auto-fixable issue plugins through drush content-reviewer:fix
  • Checks orphan paragraph references across every stored revision and translation of the paragraph's recorded parent, plus all SQL entity-reference fields that can point to paragraphs
  • Excludes paragraphs with missing parent metadata, incoming references, or incomplete reference scans from cleanup
  • Rechecks orphan candidates immediately before deletion

Built-In Issue Types

  • orphan_paragraphs
  • broken_references
  • hardcoded_internal_absolute_urls
  • linkit_legacy_links
  • linkit_broken_uuids
  • missing_files
  • accessibility_basics
  • redirect_health
Issue typeWhat it reportsScope and limits
missing_filesFile entities whose files are absent from local storageChecks local Drupal stream wrappers. Remote and custom non-local wrappers are counted as not checked; no network request is made.
accessibility_basicsImage fields or rich-text images without alternative text, rich-text links without a label, and link fields without an explicit titleScans current node, paragraph, and media entities. A missing link-field title is a review signal because its formatter may show another label. It cannot decide from context whether an image is decorative or a label is meaningful.
redirect_healthInternal redirects to missing nodes, redirect chains, and loopsRequires the Redirect module. It checks exact internal paths and direct node targets; arbitrary routes and external destinations are not HTTP-checked.

These reports are diagnostic only. They do not edit content, delete file entities, or change redirect records. Use drush content-reviewer:scan <issue_id> --stdout to export each report as JSON.

The linkit_broken_uuids report includes current paragraph rows and historical paragraph revisions. It groups repeated historical anchors by paragraph, field, link, and UUID, and shows the matching revision IDs and the parent metadata from each row. The admin report defaults to 50 occurrences; append ?limit=0 to include every occurrence in the report. Missing parent metadata is a review signal, not proof that a paragraph is unused.

The fix only scans and updates current rows by default. It synchronizes the matching active revision row only when its stored value still matches the current row; older revisions are read-only unless explicitly included with --set=include_revisions=1. Preview both scopes before applying a fix, especially when revisions are included:

drush content-reviewer:fix linkit_broken_uuids --dry-run
drush content-reviewer:fix linkit_broken_uuids --dry-run --set=include_revisions=1

To apply a reviewed fix to historical revisions, explicitly pass --dry-run=0 --set=include_revisions=1.

The linkit_legacy_links fix follows the same safety rules: it updates current paragraph content and its matching active revision snapshot by default, and does not patch older revisions unless --set=include_revisions=1 is supplied.

The hardcoded_internal_absolute_urls report identifies URLs that can be normalized by removing the same-site host. It checks whether /node/{nid} targets exist, but does not make HTTP requests; other destinations are marked not checked. The fix skips missing node targets and only normalizes their URL when the report can verify the node exists or the destination was not checked.

The broken_references report describes current node field rows and adds source publication status. It is a diagnostic report and does not repair references. The orphan paragraph report checks all SQL entity-reference fields that target paragraphs in addition to each paragraph's recorded parent history. A custom storage reference field or scan error blocks deletion of candidates that could not be checked. The result therefore means no incoming reference was found in the scanned storage; it is not a claim about references outside Drupal's registered SQL entity fields.

Installation

composer require protonsystems/content-reviewer
drush en content_reviewer

Usage

Generic issue commands:

drush content-reviewer:list
drush content-reviewer:scan orphan_paragraphs --stdout
drush content-reviewer:fix orphan_paragraphs --dry-run

Useful options:

  • --output=<path>: write JSON output to a file
  • --stdout: print scan results to stdout
  • --pretty=0|1: control pretty-printed JSON output
  • --set=key=value: pass plugin-specific options, repeat as needed
  • --dry-run=0|1: control whether fix commands persist changes

Examples:

drush content-reviewer:scan broken_references --set=max_reference_fields=300 --stdout
drush content-reviewer:scan hardcoded_internal_absolute_urls --set=hosts=example.com,www.example.com --stdout
drush content-reviewer:fix hardcoded_internal_absolute_urls --dry-run=0 --set=hosts=example.com,www.example.com
drush content-reviewer:fix linkit_broken_uuids --dry-run=0

Legacy orphan paragraph commands:

drush content_reviewer:analyze-orphaned-paragraphs
drush content_reviewer:verify-orphans
drush content_reviewer:delete-orphaned-paragraphs

The orphan paragraph report separates current references, historical-only references, absence from the recorded parent's revisions, and records that could not be checked. The cleanup commands only consider the absence group; paragraphs with missing parent metadata remain for manual review.

Releasing A New Version On Packagist

Packagist publishes versions of protonsystems/content-reviewer from git tags. Do not add a version field to composer.json; create and push a new release tag instead.

For the first public release, submit the public Git repository URL to Packagist. If you want automatic updates from GitLab, configure the Packagist integration in GitLab under Settings > Integrations using your Packagist username and API token.

Prepare the release commit on main:

git checkout main
git pull origin main
composer validate --no-check-publish --strict
git status
git add <files>
git commit -m "Release v1.0.0"
git push origin main

Create and push an annotated tag for the release:

git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0

Use semantic version tags such as v1.0.0. After the tag is pushed, Packagist should detect the new version automatically. If it does not appear, open the package page in Packagist and trigger a manual update.

Notes

  • Keep a DB snapshot before non-dry-run cleanup commands
  • Prefer internal or entity-relative replacements for same-site links instead of hardcoded production URLs
  • protonsystems/qa must expose a compatible qa_audit_core submodule release line before external Packagist installs of this module will resolve