<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mwiki.costasano.club/index.php?action=history&amp;feed=atom&amp;title=ICT%3AFinalConfig_-_ASSET_-_automatic_numbering</id>
	<title>ICT:FinalConfig - ASSET - automatic numbering - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mwiki.costasano.club/index.php?action=history&amp;feed=atom&amp;title=ICT%3AFinalConfig_-_ASSET_-_automatic_numbering"/>
	<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:FinalConfig_-_ASSET_-_automatic_numbering&amp;action=history"/>
	<updated>2026-07-23T02:48:53Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.1</generator>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:FinalConfig_-_ASSET_-_automatic_numbering&amp;diff=694&amp;oldid=prev</id>
		<title>Mngr: Created page with &quot;= DigitalAsset Numbering Engine (Architecture + Lua + JavaScript) =  Document revision: {{#time:Y-m-d|{{REVISIONTIMESTAMP}}}} by {{REVISIONUSER}}  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 understandi...&quot;</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:FinalConfig_-_ASSET_-_automatic_numbering&amp;diff=694&amp;oldid=prev"/>
		<updated>2026-02-13T21:30:46Z</updated>

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