ICT:FinalConfig - AssetFileNormakizer extension
Final Configuration for Extension: AssetFileNormalizer
Document revision: 2026-02-18 by Mngr
This document defines the private MediaWiki extension AssetFileNormalizer.
The extension enforces the archival invariant:
One Asset = one File, both sharing the same immutable identifier.
The extension is part of the Asset v2 architecture and must be considered normative infrastructure.
1. Purpose and Scope
The AssetFileNormalizer extension automatically renames uploaded files associated with Asset pages so that:
- The file name matches the Asset identifier
- The original upload filename is removed
- No redirect is created
- No user intervention is required
This extension applies only to:
- newly created Asset pages
- files uploaded through the Asset form
It does not affect:
- existing files
- non-Asset pages
- manual file moves
2. Design Principles
The extension is designed according to the following principles:
- Minimal responsibility (one job only)
- No dependency on Cargo internals
- No dependency on Page Forms internals
- Server-side authority (no JavaScript enforcement)
- Upgrade-safe MediaWiki APIs
- Non-blocking behavior on failure
The extension intentionally performs no validation of identifiers. It assumes that Asset v2 numbering logic has already produced a valid identifier.
3. Functional Behavior
3.1 Trigger
The extension runs when:
- a page is saved
- the page is newly created
- the page is in the Asset namespace
3.2 Conditions
The extension proceeds only if:
- the page contains a File field
- the referenced file exists
- the file name does not already match the Asset identifier
3.3 Action
If all conditions are met:
- the file is moved to <AssetIdentifier>.<extension>
- file history is preserved
- redirect creation is suppressed
3.4 Failure Policy
If the file move fails:
- the Asset page remains valid
- the file remains under its original name
- a debug log entry is written
The extension never blocks page saving.
4. Directory Structure
The extension is installed as a private extension under:
extensions/AssetFileNormalizer/ ├─ extension.json └─ AssetFileNormalizer.php
5. Extension Registration
5.1 extension.json
{
"name": "AssetFileNormalizer",
"version": "1.0",
"author": "Costa Sano Research",
"description": "Automatically renames Asset files to match Asset identifiers",
"type": "extension",
"Hooks": {
"PageSaveComplete": "AssetFileNormalizer::onPageSaveComplete"
},
"AutoloadClasses": {
"AssetFileNormalizer": "AssetFileNormalizer.php"
},
"ManifestVersion": 2
}
6. Core Implementation
6.1 AssetFileNormalizer.php
<?php
use MediaWiki\MediaWikiServices;
class AssetFileNormalizer {
public static function onPageSaveComplete(
WikiPage $wikiPage,
UserIdentity $user,
string $summary,
int $flags,
RevisionRecord $revisionRecord,
EditResult $editResult
) {
$title = $wikiPage->getTitle();
// Only Asset namespace (ID must match local configuration)
if ( $title->getNamespace() !== 3014 ) {
return;
}
// Only act on page creation
if ( !$editResult->isNewPage() ) {
return;
}
// Extract wikitext content
$content = $revisionRecord->getContent( SlotRecord::MAIN );
if ( !$content instanceof WikitextContent ) {
return;
}
$text = $content->getText();
// Extract File field
if ( !preg_match( '/\|\s*File\s*=\s*(File:[^\|\n]+)/', $text, $m ) ) {
return;
}
$fileTitle = Title::newFromText( trim( $m[1] ) );
if ( !$fileTitle || !$fileTitle->inNamespace( NS_FILE ) ) {
return;
}
$repo = MediaWikiServices::getInstance()
->getRepoGroup()
->getLocalRepo();
$file = $repo->newFile( $fileTitle );
if ( !$file->exists() ) {
return;
}
// Compute target filename
$assetCode = $title->getText();
$extension = $file->getExtension();
$targetTitle = Title::makeTitle(
NS_FILE,
$assetCode . '.' . $extension
);
// Already normalized
if ( $fileTitle->equals( $targetTitle ) ) {
return;
}
// Move file without redirect
$status = $file->move(
$targetTitle,
'Automatic Asset file normalization',
/* suppressRedirect */ true,
$user
);
if ( !$status->isOK() ) {
wfDebugLog(
'AssetFileNormalizer',
'File move failed for ' . $fileTitle->getPrefixedText()
);
}
}
}
7. Enabling the Extension
Add the following line to LocalSettings.php:
wfLoadExtension( 'AssetFileNormalizer' );
Optionally, the extension may be wrapped in a feature flag for emergency disablement.
8. Security and Permissions
- The file move is executed under the saving user context
- Users creating Assets must have:
- upload rights
- movefile rights (or equivalent)
- The extension does not escalate privileges
Deletion of Asset/File couples remains a sysop-only operation.
9. Relationship to Asset v2
This extension is a mandatory component of Asset v2.
Without it:
- Asset creation still works
- File upload still works
- The archival invariant (name equality) is not enforced
With it:
- Asset and File identity are strictly aligned
- The Asset–File couple is sealed at creation time
10. Status
AssetFileNormalizer is complete and ready for deployment.
No database changes are required. No migration of existing files is performed.