Jump to content

ICT:FinalConfig - Asset v2 code: Difference between revisions

From Costa Sano MediaWiki
Created page with "= Final Configuration for the Asset Entity – v2 = Document revision: {{#time:Y-m-d|{{REVISIONTIMESTAMP}}}} by {{REVISIONUSER}} 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. Category:FinalConfig..."
 
 
Line 191: Line 191:


{{#tag:html|
{{#tag:html|
<script src="/wiki/MediaWiki:Asset.js"></script>
<script>
mw.loader.load(
    mw.util.getUrl(
        'MediaWiki:Asset.js',
        { action: 'raw', ctype: 'text/javascript' }
    )
);
</script>
|}}
|}}
</pre>
</pre>

Latest revision as of 15:12, 18 February 2026

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>
mw.loader.load(
    mw.util.getUrl(
        'MediaWiki:Asset.js',
        { action: 'raw', ctype: 'text/javascript' }
    )
);
</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.