ICT:FinalConfig - ASSET - automatic numbering
Appearance
DigitalAsset Numbering Engine (Architecture + Lua + JavaScript)
Document revision: 2026-02-13 by Mngr
This page explains the architecture and implementation of the automatic numbering system for the DigitalAsset entity. It contains:
- The conceptual workflow
- The Lua module (Module:DigitalAssetID)
- The JavaScript logic (MediaWiki:DigitalAsset.js)
- Notes on integration with Page Forms
This page is for study and understanding before implementation.
1. Conceptual Workflow
The identifier of a DigitalAsset follows the pattern:
<ChapterCode>-<ContextCode>-<SequenceNumber>
Where:
- ChapterCode comes from the selected Chapter
- ContextCode comes from either Place OR Organisation
- SequenceNumber is a 4‑digit counter starting at 0001 for each (Chapter, Context) pair
The identifier becomes:
- the page name of the DigitalAsset
- the file name of the uploaded file (with extension)
- an immutable archival reference
The user workflow:
- User selects Chapter
- User selects Place OR Organisation (mutually exclusive)
- Lua module computes next sequence number + identifier
- JavaScript fills the form fields and locks identifier
- User uploads a file → JS renames it to the identifier
- Save → file upload + Cargo store + page creation
2. Lua Module: Module:DigitalAssetID
This module performs:
- Exclusivity checks
- Fetching Chapter.code
- Fetching Place.code or Organisation.code
- Querying Cargo for existing sequence numbers
- Computing next sequence number
- Formatting the identifier
- Returning JSON to JavaScript
-- Module:DigitalAssetID
local p = {}
local cargo = mw.ext.cargo
-- Helper: zero-pad sequence number
local function pad(num)
return string.format("%04d", tonumber(num))
end
-- Main function: compute identifier
function p.generate(frame)
local args = frame.args
local chapterPage = args.chapter_id
local placePage = args.place_id
local orgPage = args.organisation_id
-- 1. Enforce exclusivity
if (placePage ~= "" and orgPage ~= "") then
return mw.text.jsonEncode({
error = "Select either a Place OR an Organisation, not both."
})
end
if (placePage == "" and orgPage == "") then
return mw.text.jsonEncode({
error = "You must select a Place OR an Organisation."
})
end
-- 2. Fetch Chapter code
local chapterRes = cargo.query(
"ResearchChapters",
"code",
{ where = string.format("_pageName='%s'", chapterPage) }
)
if not chapterRes[1] then
return mw.text.jsonEncode({ error = "Invalid chapter selected." })
end
local chapterCode = chapterRes[1].code
-- 3. Fetch context code (Place or Organisation)
local contextCode = nil
local contextTable = nil
local contextPage = nil
if placePage ~= "" then
contextTable = "Places"
contextPage = placePage
else
contextTable = "Organisations"
contextPage = orgPage
end
local ctxRes = cargo.query(
contextTable,
"code",
{ where = string.format("_pageName='%s'", contextPage) }
)
if not ctxRes[1] then
return mw.text.jsonEncode({ error = "Invalid context selected." })
end
contextCode = ctxRes[1].code
-- 4. Determine next sequence number
local existing = cargo.query(
"DigitalAssets",
"sequence_number",
{
where = string.format(
"chapter_id='%s' AND (place_id='%s' OR organisation_id='%s')",
chapterPage, placePage, orgPage
),
orderBy = "sequence_number DESC",
limit = 1
}
)
local nextSeq = 1
if existing[1] and existing[1].sequence_number then
nextSeq = tonumber(existing[1].sequence_number) + 1
end
-- 5. Format identifier
local identifier = string.format(
"%s-%s-%s",
chapterCode,
contextCode,
pad(nextSeq)
)
-- 6. Return result
return mw.text.jsonEncode({
identifier = identifier,
sequence_number = nextSeq
})
end
return p
3. JavaScript: MediaWiki:DigitalAsset.js
This script:
- Watches for changes in Chapter / Place / Organisation
- Enforces exclusivity
- Calls the Lua module
- Fills identifier + sequence number
- Locks identifier
- Renames uploaded file
- Overrides page name before submission
/* MediaWiki:DigitalAsset.js */
$(document).ready(function () {
function getField(name) {
return $("[name='DigitalAsset[" + name + "]']");
}
function generateIdentifier() {
const chapter = getField("chapter_id").val();
const place = getField("place_id").val();
const org = getField("organisation_id").val();
// Exclusivity check
if (place && org) {
alert("Select either a Place OR an Organisation, not both.");
return;
}
if (!chapter || (!place && !org)) {
return; // Not enough info yet
}
// Call Lua module
new mw.Api().get({
action: "scribunto-console",
title: "Module:DigitalAssetID",
question: "return require('Module:DigitalAssetID').generate{chapter_id='" +
chapter + "', place_id='" + place + "', organisation_id='" + org + "'}"
}).done(function (data) {
const result = JSON.parse(data.return);
if (result.error) {
alert(result.error);
return;
}
// Fill fields
getField("identifier").val(result.identifier).prop("readonly", true);
getField("sequence_number").val(result.sequence_number);
// Override page name
$("input[name='pfFormPageName']").val(result.identifier);
// Override file name on upload
$("input[type='file']").on("change", function () {
const file = this.files[0];
if (!file) return;
const ext = file.name.split('.').pop();
const newName = result.identifier + "." + ext;
$(this).attr("data-filename", newName);
});
});
}
// Trigger generation when fields change
getField("chapter_id").change(generateIdentifier);
getField("place_id").change(generateIdentifier);
getField("organisation_id").change(generateIdentifier);
});
4. Integration Notes
- The Lua module must be saved as: Module:DigitalAssetID
- The JavaScript must be saved as: MediaWiki:DigitalAsset.js
- The Page Form must load the JS via:
{{#tag:html|<script src="/wiki/MediaWiki:DigitalAsset.js"></script>|}}
- The identifier and sequence_number fields must exist in the form
- The identifier field must be editable initially but locked by JS
- The file upload field must be named file_id
5. Status
This page is a study and preparation document. Implementation will follow after validation and testing.