Developer
Architecture
- All classes are registered through
Configuration/with autowiring and use constructor injection.Services. yaml - The page module panel and the file list button are PSR-14 event listeners.
- The JSON-LD output is a PSR-15 frontend middleware.
- Backend actions are backend routes. The Content Quality module is an Extbase backend module.
- The rich-text editor button is a CKEditor 5 plugin loaded as an ES module.
The analysis pipeline, started by
Analysis:
PageContentExtractor::extractFromPage() page data from the database
RenderedHeadingExtractor::extract() headings from the rendered page (optional)
AccessibilityAnalyzer / SeoAnalyzer /
ReadabilityAnalyzer / DuplicateContentAnalyzer rule-based issues
SchemaAdvisor PageIntentDetector → SchemaMapper
→ JsonLdGenerator → SchemaValidator
AiAnalyzer, InternalLinkAnalyzer only if AI is enabled
ScoreCalculator scores
→ tx_t3contentquality_result + tx_t3contentquality_history
Using the analysis in your own code
Inject
\Woit\:
use Woit\T3ContentQuality\Service\AnalysisOrchestrator;
final class AnalyzeCommand extends Command
{
public function __construct(
private readonly AnalysisOrchestrator $analysisOrchestrator,
) {
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$result = $this->analysisOrchestrator->analyze(pageUid: 42, languageUid: 0);
$output->writeln('Overall score: ' . $result->overallScore);
return Command::SUCCESS;
}
}
Public methods of
Analysis:
analyze(int $page Uid, int $language Uid = 0): Analysis Result - Runs all checks and stores the result.
getStored Result (int $page Uid, int $language Uid = 0): ?array - Returns the stored result without analysing again.
analyzeSchema With Ai (int $page Uid, int $language Uid = 0): string - Runs AI schema detection and updates the stored result. Returns a
string starting with
ok:orerror:. checkLinks (int $page Uid, int $language Uid, Link Checker $link Checker): array - Checks the links of the page and stores the result.
getHistory (int $page Uid, int $language Uid = 0, int $limit = 20): array - Score history of a page.
getAll Results (): array - Stored results of all pages in the default language.
getUnanalyzed Page Count (): int - Number of visible standard pages without a result.
Note
The extension is in beta state. Class names and method signatures may change until version 1.0. There is no PHP interface for analyzers yet.
Domain models
AnalysisIssue
Immutable value object:
use Woit\T3ContentQuality\Domain\Model\AnalysisIssue;
$issue = new AnalysisIssue(
category: AnalysisIssue::CATEGORY_ACCESSIBILITY,
severity: AnalysisIssue::SEVERITY_ERROR,
message: 'Image has no ALT text.',
suggestion: 'Add a descriptive ALT text.',
);
Categories: CATEGORY_, CATEGORY_,
CATEGORY_, CATEGORY_.
Severities: SEVERITY_, SEVERITY_, SEVERITY_.
AnalysisResult
Collects issues and scores during an analysis:
$result->overallScore; // int, average of accessibility, SEO, readability
$result->accessibilityScore; // int 0–100
$result->seoScore; // int 0–100
$result->readabilityScore; // int 0–100
$result->schemaScore; // int 0–100
$result->providerUsed; // string
$result->modelUsed; // string
$result->metadata; // array, page facts
$result->getIssues(); // AnalysisIssue[]
$result->getIssuesByCategory('seo');
$result->getAiSuggestions(); // string[]
Page data
Page returns an array with the keys
page_, page_, seo_, meta_,
abstract, author, slug, doktype, crdate, tstamp,
schema_, headings (list of level and text),
images, links (list of text and href), text_ and
ctypes.
Analysis adds page_.
Other services
\Woit\T3Content Quality\ Security\ Permission Service can,Show Page () can,Edit Page () can,Edit Page Content () canandApply Fix Type () filterfor the current backend user.Showable Page Uids () \Woit\T3Content Quality\ Service\ Page Fix Applier - Stores AI proposals as pending rows and applies them with
applythrough the DataHandler.Rows () \Woit\T3Content Quality\ Domain\ Repository\ Approved Schema Repository - Reads approved JSON-LD. Used by the frontend middleware.
\Woit\T3Content Quality\ Service\ Ollama Endpoint - Resolves the Ollama URL from ollamaUrl.
Events used
\TYPO3\CMS\ Backend\ Controller\ Event\ Modify Page Layout Content Event Pageadds the quality panel to the page module.Module Content Listener \TYPO3\CMS\ Filelist\ Event\ Process File List Actions Event Filelistadds the metadata button to image rows in the file list.Actions Event Listener
Both listeners are registered in Configuration/ with
the identifiers t3contentquality. and
t3contentquality.. To remove a listener, override its
service definition in your own Services..
Backend routes
All routes require a backend login and the TYPO3 route token. Build URLs
with
\TYPO3\. Each route checks the
page permissions listed in Page permissions; requests for
pages the user may not access are ignored.
| Route identifier | Method | Purpose |
|---|---|---|
t3contentquality_ | GET | Analyse page/language, redirect to the page module. |
t3contentquality_ | GET | Generate pending fixes; fix is title,
description, alt_ or all. |
t3contentquality_ | GET, POST | Apply pending fixes of a page, or the fixes listed in uids. |
t3contentquality_ | GET, POST | Discard pending fixes of a page, or the fixes listed in
uids. |
t3contentquality_ | POST | Generate pending fixes for page (max. 15) of type
fix. |
t3contentquality_ | GET | Check the links of a page. |
t3contentquality_ | GET | AI schema detection. |
t3contentquality_ | POST | Store jsonld as approved schema. |
t3contentquality_ | GET | Delete the approved schema. |
t3contentquality_ | GET | CSV export. |
t3contentquality_ | GET | Fill metadata of the image file, redirect to the
metadata form. |
AJAX route t3contentquality_ (POST, JSON body with
prompt, tone, language, format, max,
language) returns {"text": "…"} or {"error": "…"}.
Frontend middleware
\Woit\ is registered as
woit/. It re-encodes the stored
JSON-LD with JSON_, so < and > in values can not end the
<script> element. To disable it, for example
if you render JSON-LD yourself:
return [
'frontend' => [
'woit/t3-content-quality/json-ld-injector' => [
'disabled' => true,
],
],
];
Schema types
Supported schema.org types and their required and recommended properties
are defined in
\Woit\. Rule-based
detection is in
Page, the mapping of page data to
properties in
Schema. Both are regular services and can be
replaced in Services..
AI providers
All AI requests go through
\Woit\:
call(string $prompt, Ai Settings $settings): string - Text prompt, returns the model's answer.
callVision (string $prompt, string $image Base64, string $mime Type, Ai Settings $settings): string - Prompt plus one image.
\Woit\ reads the
extension configuration and returns an immutable
\Woit\ object (provider, key,
model, limits). The default model IDs are defined only there.
Requests are sent with TYPO3's
\TYPO3\
instead of the vendor SDKs, so the extension also works in classic mode
installations without Composer. For Anthropic the client
- sends
output_andconfig. effort fallbacks: "default"only to models that accept them (see anthropicEffort and anthropicFallback), - reads the answer by content block type, because thinking blocks can come first,
- turns
stop_into an exception with the refusal category.reason: refusal
To add a provider, add a match arm in
Ai and the
provider's settings in
Ai and
ext_.
JavaScript modules
Configuration/ maps the import prefix
@woit/ to
EXT:.
CKEditor/ai- text- generator. js - CKEditor 5 plugin
Aiwith the toolbar itemText Generator ai.Text Generator backend-,loader. js panel-loader. js - Loading indicators for the module and the panel.
Tests
Unit tests are in Tests/ and use PHPUnit 11. They cover the
score calculation, schema validation and type detection, the JSON-LD
escaping in the middleware, the AI settings, the request and response
handling of
Ai and the parsing of AI answers. No
network access or database is needed.
In a standalone checkout of the extension:
composer install
composer test:unit
In a TYPO3 project that has PHPUnit installed:
vendor/bin/phpunit -c vendor/woit/t3-content-quality/Build/phpunit/UnitTests.xml
Permission checks and the DataHandler integration need a database and are not covered by unit tests.