|
|
| Line 5: |
Line 5: |
| The aim is to create an interface without MediaWiki clutter and well protected to avoid confusing users with unnecessary elements on the page. | | The aim is to create an interface without MediaWiki clutter and well protected to avoid confusing users with unnecessary elements on the page. |
| The Cargo template follows what has been decided in the DBML. It is the implementation of one entity "Place". | | The Cargo template follows what has been decided in the DBML. It is the implementation of one entity "Place". |
| This entity have a special feature: filling in the address will fill in automativcally the coordinnates and in dashboard you then have a link to openstreetmap. In order to realise this a javascript is an extra. | | |
| Also, as we use de facto Dark mode, we need to take some measures in order to avoid the system to give error messages with white boxes in this dark environment. Using Vector-2022 as a skin, it is very tricky to try to solves this modifying the Common.css, again a soluiton via Javascript was preferred.
| | This entity has a special feature: filling in the address will automatically fill in the coordinates and, in the dashboard, provide a link to OpenStreetMap. In order to realize this, JavaScript is used. |
| | |
| | As the system uses de facto Dark mode, special care is taken to avoid default MediaWiki or Page Forms error messages that render as white boxes. Using the Vector-2022 skin, modifying Common.css proved unreliable; therefore, JavaScript-based solutions were preferred. |
| | |
| This configuration deliberately favors explicit behavior and robustness over visual polish or implicit framework behavior, in order to ensure long-term maintainability. | | This configuration deliberately favors explicit behavior and robustness over visual polish or implicit framework behavior, in order to ensure long-term maintainability. |
|
| |
|
| Line 49: |
Line 52: |
| {{{Notes|}}} | | {{{Notes|}}} |
|
| |
|
| {{#if:{{{Latitude}}}{{{Longitude}}}| | | {{#if:{{{Latitude}}}{{{Longitude}}}| |
| '''Map:''' | | '''Map:''' |
| [https://www.openstreetmap.org/?mlat={{{Latitude}}}&mlon={{{Longitude}}} View on OpenStreetMap] | | [https://www.openstreetmap.org/?mlat={{{Latitude}}}&mlon={{{Longitude}}} View on OpenStreetMap] |
| Line 55: |
Line 58: |
| </pre> | | </pre> |
|
| |
|
| Once the template is created don't forget to manually force the creation of the Cargo table. | | Once the template is created, the Cargo table must be created manually. |
| If modifications are done to the table, one needs to manually update the table and then go to the Cargo interface to swap the temporary table to become the official one. Take care what is inside and outside. | | If modifications are made to the table structure, the table must be recreated and the temporary table swapped via the Cargo administration interface. |
| | |
| The field Code stores the full page name (including namespace) to ensure unambiguous identification and safe use as a foreign key across entities. | | The field Code stores the full page name (including namespace) to ensure unambiguous identification and safe use as a foreign key across entities. |
|
| |
|
| Important Conventions: | | Important conventions: |
| - Field names should start with a capital letter | | - Field names start with a capital letter |
| - Code field is used as the Page name | | - Code is the page name and acts as the technical identifier |
| - Dashboard will produce pages with this Cargo definition and a form to be hosted in a dedicated namespace Place: | | - Label is the human-readable name and is mandatory for all entities |
| - The field name Label is mandatory in all entities and must not be renamed, as it is relied upon by shared JavaScript validation logic. | | - Dashboards generate and manage pages in a dedicated namespace (Place) |
| | - The field name Label must not be renamed, as it is relied upon by shared JavaScript validation logic |
|
| |
|
| | | Special lines at the bottom of the template: |
| Special lines in the bottom of the Template: | |
| <pre> | | <pre> |
| == {{{Label|}}} == | | == {{{Label|}}} == |
| Line 78: |
Line 82: |
| {{{Notes|}}} | | {{{Notes|}}} |
|
| |
|
| {{#if:{{{Latitude}}}{{{Longitude}}}| | | {{#if:{{{Latitude}}}{{{Longitude}}}| |
| '''Map:''' | | '''Map:''' |
| [https://www.openstreetmap.org/?mlat={{{Latitude}}}&mlon={{{Longitude}}} View on OpenStreetMap] | | [https://www.openstreetmap.org/?mlat={{{Latitude}}}&mlon={{{Longitude}}} View on OpenStreetMap] |
| }} | | }} |
| </pre> | | </pre> |
| These lines ensure that the human-readable name (Label) is consistently used as the visible title of the page, while the actual page name remains the technical identifier (Code).
| |
|
| |
|
| This decouples the stable identifier from the display name and allows renaming the label without breaking references, Cargo relations, or dashboards.
| | These lines ensure that the human-readable name (Label) is consistently used as the visible page title, while the actual page name remains the stable technical identifier (Code). |
|
| |
|
| The last block is a preparation for the use of the openstreetmap.org.
| | This decoupling allows renaming the label without breaking Cargo relations, dashboards, or foreign keys. |
| This block conditionally displays a link to OpenStreetMap when valid geographic coordinates are available.
| |
|
| |
|
| The coordinates are normally filled automatically via JavaScript based on the Address field. This conditional display avoids broken or misleading map links when no coordinates are present. | | The last block conditionally displays a link to OpenStreetMap only when valid geographic coordinates are present, avoiding broken or misleading links. |
| | |
| | Coordinates are normally filled automatically via JavaScript based on the Address field. |
|
| |
|
| = Page Form: = | | = Page Form: = |
| Line 121: |
Line 125: |
|
| |
|
| ! Parent place | | ! Parent place |
| |{{{field|Parent | | | {{{field|Parent |
| |input type=combobox | | |input type=combobox |
| |values from namespace=Place | | |values from namespace=Place |
| |placeholder=Top level (no parent) | | |placeholder=Top level (no parent) |
| }}} | | }}} |
| |- | | |- |
| Line 130: |
Line 134: |
| ! Type | | ! Type |
| | {{{field|Type | | | {{{field|Type |
| |input type=dropdown | | |input type=dropdown |
| |values=Continent,Country,Region,Province,City,Neighborhood,Street | | |values=Continent,Country,Region,Province,City,Neighborhood,Street |
| }}} | | }}} |
| |- | | |- |
| Line 148: |
Line 152: |
|
| |
|
| ! Notes | | ! Notes |
| | {{{field|Notes|input type=textarea}}} | | | {{{field|Notes|input type=tex |
| | |
| |}
| |
| | |
| {{{standard input|save}}}
| |
| | |
| {{{end template}}}
| |
| </pre>
| |
| | |
| The first section is important to avoid clutter.
| |
| <pre>
| |
| {{{info
| |
| |no summary
| |
| |no preview
| |
| |no minor edit
| |
| |no watch
| |
| |no footer
| |
| }}}
| |
| </pre>
| |
| In the display of the Form:Place page one might see
| |
| <pre>
| |
| {{{end template}}}
| |
| </pre>
| |
| at the bottom. This is not a bug but a side effect too long to explain here.
| |
| | |
| Convention: The field Label (displayed to the user as “Name”) is mandatory for all entities.
| |
| This requirement is enforced via JavaScript at save time, not via the Page Forms mandatory option.
| |
| However, it is not defined like this. A text informs the user about it, and a javascript is used to impose it, rather than the |mandatory option as this generates a white box in the dark environment, difficult to use, and even more difficult to fix this via css for vector-2022 skin.
| |
| | |
| = Dashboard page =
| |
| Dashboard:Place
| |
| <pre>
| |
| = 🗺️ Costa Sano Research PLACE Dashboard =
| |
| | |
| 📖 [[Dashboard:Place/Help|Need Help?]]
| |
| | |
| {| class="wikitable sortable"
| |
| ! Code !! Title !! Type !! Parent !! Map
| |
| {{#cargo_query:
| |
| tables=Places
| |
| |fields=_pageName,_pageTitle,Label,Type,Parent,Latitude,Longitude
| |
| |where=_pageNamespace=3006
| |
| |order by=_pageTitle
| |
| |format=template
| |
| |template=PlaceRow
| |
| |named args=yes
| |
| |cache=no
| |
| }}
| |
| |}
| |
| | |
| Choose your place code corresponding to above table and respecting the naming conventions.
| |
| Take maximum 6 capital letters for the code.
| |
| | |
| {{#forminput:
| |
| form=Place
| |
| |namespace=Place
| |
| |button text=➕ New place
| |
| |returnto=Dashboard:Place
| |
| }}
| |
| | |
| | |
| <div style="text-align:right; font-size:90%;">
| |
| Last updated: {{CURRENTDAY}} {{CURRENTMONTHNAME}} {{CURRENTYEAR}} – {{CURRENTTIME}} UTC
| |
| </div>
| |
| </pre>
| |
| | |
| Different measures are taken to deal with caching data in MediaWiki, Cargo and what happens in a browser.
| |
| <pre>
| |
| |cache=no
| |
| </pre>
| |
| This is needed on the Cargo side to avoid caching and the following puts a new datetime on the page making it different from the previous version with the result that the page is renewed and the cached version is dropped.
| |
| <pre>
| |
| <div style="text-align:right; font-size:90%;">
| |
| Last updated: {{CURRENTDAY}} {{CURRENTMONTHNAME}} {{CURRENTYEAR}} – {{CURRENTTIME}} UTC
| |
| </div>
| |
| </pre>
| |
| Experience shows that this works but not in all circumstances. Therefore a javascript solution is used.
| |
| | |
| The fields Adress, Lattitude and Longitude are used in the javascript. If the user fills in an address recognised by openstreetmap, leaving the field Address generates automatically the geo coordinnates.
| |
| | |
| The layout of the dashboard makes use of a Template:PlaceRow as described below.
| |
| It makes the first column of the table clickable to go back to the Form and edit the record.
| |
| | |
| = Row transclusion Template =
| |
| Template:PlaceRow
| |
| <pre>
| |
| <includeonly>
| |
| |-
| |
| | {{#formlink:
| |
| form=Place
| |
| |target={{{_pageName}}}
| |
| |link text={{{_pageTitle}}}
| |
| |returnto=Dashboard:Place
| |
| }}
| |
| | {{{Label}}}
| |
| | {{{Type}}}
| |
| | {{#if:{{{Parent|}}}|[[{{{Parent}}}]]|—}}
| |
| | {{#if:{{{Latitude}}}{{{Longitude}}}|
| |
| [https://www.openstreetmap.org/?mlat={{{Latitude}}}&mlon={{{Longitude}}} 🗺️]
| |
| | — }}
| |
| </includeonly>
| |
| </pre>
| |
| | |
| =Javascript=
| |
| Multiple javascripts are used to fill the coordinnates from the content of the address field and to deal with mandatory fields.
| |
| | |
| MediaWiki:Common.js
| |
| <Pre>
| |
| /************************************************************
| |
| * 1. AUTO‑PURGE FOR ALL DASHBOARD:* PAGES
| |
| ************************************************************/
| |
| $(function () {
| |
| | |
| // Prevent purge logic from running on edit, history, diff, preview, etc.
| |
| if (mw.config.get('wgAction') !== 'view') return;
| |
| | |
| const title = mw.config.get('wgPageName');
| |
| | |
| if (title && title.startsWith('Dashboard:')) {
| |
| | |
| // Only purge once
| |
| if (!location.search.includes('mw_purged=1')) {
| |
| | |
| new mw.Api().get({
| |
| action: 'query',
| |
| meta: 'tokens',
| |
| type: 'csrf'
| |
| }).done(function (data) {
| |
| | |
| const token = data.query.tokens.csrftoken;
| |
| | |
| new mw.Api().post({
| |
| action: 'purge',
| |
| titles: title,
| |
| token: token
| |
| }).always(function () {
| |
| | |
| // Reload the dashboard normally, marked as purged
| |
| const url = mw.util.getUrl(title, { mw_purged: 1 });
| |
| location.replace(url);
| |
| });
| |
| });
| |
| }
| |
| }
| |
| });
| |
| | |
| /************************************************************
| |
| * 2. PAGE FORMS GEOCODING (ON BLUR ONLY – STABLE)
| |
| ************************************************************/
| |
| | |
| mw.hook('wikipage.content').add(function () {
| |
| | |
| function setupGeocoding(addressField, latField, lonField) {
| |
| | |
| const addr = $('input[name$="[' + addressField + ']"]');
| |
| const lat = $('input[name$="[' + latField + ']"]');
| |
| const lon = $('input[name$="[' + lonField + ']"]');
| |
| | |
| if (!addr.length || !lat.length || !lon.length) return;
| |
| | |
| // Remove previous handlers to avoid duplicates
| |
| addr.off('.pfGeocode');
| |
| | |
| addr.on('blur.pfGeocode', function () {
| |
| | |
| const q = addr.val().trim();
| |
| if (!q) return;
| |
| | |
| console.log('Geocoding on blur:', q);
| |
| | |
| const url =
| |
| 'https://nominatim.openstreetmap.org/search' +
| |
| '?format=json&limit=1&q=' + encodeURIComponent(q);
| |
| | |
| $.getJSON(url, function (data) {
| |
| if (data && data.length) {
| |
| lat.val(data[0].lat).trigger('change');
| |
| lon.val(data[0].lon).trigger('change');
| |
| }
| |
| });
| |
| });
| |
| }
| |
| | |
| // === ENABLE FOR PLACE ===
| |
| setupGeocoding('Address', 'Latitude', 'Longitude');
| |
| });
| |
| | |
| | |
| /************************************************************
| |
| * 3. GENERIC OSM LINK HELPER (SAFE)
| |
| ************************************************************/
| |
| | |
| mw.hook('wikipage.content').add(function () {
| |
| | |
| function setupOSMLink(latField, lonField) {
| |
| | |
| const lat = $('input[name$="[' + latField + ']"]');
| |
| const lon = $('input[name$="[' + lonField + ']"]');
| |
| | |
| if (!lat.length || !lon.length) return;
| |
| | |
| const linkBox =
| |
| $('<div class="pf-osm-link" style="margin-top:4px;"></div>');
| |
| lon.closest('td').append(linkBox);
| |
| | |
| function update() {
| |
| | |
| const la = lat.val();
| |
| const lo = lon.val();
| |
| | |
| if (la && lo) {
| |
| const url =
| |
| 'https://www.openstreetmap.org/?mlat=' + la +
| |
| '&mlon=' + lo;
| |
| linkBox.html(
| |
| '<a href="' + url +
| |
| '" target="_blank">View on OpenStreetMap</a>'
| |
| );
| |
| } else {
| |
| linkBox.empty();
| |
| }
| |
| }
| |
| | |
| lat.on('change keyup', update);
| |
| lon.on('change keyup', update);
| |
| update();
| |
| }
| |
| | |
| // === ENABLE FOR PLACE ===
| |
| setupOSMLink('Latitude', 'Longitude');
| |
| });
| |
| | |
| /************************************************************
| |
| * 4. EMPTY MANDATORY FIELD CHECK
| |
| ************************************************************/
| |
| mw.hook('wikipage.content').add(function () {
| |
| | |
| // Save buttons used by Page Forms
| |
| const saveButtons = $('input[name="wpSave"], button[name="wpSave"]');
| |
| if (!saveButtons.length) {
| |
| console.log('PF required check: save button not found');
| |
| return;
| |
| }
| |
| | |
| // Avoid duplicate bindings
| |
| saveButtons.off('.pfRequiredCheck');
| |
| | |
| saveButtons.on('click.pfRequiredCheck', function (e) {
| |
| | |
| const requiredFields = [
| |
| { name: 'Label', label: 'Name' }
| |
| ];
| |
| | |
| let missing = [];
| |
| | |
| requiredFields.forEach(function (f) {
| |
| const input = $(
| |
| 'input[name$="[' + f.name + ']"], ' +
| |
| 'textarea[name$="[' + f.name + ']"], ' +
| |
| 'select[name$="[' + f.name + ']"]'
| |
| );
| |
| | |
| if (!input.length || !input.val().trim()) {
| |
| missing.push(f.label);
| |
| }
| |
| });
| |
| | |
| if (missing.length) {
| |
| e.preventDefault();
| |
| e.stopImmediatePropagation();
| |
| | |
| alert(
| |
| 'Please fill in the following required field(s):\n\n' +
| |
| missing.join('\n')
| |
| );
| |
| | |
| return false;
| |
| }
| |
| });
| |
| });
| |
| </pre>
| |
| The javasripts are designed to be useful for all pages in the website.
| |
| The entity pages Template:entity should take care to define Label as a field that is considered mandatory via the javascript.
| |
| | |
| All this creates pages in the namespace Place with the name being the code. The page is a configuration to serve in Page Forms extension. Below is an example of such a page.
| |
| <pre>
| |
| {{Place
| |
| |Label=Brussels
| |
| |Parent=EURO
| |
| |Type=City
| |
| |Address=Brussel
| |
| |Latitude=50.8467372
| |
| |Longitude=4.3524930
| |
| |Notes=Nihil
| |
| }}
| |
| </pre>
| |
| | |
| As you can see the page as is cannot be used as such and should be read via the dashboard page.
| |
| | |
| == References: ==
| |
| | |
| * [[mediawikiwiki:Extension:Cargo/Storing_data|Cargo table explanation]]
| |
| * [[mediawikiwiki:Extension:Page_Forms|Page Forms explained]]
| |
| | |
| [[Category:FinalConfig]]
| |
Final Configuration for the Place Entity
Document revision: 2026-02-17 by Mngr
The aim is to create an interface without MediaWiki clutter and well protected to avoid confusing users with unnecessary elements on the page.
The Cargo template follows what has been decided in the DBML. It is the implementation of one entity "Place".
This entity has a special feature: filling in the address will automatically fill in the coordinates and, in the dashboard, provide a link to OpenStreetMap. In order to realize this, JavaScript is used.
As the system uses de facto Dark mode, special care is taken to avoid default MediaWiki or Page Forms error messages that render as white boxes. Using the Vector-2022 skin, modifying Common.css proved unreliable; therefore, JavaScript-based solutions were preferred.
This configuration deliberately favors explicit behavior and robustness over visual polish or implicit framework behavior, in order to ensure long-term maintainability.
Cargo Table:
Template:Place
<noinclude>
Place data template
</noinclude>
{{#cargo_declare:
|_table=Places
|Code=Page
|Parent=Page
|Label=String
|Type=String
|Address=String
|Latitude=Float
|Longitude=Float
|Notes=Text
}}
{{#cargo_store:
|Code={{FULLPAGENAME}}
|Parent={{{Parent|}}}
|Label={{{Label|}}}
|Type={{{Type|}}}
|Address={{{Address|}}}
|Latitude={{{Latitude|}}}
|Longitude={{{Longitude|}}}
|Notes={{{Notes|}}}
}}
== {{{Label|}}} ==
{{DISPLAYTITLE:{{{Label}}}}}
{{#if:{{{Parent|}}}|
'''Parent place:''' [[{{{Parent}}}]]
}}
'''Notes:'''
{{{Notes|}}}
{{#if:{{{Latitude}}}{{{Longitude}}}|
'''Map:'''
[https://www.openstreetmap.org/?mlat={{{Latitude}}}&mlon={{{Longitude}}} View on OpenStreetMap]
}}
Once the template is created, the Cargo table must be created manually.
If modifications are made to the table structure, the table must be recreated and the temporary table swapped via the Cargo administration interface.
The field Code stores the full page name (including namespace) to ensure unambiguous identification and safe use as a foreign key across entities.
Important conventions:
- Field names start with a capital letter
- Code is the page name and acts as the technical identifier
- Label is the human-readable name and is mandatory for all entities
- Dashboards generate and manage pages in a dedicated namespace (Place)
- The field name Label must not be renamed, as it is relied upon by shared JavaScript validation logic
Special lines at the bottom of the template:
== {{{Label|}}} ==
{{DISPLAYTITLE:{{{Label}}}}}
{{#if:{{{Parent|}}}|
'''Parent place:''' [[{{{Parent}}}]]
}}
'''Notes:'''
{{{Notes|}}}
{{#if:{{{Latitude}}}{{{Longitude}}}|
'''Map:'''
[https://www.openstreetmap.org/?mlat={{{Latitude}}}&mlon={{{Longitude}}} View on OpenStreetMap]
}}
These lines ensure that the human-readable name (Label) is consistently used as the visible page title, while the actual page name remains the stable technical identifier (Code).
This decoupling allows renaming the label without breaking Cargo relations, dashboards, or foreign keys.
The last block conditionally displays a link to OpenStreetMap only when valid geographic coordinates are present, avoiding broken or misleading links.
Coordinates are normally filled automatically via JavaScript based on the Address field.
Page Form:
Form:Place
Form for creating and editing Place pages.
no summary
Fields marked with (*) are required.
Place