Analyze is a content analysis framework for Drupal that unifies SEO scores, readability metrics, AI insights, and link health in a single content tab. Created by DXPR.
API framework adding analysis data to Drupal entities' Analyze tab.
- See also https://www.drupal.org/project/analyze.
The Analyze module provides an API framework for adding content-related information to Drupal entities with canonical URLs. It doesn't offer any standalone features but serves as a foundation for other modules to build on and add data to the "Analyze" tab. This module is designed to integrate smoothly with various content types and extend its analysis capabilities.
The main feature of the Analyze module is the "Analyze" tab that appears on entities. This tab provides a structured interface where other modules can display their analysis results.
The problem we are trying to solve is that various modules, like the Realtime SEO module, the Google Analytics Node Reports module, and the Editoria11y Accessibility Checker module, all provide wildly different UI structures to content editors. The Statistics module, formerly in Drupal core, does not even provide a UI, except a small counter at the bottom of the content. You can be quite sure your content editors are not seeing it!
We want to unify content analysis to live inside the Analyze tab, placing small reports directly on the Analyze tab page and elaborate reports in secondary tabs. At the same time, we promote a better user experience and solid accessibility standards across our analytics user interfaces.
- Adds an "Analyze" tab to Drupal entities with canonical URLs.
- Provides an API for other modules to display analysis data.
- Includes a linear gauge component for displaying a value on a spectrum.
This repository uses Docker to ensure consistent development and testing environments. Here are the key Docker commands you can use:
To run the Drupal linter:
docker compose run --rm drupal-lintThis command checks your Drupal code for adherence to coding standards and best practices.
To perform Drupal deprecation and analysis checks:
docker compose run --rm drupal-checkThis command analyzes your code for usage of deprecated Drupal APIs and other potential issues.
To automatically fix some coding standard issues:
docker compose run --rm drupal-lint-auto-fixThis command will attempt to automatically fix coding standard violations in your Drupal code.
The DRUPAL_RECOMMENDED_PROJECT environment variable is already defined in the
process. You don't need to specify it when running the commands.
These Docker commands help maintain code quality and compatibility across different Drupal versions. Make sure to run these checks before submitting pull requests or merging changes into the main branch.
After installing the Analyze module, developers can use the provided API to integrate their custom analyzers. There is no immediate user-facing functionality or specific configuration page for the Analyze module itself. Configuration is typically handled through other modules that extend the Analyze module's functionality. Developers should ensure their modules correctly implement the API to display data on the "Analyze" tab.
As this module's main aim is to improve the user experience for content editors and marketers, it is opinionated about its presentation:
- On the Analyze main page, you can display a maximum of three pieces of information.
- Currently, we support two widgets: Tables and Linear Gauges.
- On the secondary tab pages or in your full sitewide topical report, you can display anything you want, unrestricted.
The above rules will put critical content information closely at hand in an easily digestible and accessible format. When users are figuring out if and how to improve the content item, they may choose to delve deeper into a full report. When they choose to delve deeper into a specific topic, they can access a full page-specific or sitewide report where you have full control over the contents.
- Only works on entities with canonical URLs.
The Analyze module is designed to work with other modules that provide specific content analysis features, such as SEO analysis tools, accessibility checkers, and sentiment analysis libraries.
To create your own Analyze plugin, follow these steps:
- Create a new module with the following structure:
my_module/
├── src/
│ └── Plugin/
│ └── Analyze/
│ └── MyAnalyzer.php
└── my_module.info.yml
- Define your module dependencies in
my_module.info.yml:
dependencies:
- analyze:analyze- Create your Analyzer plugin class that extends
AnalyzePluginBase. At minimum, you must:- Implement the
@Analyzeannotation - Override the
renderSummary()method - Override the
renderFullReport()method if you want a detailed view
- Implement the
Example plugin structure:
/**
* @Analyze(
* id = "my_analyzer",
* label = @Translation("My Analyzer"),
* description = @Translation("Description of what this analyzer does")
* )
*/
final class MyAnalyzer extends AnalyzePluginBase {
public function renderSummary(EntityInterface $entity): array {
// Return either:
// - analyze_table with max 3 rows
// - analyze_gauge component
return [
'#theme' => 'analyze_table',
'#table_title' => 'My Analysis',
'#row_one' => [
'label' => 'Metric 1',
'data' => 'Value 1',
],
// ... up to 3 rows
];
}
}- Optional methods you can override:
getFullReportUrl()- Customize or disable the full report URLisEnabled()- Control when the analyzer is availableisApplicable()- Define which entity types/bundles can use this analyzeraccess()- Set permission requirementsextraSummaryLinks()- Add additional links to the summary page
See the analyze_plugin_example module in the codebase for a complete
working example.
All Analyze batch operations are available via Drush for AI agent and CLI workflows.
Quick start:
# List available batch-capable analyzers
drush analyze:batch --list
# Run all analyzers on all enabled content types
drush analyze:batch
# Run specific analyzers on articles
drush analyze:batch \
--analyzers=analyze_ai_sentiments_analyzer \
--types=node:article
# Force re-analysis of first 50 entities
drush analyze:batch --limit=50 --forceCommands:
| Command | Alias | Description |
|---|---|---|
analyze:batch |
ab |
Run batch analysis on entities |
analyze:setup-ai |
analyze-sa |
Install AI skill files |
The Analyze module includes a built-in
Agent Skills file that teaches AI
coding assistants how to run content analysis through natural
language. Run drush analyze:setup-ai to enable, then ask
naturally:
"Run sentiment analysis on all articles"
"Analyze brand voice consistency across the site"
"Check all pages for broken links"
"List available analyzers and which content types they cover"
"Re-analyze the last 50 published nodes with all analyzers"
Quick setup:
drush analyze:setup-ai # All tools
drush analyze:setup-ai --host=claude # Claude Code only
drush analyze:setup-ai --host=agents # Codex/Gemini/Copilot/CursorCompatible with Claude Code, Codex CLI, Gemini CLI, GitHub Copilot, Cursor, and other tools supporting the Agent Skills standard.
To make your analyzer plugin batch-capable, implement
\Drupal\analyze\BatchableAnalyzerInterface. The interface has
three methods:
use Drupal\analyze\BatchableAnalyzerInterface;
class MyAnalyzer extends AnalyzePluginBase
implements BatchableAnalyzerInterface {
/**
* Run analysis on a single entity.
*
* Call your analysis logic directly; do NOT delegate through
* renderSummary() as it builds throwaway render arrays and
* swallows exceptions the batch system needs to see.
*
* If your analyzer calls AI APIs, let AiRateLimitException
* propagate (don't catch it); the batch system handles
* retry with exponential backoff.
*
* Return TRUE only when analysis succeeded and results were
* saved. Return FALSE when skipped or failed.
*/
public function processEntity(
EntityInterface $entity,
bool $force_refresh = FALSE,
): bool {
if (!$force_refresh && $this->hasResults($entity)) {
return FALSE;
}
if ($force_refresh) {
$this->storage->deleteScores($entity);
}
$scores = $this->runAnalysis($entity);
if (empty($scores)) {
return FALSE;
}
$this->storage->saveScores($entity, $scores);
return TRUE;
}
/**
* Check if results exist. May validate content/config hashes.
*/
public function hasResults(
EntityInterface $entity,
): bool {
return !empty(
$this->storage->getScores($entity)
);
}
}Optional: If your analyzer persists results to a DB table,
override countAnalyzedEntities() from AnalyzePluginBase for
fast --status coverage reporting. The default returns 0.
Key rules:
processEntity()must return FALSE on failure; the batch system uses this for honest success/failure reporting.- Do NOT catch
AiRateLimitException; the batch system retries with exponential backoff (2s, 4s, 8s). - Use
$this->renderer->renderInIsolation()(notrender()) if you need to render entities;render()throws in CLI/Drush.
Analyze plugins - these modules register @Analyze plugins that appear in
the Analyze tab:
- AI Brand Voice Analyzer
- Brand voice consistency scoring via AI
- AI Sentiment Analyzer
- Multi-dimensional content tone analysis via AI
- AI Marketing Audit
- Content marketing effectiveness scoring via AI
- AI Security Audit
- Sensitive data leak detection via AI
- Broken Links Analyzer
- Link health monitoring per page
- Search Console Analyzer
- Google Search performance per page
Bundled submodules (ship inside the Analyze project):
- Basic Content Info - Word count and image count per entity
- Node Statistics - Page view counts from the Statistics module
- Google Analytics - Per-page GA data via Google Analytics Reports
Modules that integrate with Analyze data:
- Content Intel - Analyze provides a ContentIntel plugin that exposes analyzer data to the Content Intel framework
- AI - AI-powered analyzers use this as their LLM provider; the batch system handles AI rate-limit backoff
@todo