Jump to content

ICT:FinalConfig - Asset v2 code

From Costa Sano MediaWiki

Final Configuration for the Asset Entity – v2

Document revision: 2026-02-20 by Mngr This revision is adapted taking into account a now stable version 1 Asset test.

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)

{{#cargo_declare:
 |_table=Assets
 |Code=Page
 |Label=String
 |Chapter=Page
 |Place=Page
 |Organisation=Page
 |Sequence=Integer
 |File=String
 |OriginalFilename=String
 |Parent=Page
 |AssetType=Page
 |Description=Text
 |Notes=Text
}}
</noinclude>

{{#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**

Adaptations from previous versions: 1. Cargo_declare should be inside the "noinclude" section 2. File=String instead of Page


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=page
   |namespace=File
   |uploadable=yes
}}}
|-

! 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>
|}}

Adaptation from preious version: The File upload field is adapted to what has been found working in v1. Suggestion: It might be more logical considering the purpose of of OriginalFileName to do the upload under this ambrella.


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
 |size=24
 |placeholder=Create new Asset
 |returnto=Dashboard:Asset
}}

<div style="text-align:right; font-size:90%;">
Last updated: {{CURRENTTIME}} UTC
</div>

Remark: The forminput probably needs adaptation to start with the NILL Asset.

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 (renewed) is fully specified and ready for production use. The Lua Module and Javascript might still need adaptation. During the first try it has also be decided that a private extension will be used.