ICT:FinalConfig - Asset v2 code
Final Configuration for the Asset Entity – v2
Document revision: 2026-02-18 by Mngr
This page is the authoritative implementation guide for the Asset entity (v2).
It describes, in a single place:
- the Cargo table
- the Asset template
- the Page Form
- the Dashboard
- the Lua numbering module
- the JavaScript form logic
- the required private extension
This document must be followed top-to-bottom.
0. Preconditions
The following entities and infrastructure MUST already exist:
- Chapter entity (with Code)
- Place entity (with Code)
- Organisation entity (with Code)
- AssetType entity
- Asset namespace (ID 3014)
- Cargo and Page Forms extensions
- Scribunto (Lua)
- Custom MediaWiki extension support (self-hosted)
1. Cargo Table and Template
Template:Asset
<noinclude>
Asset data template (v2)
</noinclude>
{{#cargo_declare:
|_table=Assets
|Code=Page
|Label=String
|Chapter=Page
|Place=Page
|Organisation=Page
|Sequence=Integer
|File=Page
|OriginalFilename=String
|Parent=Page
|AssetType=Page
|Description=Text
|Notes=Text
}}
{{#cargo_store:
|Code={{FULLPAGENAME}}
|Label={{{Label|}}}
|Chapter={{{Chapter|}}}
|Place={{{Place|}}}
|Organisation={{{Organisation|}}}
|Sequence={{{Sequence|}}}
|File={{{File|}}}
|OriginalFilename={{{OriginalFilename|}}}
|Parent={{{Parent|}}}
|AssetType={{{AssetType|}}}
|Description={{{Description|}}}
|Notes={{{Notes|}}}
}}
== {{{Label|}}} ==
{{DISPLAYTITLE:{{{Label}}}}}
{{#if:{{{File|}}}|'''File:''' [[{{{File}}}]]}}
{{#if:{{{OriginalFilename|}}}|'''Original filename:''' {{{OriginalFilename}}}}}
{{#if:{{{Parent|}}}|'''Parent asset:''' [[{{{Parent}}}]]}}
'''Description:'''
{{{Description|}}}
'''Notes:'''
{{{Notes|}}}
After creating the template:
- Go to Cargo administration
- Manually create the table **Assets**
2. Page Form
Form:Asset
<noinclude>
Form for creating and editing Asset pages (v2)
</noinclude>
{{{info
|no summary
|no preview
|no minor edit
|no watch
|no footer
}}}
{{{for template|Asset}}}
{| class="formtable"
! Chapter (*)
| {{{field|Chapter
|input type=combobox
|values from namespace=Chapter
|existing values only
}}}
|-
! Place
| {{{field|Place
|input type=combobox
|values from namespace=Place
|existing values only
|placeholder=Use Organisation instead
}}}
|-
! Organisation
| {{{field|Organisation
|input type=combobox
|values from namespace=Organisation
|existing values only
|placeholder=Use Place instead
}}}
|-
! Identifier
| {{{field|Label|readonly}}}
|-
! Sequence
| {{{field|Sequence|readonly}}}
|-
! File (*)
| {{{field|File|input type=upload}}}
|-
! Original filename
| {{{field|OriginalFilename|readonly}}}
|-
! Asset type
| {{{field|AssetType
|input type=combobox
|values from namespace=AssetType
|existing values only
}}}
|-
! Parent asset
| {{{field|Parent
|input type=combobox
|values from namespace=Asset
|existing values only
|placeholder=Top level
}}}
|-
! Description
| {{{field|Description|input type=textarea}}}
|-
! Notes
| {{{field|Notes|input type=textarea}}}
|}
{{{standard input|save}}}
{{{end template}}}
{{#tag:html|
<script src="/wiki/MediaWiki:Asset.js"></script>
|}}
3. Dashboard
Dashboard:Asset
= 🗂️ Asset Dashboard =
{| class="wikitable sortable"
! Code !! Chapter !! Place !! Organisation !! File !! Type
{{#cargo_query:
tables=Assets
|fields=_pageName,_pageTitle,Chapter,Place,Organisation,File,AssetType
|where=_pageNamespace=3014
|order by=_pageTitle
|format=template
|template=AssetRow
|named args=yes
|cache=no
}}
|}
{{#forminput:
form=Asset
|namespace=Asset
|button text=➕ New Asset
|returnto=Dashboard:Asset
}}
<div style="text-align:right; font-size:90%;">
Last updated: {{CURRENTTIME}} UTC
</div>
Template:AssetRow
<includeonly>
|-
| {{#formlink:
form=Asset
|target={{{_pageName}}}
|link text={{{_pageTitle}}}
|returnto=Dashboard:Asset
}}
| {{{Chapter}}}
| {{{Place}}}
| {{{Organisation}}}
| {{{File}}}
| {{{AssetType}}}
</includeonly>
4. Lua Numbering Module
Module:AssetID
local p = {}
local cargo = mw.ext.cargo
local function pad(n)
return string.format("%04d", tonumber(n))
end
function p.generate(frame)
local a = frame.args
local chapter = a.Chapter
local place = a.Place
local org = a.Organisation
if place ~= "" and org ~= "" then
return mw.text.jsonEncode({ error = "Choose Place OR Organisation." })
end
if place == "" and org == "" then
return mw.text.jsonEncode({ error = "Place or Organisation required." })
end
local ctxField = place ~= "" and "Place" or "Organisation"
local ctxValue = place ~= "" and place or org
local res = cargo.query(
"Assets",
"MAX(Sequence)=max",
{
where = string.format(
"Chapter='%s' AND %s='%s'",
chapter, ctxField, ctxValue
)
}
)
local nextSeq = (res[1] and res[1].max or 0) + 1
local code = string.format(
"%s-%s-%s",
mw.title.new(chapter).text,
mw.title.new(ctxValue).text,
pad(nextSeq)
)
return mw.text.jsonEncode({
identifier = code,
sequence = nextSeq
})
end
return p
5. JavaScript
MediaWiki:Asset.js
$(document).ready(function () {
function f(name) {
return $("[name='Asset[" + name + "]']");
}
function generate() {
const ch = f("Chapter").val();
const pl = f("Place").val();
const org = f("Organisation").val();
if (!ch || (pl && org) || (!pl && !org)) return;
new mw.Api().get({
action: "scribunto-console",
title: "Module:AssetID",
question:
"return require('Module:AssetID').generate{" +
"Chapter='" + ch + "'," +
"Place='" + pl + "'," +
"Organisation='" + org + "'}"
}).done(function (data) {
const r = JSON.parse(data.return);
if (r.error) { alert(r.error); return; }
f("Label").val(r.identifier);
f("Sequence").val(r.sequence);
$("input[name='pfFormPageName']").val(r.identifier);
});
}
f("Chapter").change(generate);
f("Place").change(generate);
f("Organisation").change(generate);
$("input[type='file']").on("change", function () {
if (this.files[0]) {
f("OriginalFilename").val(this.files[0].name);
}
});
});
6. Private Extension (Mandatory)
Purpose
The AssetFileNormalizer extension enforces the rule:
The uploaded file is automatically renamed to match the Asset identifier, with no redirect and no user action.
Installation
Create directory:
extensions/AssetFileNormalizer/
Add:
- extension.json
- AssetFileNormalizer.php
(See FinalConfig:AssetFileNormalizer for full code.)
Enable in LocalSettings.php:
wfLoadExtension( 'AssetFileNormalizer' );
7. Immutability Rules
After save, the following fields MUST NOT change:
- Code (page name)
- Label
- Chapter
- Place / Organisation
- File
- OriginalFilename
Other metadata MAY change.
Deletion of an Asset/File couple is sysop-only.
8. Implementation Order (Mandatory)
1. Create Template:Asset 2. Create Cargo table Assets 3. Create Form:Asset 4. Create Dashboard:Asset and Template:AssetRow 5. Create Module:AssetID 6. Create MediaWiki:Asset.js 7. Install and enable AssetFileNormalizer extension 8. Test with fake records 9. Start real data entry
9. Status
Asset v2 is fully specified and ready for production use.