<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mwiki.costasano.club/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mngr</id>
	<title>Costa Sano MediaWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://mwiki.costasano.club/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mngr"/>
	<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/Special:Contributions/Mngr"/>
	<updated>2026-04-17T14:41:03Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.45.1</generator>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MWC/FinalConfig-Chapter&amp;diff=1625</id>
		<title>ICT:MWC/FinalConfig-Chapter</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MWC/FinalConfig-Chapter&amp;diff=1625"/>
		<updated>2026-04-13T09:01:02Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Mngr moved page ICT:MWC - FinalConfig-Chapter to ICT:MWC/FinalConfig-Chapter without leaving a redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Final Configuration for the Chapter Entity =&lt;br /&gt;
&lt;br /&gt;
Document revision: {{#time:Y-m-d|{{REVISIONTIMESTAMP}}}} by {{REVISIONUSER}}&lt;br /&gt;
&lt;br /&gt;
The aim is to create an interface without MediaWiki clutter and well protected to avoid confusing users with unnecessary elements on the page. &lt;br /&gt;
The Cargo template follows what has been decided in the DBML and is fully aligned with the reference entities Place and Organisation.&lt;br /&gt;
&lt;br /&gt;
The Chapter entity is structurally simple:&lt;br /&gt;
- it is recursive (parent chapter)&lt;br /&gt;
- it has no foreign keys to other entities&lt;br /&gt;
&lt;br /&gt;
This entity serves as the reference for the simplest recursive pattern in the system.&lt;br /&gt;
&lt;br /&gt;
This configuration deliberately favors explicit behavior and robustness over visual polish or implicit framework behavior, in order to ensure long-term maintainability.&lt;br /&gt;
&lt;br /&gt;
= Cargo Table: =&lt;br /&gt;
Template:Chapter&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Chapter data template&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#cargo_declare:&lt;br /&gt;
 |_table=Chapters&lt;br /&gt;
 |Code=Page&lt;br /&gt;
 |Label=String&lt;br /&gt;
 |Parent=Page&lt;br /&gt;
 |Sequence=Integer&lt;br /&gt;
 |Description=Text&lt;br /&gt;
 |StartDate=Date&lt;br /&gt;
 |EndDate=Date&lt;br /&gt;
 |Notes=Text&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{#cargo_store:&lt;br /&gt;
 |Code={{FULLPAGENAME}}&lt;br /&gt;
 |Label={{{Label|}}}&lt;br /&gt;
 |Parent={{{Parent|}}}&lt;br /&gt;
 |Sequence={{{Sequence|}}}&lt;br /&gt;
 |Description={{{Description|}}}&lt;br /&gt;
 |StartDate={{{StartDate|}}}&lt;br /&gt;
 |EndDate={{{EndDate|}}}&lt;br /&gt;
 |Notes={{{Notes|}}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== {{{Label|}}} ==&lt;br /&gt;
{{DISPLAYTITLE:{{{Label}}}}}&lt;br /&gt;
&lt;br /&gt;
{{#if:{{{Parent|}}}|&lt;br /&gt;
&#039;&#039;&#039;Parent chapter:&#039;&#039;&#039; [[{{{Parent}}}]]&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039;&lt;br /&gt;
{{{Notes|}}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the template is created, the Cargo table must be created manually.&lt;br /&gt;
If modifications are made to the table structure, the table must be recreated and the temporary table swapped via the Cargo administration interface.&lt;br /&gt;
&lt;br /&gt;
The field Code stores the full page name (including namespace) to ensure unambiguous identification.&lt;br /&gt;
&lt;br /&gt;
Important conventions:&lt;br /&gt;
- Field names start with a capital letter&lt;br /&gt;
- Code is the page name and acts as the technical identifier&lt;br /&gt;
- Label is the human-readable name and is mandatory for all entities&lt;br /&gt;
- Parent is stored as a Page field&lt;br /&gt;
- The field name Label must not be renamed, as it is relied upon by shared JavaScript validation logic&lt;br /&gt;
&lt;br /&gt;
Special lines at the bottom of the template:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
== {{{Label|}}} ==&lt;br /&gt;
{{DISPLAYTITLE:{{{Label}}}}}&lt;br /&gt;
&lt;br /&gt;
{{#if:{{{Parent|}}}|&lt;br /&gt;
&#039;&#039;&#039;Parent chapter:&#039;&#039;&#039; [[{{{Parent}}}]]&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039;&lt;br /&gt;
{{{Notes|}}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
This decoupling allows renaming the label without breaking Cargo relations or dashboards.&lt;br /&gt;
&lt;br /&gt;
= Page Form: =&lt;br /&gt;
Form:Chapter&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Form for creating and editing Chapter pages.&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{{info&lt;br /&gt;
|no summary&lt;br /&gt;
|no preview&lt;br /&gt;
|no minor edit&lt;br /&gt;
|no watch&lt;br /&gt;
|no footer&lt;br /&gt;
}}}&lt;br /&gt;
&#039;&#039;Fields marked with (*) are required.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{{for template|Chapter}}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;formtable&amp;quot;&lt;br /&gt;
&lt;br /&gt;
! Code&lt;br /&gt;
| {{PAGENAME}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Name (*)&lt;br /&gt;
| {{{field|Label}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Parent chapter&lt;br /&gt;
| {{{field|Parent&lt;br /&gt;
 |input type=combobox&lt;br /&gt;
 |values from namespace=Chapter&lt;br /&gt;
 |placeholder=Top level (no parent)&lt;br /&gt;
}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Sequence&lt;br /&gt;
| {{{field|Sequence}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Description&lt;br /&gt;
| {{{field|Description|input type=textarea}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Start date&lt;br /&gt;
| {{{field|StartDate|input type=datepicker|year range=1800:2100|show year dropdown|show month dropdown}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! End date&lt;br /&gt;
| {{{field|EndDate|input type=datepicker|year range=1800:2100|show year dropdown|show month dropdown}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Notes&lt;br /&gt;
| {{{field|Notes|input type=textarea}}}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{{standard input|save}}}&lt;br /&gt;
&lt;br /&gt;
{{{end template}}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first section avoids MediaWiki edit clutter.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{{info&lt;br /&gt;
|no summary&lt;br /&gt;
|no preview&lt;br /&gt;
|no minor edit&lt;br /&gt;
|no watch&lt;br /&gt;
|no footer&lt;br /&gt;
}}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The field Label (displayed to the user as “Name”) is mandatory for all entities.&lt;br /&gt;
This requirement is not implemented using the Page Forms |mandatory option, as this produces unusable white error boxes in the dark Vector-2022 environment.&lt;br /&gt;
&lt;br /&gt;
Instead:&lt;br /&gt;
- the requirement is communicated via text and (*)&lt;br /&gt;
- enforcement is handled via JavaScript at save time&lt;br /&gt;
&lt;br /&gt;
= Dashboard page =&lt;br /&gt;
Dashboard:Chapter&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
= 📘 Costa Sano Research CHAPTER Dashboard =&lt;br /&gt;
&lt;br /&gt;
📖 [[Dashboard:Chapter/Help|Need Help?]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Code !! Name !! Parent&lt;br /&gt;
{{#cargo_query:&lt;br /&gt;
 tables=Chapters&lt;br /&gt;
 |fields=_pageName,_pageTitle,Label,Parent&lt;br /&gt;
 |where=_pageNamespace=3004&lt;br /&gt;
 |order by=_pageTitle&lt;br /&gt;
 |format=template&lt;br /&gt;
 |template=ChapterRow&lt;br /&gt;
 |named args=yes&lt;br /&gt;
 |cache=no&lt;br /&gt;
}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Choose your chapter code corresponding to above table and respecting the naming conventions.&lt;br /&gt;
&lt;br /&gt;
{{#forminput:&lt;br /&gt;
 form=Chapter&lt;br /&gt;
 |namespace=Chapter&lt;br /&gt;
 |button text=➕ New chapter&lt;br /&gt;
 |returnto=Dashboard:Chapter&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:right; font-size:90%;&amp;quot;&amp;gt;&lt;br /&gt;
Last updated: {{CURRENTDAY}} {{CURRENTMONTHNAME}} {{CURRENTYEAR}} – {{CURRENTTIME}} UTC&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
IMPORTANT:&lt;br /&gt;
Dashboard tables are positional.&lt;br /&gt;
The number and order of:&lt;br /&gt;
- table headers&lt;br /&gt;
- Cargo query fields&lt;br /&gt;
- row template cells&lt;br /&gt;
MUST match exactly.&lt;br /&gt;
&lt;br /&gt;
Only the first column (Code) is clickable and leads to the edit form.&lt;br /&gt;
All other columns are displayed as plain text to prevent navigation to record pages.&lt;br /&gt;
&lt;br /&gt;
= Row transclusion Template =&lt;br /&gt;
Template:ChapterRow&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| {{#formlink:&lt;br /&gt;
   form=Chapter&lt;br /&gt;
   |target={{{_pageName}}}&lt;br /&gt;
   |link text={{{_pageTitle}}}&lt;br /&gt;
   |returnto=Dashboard:Chapter&lt;br /&gt;
 }}&lt;br /&gt;
| {{{Label}}}&lt;br /&gt;
| {{#if:{{{Parent|}}}|{{#titleparts:{{{Parent}}}|1}}|—}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Parent values are displayed as plain text (not links) to prevent users from navigating directly to record pages. All record editing is performed via forms accessed from the dashboard.&lt;br /&gt;
&lt;br /&gt;
= Javascript =&lt;br /&gt;
&lt;br /&gt;
The Chapter entity reuses the same shared JavaScript infrastructure as Place and Organisation:&lt;br /&gt;
- dashboard auto-purge&lt;br /&gt;
- save-time mandatory field enforcement (Label)&lt;br /&gt;
- no Page Forms |mandatory usage&lt;br /&gt;
&lt;br /&gt;
No Chapter-specific JavaScript is required.&lt;br /&gt;
&lt;br /&gt;
All Chapter pages are stored in the Chapter namespace with the page name acting as the Code.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{Chapter&lt;br /&gt;
|Label=In the beginning&lt;br /&gt;
|Description=Chapter describing what happened before Berck-sur-Mer...&lt;br /&gt;
|StartDate=1800-01&lt;br /&gt;
|EndDate=1849-12&lt;br /&gt;
|Notes=NIHIL&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Such pages are configuration pages and must be accessed via the dashboard, not directly.&lt;br /&gt;
&lt;br /&gt;
== References: ==&lt;br /&gt;
&lt;br /&gt;
* [[mediawikiwiki:Extension:Cargo/Storing_data|Cargo table explanation]]&lt;br /&gt;
* [[mediawikiwiki:Extension:Page_Forms|Page Forms explained]]&lt;br /&gt;
&lt;br /&gt;
[[Category:FinalConfig]]&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MWC&amp;diff=1624</id>
		<title>ICT:MWC</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MWC&amp;diff=1624"/>
		<updated>2026-04-13T09:00:32Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Section for MediaWiki Cargo application =  This application got stuck on the automatic numbering of the assests.  Her the interesting pages about this application are collected.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Section for MediaWiki Cargo application =&lt;br /&gt;
&lt;br /&gt;
This application got stuck on the automatic numbering of the assests.&lt;br /&gt;
&lt;br /&gt;
Her the interesting pages about this application are collected.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MWC/FinalConfig-Chapter&amp;diff=1623</id>
		<title>ICT:MWC/FinalConfig-Chapter</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MWC/FinalConfig-Chapter&amp;diff=1623"/>
		<updated>2026-04-13T08:43:33Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Mngr moved page ICT:FinalConfig-Chapter to ICT:MWC - FinalConfig-Chapter without leaving a redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Final Configuration for the Chapter Entity =&lt;br /&gt;
&lt;br /&gt;
Document revision: {{#time:Y-m-d|{{REVISIONTIMESTAMP}}}} by {{REVISIONUSER}}&lt;br /&gt;
&lt;br /&gt;
The aim is to create an interface without MediaWiki clutter and well protected to avoid confusing users with unnecessary elements on the page. &lt;br /&gt;
The Cargo template follows what has been decided in the DBML and is fully aligned with the reference entities Place and Organisation.&lt;br /&gt;
&lt;br /&gt;
The Chapter entity is structurally simple:&lt;br /&gt;
- it is recursive (parent chapter)&lt;br /&gt;
- it has no foreign keys to other entities&lt;br /&gt;
&lt;br /&gt;
This entity serves as the reference for the simplest recursive pattern in the system.&lt;br /&gt;
&lt;br /&gt;
This configuration deliberately favors explicit behavior and robustness over visual polish or implicit framework behavior, in order to ensure long-term maintainability.&lt;br /&gt;
&lt;br /&gt;
= Cargo Table: =&lt;br /&gt;
Template:Chapter&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Chapter data template&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#cargo_declare:&lt;br /&gt;
 |_table=Chapters&lt;br /&gt;
 |Code=Page&lt;br /&gt;
 |Label=String&lt;br /&gt;
 |Parent=Page&lt;br /&gt;
 |Sequence=Integer&lt;br /&gt;
 |Description=Text&lt;br /&gt;
 |StartDate=Date&lt;br /&gt;
 |EndDate=Date&lt;br /&gt;
 |Notes=Text&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{#cargo_store:&lt;br /&gt;
 |Code={{FULLPAGENAME}}&lt;br /&gt;
 |Label={{{Label|}}}&lt;br /&gt;
 |Parent={{{Parent|}}}&lt;br /&gt;
 |Sequence={{{Sequence|}}}&lt;br /&gt;
 |Description={{{Description|}}}&lt;br /&gt;
 |StartDate={{{StartDate|}}}&lt;br /&gt;
 |EndDate={{{EndDate|}}}&lt;br /&gt;
 |Notes={{{Notes|}}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== {{{Label|}}} ==&lt;br /&gt;
{{DISPLAYTITLE:{{{Label}}}}}&lt;br /&gt;
&lt;br /&gt;
{{#if:{{{Parent|}}}|&lt;br /&gt;
&#039;&#039;&#039;Parent chapter:&#039;&#039;&#039; [[{{{Parent}}}]]&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039;&lt;br /&gt;
{{{Notes|}}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the template is created, the Cargo table must be created manually.&lt;br /&gt;
If modifications are made to the table structure, the table must be recreated and the temporary table swapped via the Cargo administration interface.&lt;br /&gt;
&lt;br /&gt;
The field Code stores the full page name (including namespace) to ensure unambiguous identification.&lt;br /&gt;
&lt;br /&gt;
Important conventions:&lt;br /&gt;
- Field names start with a capital letter&lt;br /&gt;
- Code is the page name and acts as the technical identifier&lt;br /&gt;
- Label is the human-readable name and is mandatory for all entities&lt;br /&gt;
- Parent is stored as a Page field&lt;br /&gt;
- The field name Label must not be renamed, as it is relied upon by shared JavaScript validation logic&lt;br /&gt;
&lt;br /&gt;
Special lines at the bottom of the template:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
== {{{Label|}}} ==&lt;br /&gt;
{{DISPLAYTITLE:{{{Label}}}}}&lt;br /&gt;
&lt;br /&gt;
{{#if:{{{Parent|}}}|&lt;br /&gt;
&#039;&#039;&#039;Parent chapter:&#039;&#039;&#039; [[{{{Parent}}}]]&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039;&lt;br /&gt;
{{{Notes|}}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
This decoupling allows renaming the label without breaking Cargo relations or dashboards.&lt;br /&gt;
&lt;br /&gt;
= Page Form: =&lt;br /&gt;
Form:Chapter&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Form for creating and editing Chapter pages.&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{{info&lt;br /&gt;
|no summary&lt;br /&gt;
|no preview&lt;br /&gt;
|no minor edit&lt;br /&gt;
|no watch&lt;br /&gt;
|no footer&lt;br /&gt;
}}}&lt;br /&gt;
&#039;&#039;Fields marked with (*) are required.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{{for template|Chapter}}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;formtable&amp;quot;&lt;br /&gt;
&lt;br /&gt;
! Code&lt;br /&gt;
| {{PAGENAME}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Name (*)&lt;br /&gt;
| {{{field|Label}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Parent chapter&lt;br /&gt;
| {{{field|Parent&lt;br /&gt;
 |input type=combobox&lt;br /&gt;
 |values from namespace=Chapter&lt;br /&gt;
 |placeholder=Top level (no parent)&lt;br /&gt;
}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Sequence&lt;br /&gt;
| {{{field|Sequence}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Description&lt;br /&gt;
| {{{field|Description|input type=textarea}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Start date&lt;br /&gt;
| {{{field|StartDate|input type=datepicker|year range=1800:2100|show year dropdown|show month dropdown}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! End date&lt;br /&gt;
| {{{field|EndDate|input type=datepicker|year range=1800:2100|show year dropdown|show month dropdown}}}&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Notes&lt;br /&gt;
| {{{field|Notes|input type=textarea}}}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{{standard input|save}}}&lt;br /&gt;
&lt;br /&gt;
{{{end template}}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first section avoids MediaWiki edit clutter.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{{info&lt;br /&gt;
|no summary&lt;br /&gt;
|no preview&lt;br /&gt;
|no minor edit&lt;br /&gt;
|no watch&lt;br /&gt;
|no footer&lt;br /&gt;
}}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The field Label (displayed to the user as “Name”) is mandatory for all entities.&lt;br /&gt;
This requirement is not implemented using the Page Forms |mandatory option, as this produces unusable white error boxes in the dark Vector-2022 environment.&lt;br /&gt;
&lt;br /&gt;
Instead:&lt;br /&gt;
- the requirement is communicated via text and (*)&lt;br /&gt;
- enforcement is handled via JavaScript at save time&lt;br /&gt;
&lt;br /&gt;
= Dashboard page =&lt;br /&gt;
Dashboard:Chapter&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
= 📘 Costa Sano Research CHAPTER Dashboard =&lt;br /&gt;
&lt;br /&gt;
📖 [[Dashboard:Chapter/Help|Need Help?]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Code !! Name !! Parent&lt;br /&gt;
{{#cargo_query:&lt;br /&gt;
 tables=Chapters&lt;br /&gt;
 |fields=_pageName,_pageTitle,Label,Parent&lt;br /&gt;
 |where=_pageNamespace=3004&lt;br /&gt;
 |order by=_pageTitle&lt;br /&gt;
 |format=template&lt;br /&gt;
 |template=ChapterRow&lt;br /&gt;
 |named args=yes&lt;br /&gt;
 |cache=no&lt;br /&gt;
}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Choose your chapter code corresponding to above table and respecting the naming conventions.&lt;br /&gt;
&lt;br /&gt;
{{#forminput:&lt;br /&gt;
 form=Chapter&lt;br /&gt;
 |namespace=Chapter&lt;br /&gt;
 |button text=➕ New chapter&lt;br /&gt;
 |returnto=Dashboard:Chapter&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:right; font-size:90%;&amp;quot;&amp;gt;&lt;br /&gt;
Last updated: {{CURRENTDAY}} {{CURRENTMONTHNAME}} {{CURRENTYEAR}} – {{CURRENTTIME}} UTC&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
IMPORTANT:&lt;br /&gt;
Dashboard tables are positional.&lt;br /&gt;
The number and order of:&lt;br /&gt;
- table headers&lt;br /&gt;
- Cargo query fields&lt;br /&gt;
- row template cells&lt;br /&gt;
MUST match exactly.&lt;br /&gt;
&lt;br /&gt;
Only the first column (Code) is clickable and leads to the edit form.&lt;br /&gt;
All other columns are displayed as plain text to prevent navigation to record pages.&lt;br /&gt;
&lt;br /&gt;
= Row transclusion Template =&lt;br /&gt;
Template:ChapterRow&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| {{#formlink:&lt;br /&gt;
   form=Chapter&lt;br /&gt;
   |target={{{_pageName}}}&lt;br /&gt;
   |link text={{{_pageTitle}}}&lt;br /&gt;
   |returnto=Dashboard:Chapter&lt;br /&gt;
 }}&lt;br /&gt;
| {{{Label}}}&lt;br /&gt;
| {{#if:{{{Parent|}}}|{{#titleparts:{{{Parent}}}|1}}|—}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Parent values are displayed as plain text (not links) to prevent users from navigating directly to record pages. All record editing is performed via forms accessed from the dashboard.&lt;br /&gt;
&lt;br /&gt;
= Javascript =&lt;br /&gt;
&lt;br /&gt;
The Chapter entity reuses the same shared JavaScript infrastructure as Place and Organisation:&lt;br /&gt;
- dashboard auto-purge&lt;br /&gt;
- save-time mandatory field enforcement (Label)&lt;br /&gt;
- no Page Forms |mandatory usage&lt;br /&gt;
&lt;br /&gt;
No Chapter-specific JavaScript is required.&lt;br /&gt;
&lt;br /&gt;
All Chapter pages are stored in the Chapter namespace with the page name acting as the Code.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{Chapter&lt;br /&gt;
|Label=In the beginning&lt;br /&gt;
|Description=Chapter describing what happened before Berck-sur-Mer...&lt;br /&gt;
|StartDate=1800-01&lt;br /&gt;
|EndDate=1849-12&lt;br /&gt;
|Notes=NIHIL&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Such pages are configuration pages and must be accessed via the dashboard, not directly.&lt;br /&gt;
&lt;br /&gt;
== References: ==&lt;br /&gt;
&lt;br /&gt;
* [[mediawikiwiki:Extension:Cargo/Storing_data|Cargo table explanation]]&lt;br /&gt;
* [[mediawikiwiki:Extension:Page_Forms|Page Forms explained]]&lt;br /&gt;
&lt;br /&gt;
[[Category:FinalConfig]]&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Clean_URLs_for_MW_1.45&amp;diff=1622</id>
		<title>ICT:Clean URLs for MW 1.45</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Clean_URLs_for_MW_1.45&amp;diff=1622"/>
		<updated>2026-04-11T16:30:01Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;== MediaWiki 1.45.2 Clean URL Configuration for kb.costasano.club ==  This document outlines the &amp;quot;Short URL&amp;quot; configuration for the wiki migration to version 1.45.2 on AlmaLinux 10.  === Architecture Overview === * **Domain:** &amp;lt;code&amp;gt;https://costasano.club&amp;lt;/code&amp;gt; * **Frontend (Reverse Proxy):** Nginx on AlmaLinux 10 (Handling SSL/HTTPS). * **Backend (Web Server):** Apache on AlmaLinux 10 (Handling PHP/MediaWiki). * **Document Root:** &amp;lt;code&amp;gt;/var/www/mw145&amp;lt;/code&amp;gt; (index.php...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== MediaWiki 1.45.2 Clean URL Configuration for kb.costasano.club ==&lt;br /&gt;
&lt;br /&gt;
This document outlines the &amp;quot;Short URL&amp;quot; configuration for the wiki migration to version 1.45.2 on AlmaLinux 10.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
* **Domain:** &amp;lt;code&amp;gt;https://costasano.club&amp;lt;/code&amp;gt;&lt;br /&gt;
* **Frontend (Reverse Proxy):** Nginx on AlmaLinux 10 (Handling SSL/HTTPS).&lt;br /&gt;
* **Backend (Web Server):** Apache on AlmaLinux 10 (Handling PHP/MediaWiki).&lt;br /&gt;
* **Document Root:** &amp;lt;code&amp;gt;/var/www/mw145&amp;lt;/code&amp;gt; (index.php is located here).&lt;br /&gt;
* **URL Strategy:** Using the &amp;lt;code&amp;gt;/wiki/&amp;lt;/code&amp;gt; virtual prefix to simplify the Apache configuration and avoid the complex &amp;quot;exclusion lists&amp;quot; used in version 1.43.&lt;br /&gt;
&lt;br /&gt;
=== Step 1: Nginx Proxy Configuration (The Bridge) ===&lt;br /&gt;
The Nginx proxy is configured as a &amp;quot;transparent bridge.&amp;quot; It does &#039;&#039;&#039;not&#039;&#039;&#039; handle rewrites; it simply passes the &amp;lt;code&amp;gt;/wiki/&amp;lt;/code&amp;gt; path through to the Apache backend.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    # SSL Certs (Managed at Nginx layer)&lt;br /&gt;
    ssl_certificate /etc/letsencrypt/live/kb.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/kb.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://[BACKEND_IP_OR_HOSTNAME];&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https; # Tells MediaWiki the user is on HTTPS&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 2: Apache Configuration (The Logic) ===&lt;br /&gt;
Apache handles the translation of the &amp;quot;clean&amp;quot; URL into a format PHP understands.&lt;br /&gt;
&lt;br /&gt;
==== 2.1 Enable Overrides ====&lt;br /&gt;
Ensure &amp;lt;code&amp;gt;/etc/httpd/conf/httpd.conf&amp;lt;/code&amp;gt; (or your specific Vhost file) allows the &amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt; file to function:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;apache&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;Directory &amp;quot;/var/www/mw145&amp;quot;&amp;gt;&lt;br /&gt;
    AllowOverride All&lt;br /&gt;
    Require all granted&lt;br /&gt;
&amp;lt;/Directory&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 2.2 Create .htaccess ====&lt;br /&gt;
Place this file at &amp;lt;code&amp;gt;/var/www/mw145/.htaccess&amp;lt;/code&amp;gt;. It maps the virtual &amp;lt;code&amp;gt;/wiki/&amp;lt;/code&amp;gt; path to the physical &amp;lt;code&amp;gt;index.php&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;apache&amp;quot;&amp;gt;&lt;br /&gt;
RewriteEngine On&lt;br /&gt;
&lt;br /&gt;
# 1. Map the virtual /wiki/ path to index.php&lt;br /&gt;
RewriteRule ^wiki/(.*)$ /index.php?title=$1 [PT,L,QSA]&lt;br /&gt;
&lt;br /&gt;
# 2. Redirect root domain (kb.costasano.club/) to the Main Page&lt;br /&gt;
RewriteRule ^$ /wiki/ [R,L]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3: MediaWiki LocalSettings.php ===&lt;br /&gt;
Update &amp;lt;code&amp;gt;/var/www/mw145/LocalSettings.php&amp;lt;/code&amp;gt; to define the identity of the wiki and trust the Nginx header.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
## URL Settings&lt;br /&gt;
$wgServer = &amp;quot;https://costasano.club&amp;quot;;&lt;br /&gt;
$wgScriptPath = &amp;quot;&amp;quot;;         // Files are directly in /var/www/mw145&lt;br /&gt;
$wgArticlePath = &amp;quot;/wiki/$1&amp;quot;; // Users see this path&lt;br /&gt;
&lt;br /&gt;
## Reverse Proxy / HTTPS Handling&lt;br /&gt;
# Ensures MediaWiki knows it is served over HTTPS even if Apache is HTTP&lt;br /&gt;
if (isset($_SERVER[&#039;HTTP_X_FORWARDED_PROTO&#039;]) &amp;amp;&amp;amp; $_SERVER[&#039;HTTP_X_FORWARDED_PROTO&#039;] === &#039;https&#039;) {&lt;br /&gt;
    $_SERVER[&#039;HTTPS&#039;] = &#039;on&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Trust the internal IP of your Nginx Proxy&lt;br /&gt;
$wgUsePrivateIPs = true;&lt;br /&gt;
$wgCdnServersNoPurge = [ &#039;127.0.0.1&#039;, &#039;[IP_OF_NGINX_PROXY]&#039; ]; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Why we use the &amp;quot;/wiki/&amp;quot; prefix ===&lt;br /&gt;
* &#039;&#039;&#039;Simplicity:&#039;&#039;&#039; Unlike the version 1.43 setup, we no longer need a massive list of exclusions (RewriteCond) for every folder (skins, images, resources, etc.).&lt;br /&gt;
* &#039;&#039;&#039;Stability:&#039;&#039;&#039; It prevents &amp;quot;Path Collisions&amp;quot; where a wiki page name might conflict with a real folder on the disk.&lt;br /&gt;
* &#039;&#039;&#039;Performance:&#039;&#039;&#039; Reduces the processing load on Apache for every request.&lt;br /&gt;
&lt;br /&gt;
[[Category:Server Documentation]]&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=IC:MW_Talk_Namespaces&amp;diff=1621</id>
		<title>IC:MW Talk Namespaces</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=IC:MW_Talk_Namespaces&amp;diff=1621"/>
		<updated>2026-04-06T11:53:32Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Custom Talk Namespaces in MediaWiki = This document explains the purpose of Talk namespaces in MediaWiki, how custom Talk namespaces are defined, and why they only appear in the namespace list after specific configuration steps.  == 1. Purpose of Talk Namespaces == MediaWiki separates *content* from *discussion* using paired namespaces:  * Even-numbered namespace → content (e.g. &amp;lt;code&amp;gt;Network&amp;lt;/code&amp;gt;) * Odd-numbered namespace → talk/discussion (e.g. &amp;lt;code&amp;gt;Network_Ta...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Custom Talk Namespaces in MediaWiki =&lt;br /&gt;
This document explains the purpose of Talk namespaces in MediaWiki, how custom Talk namespaces are defined, and why they only appear in the namespace list after specific configuration steps.&lt;br /&gt;
&lt;br /&gt;
== 1. Purpose of Talk Namespaces ==&lt;br /&gt;
MediaWiki separates *content* from *discussion* using paired namespaces:&lt;br /&gt;
&lt;br /&gt;
* Even-numbered namespace → content (e.g. &amp;lt;code&amp;gt;Network&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Odd-numbered namespace → talk/discussion (e.g. &amp;lt;code&amp;gt;Network_Talk&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Talk namespaces serve several purposes:&lt;br /&gt;
* Keep content pages clean and factual.&lt;br /&gt;
* Provide a dedicated space for questions, decisions, and editorial notes.&lt;br /&gt;
* Preserve decision history for future maintainers.&lt;br /&gt;
* Support structured workflows (templates, categories, archiving).&lt;br /&gt;
* Allow experimentation and discussion without altering the main content.&lt;br /&gt;
&lt;br /&gt;
Every content namespace should have a corresponding Talk namespace for long-term maintainability.&lt;br /&gt;
&lt;br /&gt;
== 2. Why MediaWiki Does Not Auto‑Create Talk Namespaces ==&lt;br /&gt;
MediaWiki automatically creates Talk namespaces only for its built‑in namespaces (Main, File, Help, etc.).&lt;br /&gt;
&lt;br /&gt;
For custom namespaces, MediaWiki does *not* assume a Talk namespace exists.  &lt;br /&gt;
It will only register a Talk namespace when:&lt;br /&gt;
&lt;br /&gt;
# A numeric ID is defined.&lt;br /&gt;
# A name is assigned in &amp;lt;code&amp;gt;$wgExtraNamespaces&amp;lt;/code&amp;gt;.&lt;br /&gt;
# The ID is an odd number paired with the content namespace.&lt;br /&gt;
&lt;br /&gt;
Until these conditions are met, the Talk namespace will not appear in:&lt;br /&gt;
* Special:SpecialPages → “Namespaces”&lt;br /&gt;
* Special:AllPages namespace dropdown&lt;br /&gt;
* API namespace lists&lt;br /&gt;
&lt;br /&gt;
== 3. Defining a Custom Namespace Pair ==&lt;br /&gt;
A correct custom namespace definition looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# --- Custom namespace: Network ---&lt;br /&gt;
define(&amp;quot;NS_NETWORK&amp;quot;, 3000);&lt;br /&gt;
define(&amp;quot;NS_NETWORK_TALK&amp;quot;, 3001);&lt;br /&gt;
&lt;br /&gt;
$wgExtraNamespaces[NS_NETWORK] = &amp;quot;Network&amp;quot;;&lt;br /&gt;
$wgExtraNamespaces[NS_NETWORK_TALK] = &amp;quot;Network_Talk&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# Optional: namespace shortcuts&lt;br /&gt;
$wgNamespaceAliases[&#039;N&#039;] = NS_NETWORK;&lt;br /&gt;
$wgNamespaceAliases[&#039;N_Talk&#039;] = NS_NETWORK_TALK;&lt;br /&gt;
&lt;br /&gt;
# Optional: enable subpages&lt;br /&gt;
$wgNamespaceWithSubpage[NS_NETWORK] = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Required elements ===&lt;br /&gt;
* &amp;lt;code&amp;gt;define(&amp;quot;NS_NETWORK&amp;quot;, 3000);&amp;lt;/code&amp;gt;  &lt;br /&gt;
* &amp;lt;code&amp;gt;define(&amp;quot;NS_NETWORK_TALK&amp;quot;, 3001);&amp;lt;/code&amp;gt;  &lt;br /&gt;
* &amp;lt;code&amp;gt;$wgExtraNamespaces[...]&amp;lt;/code&amp;gt; entries for both&lt;br /&gt;
&lt;br /&gt;
=== Optional but recommended ===&lt;br /&gt;
* namespace aliases  &lt;br /&gt;
* subpage support  &lt;br /&gt;
&lt;br /&gt;
== 4. Why the Talk Namespace Appears Only After Full Definition ==&lt;br /&gt;
MediaWiki registers namespaces early during startup.  &lt;br /&gt;
A Talk namespace will only appear when:&lt;br /&gt;
&lt;br /&gt;
* Both the content and talk IDs are defined.&lt;br /&gt;
* Both names are assigned in &amp;lt;code&amp;gt;$wgExtraNamespaces&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The definitions are placed early enough in &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If the Talk namespace is defined later in the file, or after extensions that query namespaces, it may not appear until the next reload.&lt;br /&gt;
&lt;br /&gt;
=== Rule of thumb ===&lt;br /&gt;
Place all custom namespace definitions **at the top of LocalSettings.php**, before loading extensions.&lt;br /&gt;
&lt;br /&gt;
== 5. Namespace ID Pairing (Even/Odd Rule) ==&lt;br /&gt;
MediaWiki uses numeric pairing to link content and talk namespaces:&lt;br /&gt;
&lt;br /&gt;
* Even ID → content namespace  &lt;br /&gt;
* Odd ID → talk namespace  &lt;br /&gt;
* The talk namespace must immediately follow the content namespace&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
* &amp;lt;code&amp;gt;3000 → Network&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;3001 → Network_Talk&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the pairing is broken, MediaWiki will not recognize the Talk namespace correctly.&lt;br /&gt;
&lt;br /&gt;
== 6. Summary ==&lt;br /&gt;
* Talk namespaces store discussion, decisions, and editorial notes separate from content.&lt;br /&gt;
* MediaWiki does not auto-create Talk namespaces for custom namespaces.&lt;br /&gt;
* A Talk namespace appears only after proper definition (ID + name + pairing).&lt;br /&gt;
* Namespace definitions must be placed early in &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Even/odd numeric pairing is essential for correct behavior.&lt;br /&gt;
&lt;br /&gt;
This structure ensures clean content pages, clear editorial history, and a successor‑friendly wiki architecture.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1620</id>
		<title>ICT:MW Installation procedure 1.43 LTS -v2</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1620"/>
		<updated>2026-04-04T16:11:05Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Installation Procedure for a New MediaWiki Instance (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible workflow for deploying a new MediaWiki instance on the existing 3‑VM infrastructure.&lt;br /&gt;
&lt;br /&gt;
The installation is intentionally split into two phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and testing on VM1 (Apache on port 8080)&lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 (nginx reverse‑proxy + HTTPS)&lt;br /&gt;
&lt;br /&gt;
This staged approach ensures safe testing before the wiki becomes accessible from the internet.&lt;br /&gt;
&lt;br /&gt;
== VM Overview ==&lt;br /&gt;
* &#039;&#039;&#039;VM1 (Web Layer):&#039;&#039;&#039; 192.168.33.231  &lt;br /&gt;
* &#039;&#039;&#039;VM2 (Database Layer):&#039;&#039;&#039; 192.168.33.232  &lt;br /&gt;
* &#039;&#039;&#039;VM3 (Reverse‑Proxy Layer):&#039;&#039;&#039; 192.168.33.233  &lt;br /&gt;
* &#039;&#039;&#039;DB user host address (VM1 → VM2):&#039;&#039;&#039; 10.10.10.1  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 1. Create the Database (VM2 – MariaDB) =&lt;br /&gt;
All commands in this section run on VM2 (192.168.33.232).&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Connect to MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql -u root -p&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Create the database ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki&lt;br /&gt;
  CHARACTER SET utf8mb4&lt;br /&gt;
  COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Create the database user ===&lt;br /&gt;
VM1 connects to MariaDB using its internal DB‑facing address: &#039;&#039;&#039;10.10.10.1&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;10.10.10.1&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Grant privileges ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;10.10.10.1&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.5 Exit ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 2. Prepare the Installation Directory (VM1 – Web Layer) =&lt;br /&gt;
All commands below run on VM1 (192.168.33.231).&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Move into /var/www ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 3. Download MediaWiki =&lt;br /&gt;
Example: MediaWiki 1.43.8 LTS.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 4. Extract MediaWiki =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 5. Move Extracted Files into newMW =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 6. Ownership and Permissions =&lt;br /&gt;
Allows editing via VS Code while keeping Apache functional.&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Directory/file permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.4 Enable group inheritance (setgid) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 7. Apache Configuration on VM1 (Port 8080) =&lt;br /&gt;
&lt;br /&gt;
== 7.1 Enable Apache to listen on port 8080 ==&lt;br /&gt;
Edit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/etc/httpd/conf/httpd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Listen 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.2 Validate and restart ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apachectl configtest&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.3 Verify Apache is listening ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ss -tlnp | grep httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0.0.0.0:80&lt;br /&gt;
0.0.0.0:8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.4 Firewall rule (VM1) ==&lt;br /&gt;
Allow port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
firewall-cmd --add-port=8080/tcp --permanent&lt;br /&gt;
firewall-cmd --reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.5 Local‑only Apache VirtualHost ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newmw.local&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newmw-local-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newmw-local-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 8. Run the Installer Locally =&lt;br /&gt;
Access:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://192.168.33.231:8080&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;http://newmw.local:8080&amp;lt;/code&amp;gt; (if added to /etc/hosts)&lt;br /&gt;
&lt;br /&gt;
== Internal DB Network Logic (10.10.10.x) ==&lt;br /&gt;
The internal network between VM1 and VM2 uses dedicated addresses:&lt;br /&gt;
&lt;br /&gt;
* VM1 (Web Layer): &#039;&#039;&#039;10.10.10.1&#039;&#039;&#039;&lt;br /&gt;
* VM2 (Database Layer): &#039;&#039;&#039;10.10.10.2&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When MediaWiki (running on VM1) connects to MariaDB on VM2, the connection originates from VM1’s internal NIC.  &lt;br /&gt;
Therefore:&lt;br /&gt;
&lt;br /&gt;
* VM1 connects **to** MariaDB at:  &lt;br /&gt;
  &amp;lt;code&amp;gt;10.10.10.2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* VM2 sees VM1 **coming from**:  &lt;br /&gt;
  &amp;lt;code&amp;gt;10.10.10.1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because MariaDB authorizes users based on the client’s source address, database users must be created as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;wikiuser&#039;@&#039;10.10.10.1&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is why all MediaWiki instances on VM1 use the same host restriction.  &lt;br /&gt;
It ensures that only VM1 can authenticate to MariaDB over the internal network.&lt;br /&gt;
&lt;br /&gt;
== Result after installation flow ==&lt;br /&gt;
&lt;br /&gt;
Installer will generate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;http://192.168.33.231:8080&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Place &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 9. Local Testing =&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
* Page creation  &lt;br /&gt;
* Editing  &lt;br /&gt;
* File uploads  &lt;br /&gt;
* User accounts  &lt;br /&gt;
* Extensions  &lt;br /&gt;
* Permissions  &lt;br /&gt;
* Logging  &lt;br /&gt;
&lt;br /&gt;
Only proceed when everything works locally.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 10. Public Exposure via VM3 (Reverse‑Proxy Layer) =&lt;br /&gt;
&lt;br /&gt;
== 10.1 Create minimal HTTP‑only nginx config (VM3) ==&lt;br /&gt;
File:&lt;br /&gt;
&amp;lt;code&amp;gt;/etc/nginx/conf.d/kb.costasano.club.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTP only)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto http;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 11. Issue the Certificate (VM3) =&lt;br /&gt;
Use the nginx plugin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot --nginx -d kb.costasano.club&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certbot will:&lt;br /&gt;
&lt;br /&gt;
* inject temporary ACME config  &lt;br /&gt;
* validate the domain  &lt;br /&gt;
* obtain the certificate  &lt;br /&gt;
* install it  &lt;br /&gt;
* register it for renewal  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 12. Replace with Full HTTPS Reverse‑Proxy (VM3) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTPS)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
# --- HTTP (redirect + ACME) ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- HTTPS ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/kb.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/kb.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 13. Update LocalSettings.php for Public Access =&lt;br /&gt;
Change:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgCanonicalServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 14. Final Verification =&lt;br /&gt;
* &amp;lt;code&amp;gt;curl -I https://kb.costasano.club&amp;lt;/code&amp;gt; returns 200 or 301  &lt;br /&gt;
* Browser loads the wiki  &lt;br /&gt;
* Editing works  &lt;br /&gt;
* Uploads work  &lt;br /&gt;
* No rewrite errors  &lt;br /&gt;
* nginx proxies correctly  &lt;br /&gt;
* MariaDB connections succeed  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 15. Login Issues After Changing LocalSettings.php =&lt;br /&gt;
MediaWiki is very sensitive to stale cookies and cached sessions, especially&lt;br /&gt;
after changing settings such as:&lt;br /&gt;
&lt;br /&gt;
* $wgServer&lt;br /&gt;
* $wgCookieSecure&lt;br /&gt;
* $wgCookieSameSite&lt;br /&gt;
* $wgUseReverseProxy&lt;br /&gt;
* $wgUseCdn&lt;br /&gt;
&lt;br /&gt;
If login suddenly fails or a Captcha appears even though no Captcha extension&lt;br /&gt;
is enabled, the cause is usually old browser cookies.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fix:&#039;&#039;&#039; open the wiki in a private/incognito window or clear cookies for the&lt;br /&gt;
domain. This forces MediaWiki to create a fresh session and resolves the issue.&lt;br /&gt;
&lt;br /&gt;
=== Differences Between MediaWiki 1.43 and 1.45 (Login, Captcha, Reverse‑Proxy) ===&lt;br /&gt;
MediaWiki 1.43 and 1.45 behave differently when installed behind a reverse‑proxy&lt;br /&gt;
such as nginx. This affects login, session cookies, and the appearance of the&lt;br /&gt;
“phantom Captcha” (a throttle challenge that cannot be solved).&lt;br /&gt;
&lt;br /&gt;
==== MediaWiki 1.45 (more tolerant) ====&lt;br /&gt;
MediaWiki 1.45 includes improved session and cookie handling. It automatically:&lt;br /&gt;
&lt;br /&gt;
* detects HTTPS behind a reverse‑proxy&lt;br /&gt;
* regenerates session cookies when needed&lt;br /&gt;
* accepts SameSite=None cookies more reliably&lt;br /&gt;
* avoids triggering login throttling on fresh installs&lt;br /&gt;
&lt;br /&gt;
As a result, MediaWiki 1.45 usually works immediately behind nginx without&lt;br /&gt;
showing any Captcha or blocking logins.&lt;br /&gt;
&lt;br /&gt;
==== MediaWiki 1.43 (stricter behaviour) ====&lt;br /&gt;
MediaWiki 1.43 uses older, stricter session logic. If any of the following are&lt;br /&gt;
misconfigured:&lt;br /&gt;
&lt;br /&gt;
* $wgServer&lt;br /&gt;
* $wgCookieSecure&lt;br /&gt;
* $wgCookieSameSite&lt;br /&gt;
* $wgUseReverseProxy&lt;br /&gt;
* $wgUseCdn&lt;br /&gt;
* missing $wgCdnServers entry&lt;br /&gt;
&lt;br /&gt;
…MediaWiki may fail to create a valid login session. When this happens, it&lt;br /&gt;
displays a Captcha-like box even though no Captcha extension is enabled. This is&lt;br /&gt;
not a real Captcha but the built‑in login throttle, and it always fails.&lt;br /&gt;
&lt;br /&gt;
==== Why this did not happen on the previous install ====&lt;br /&gt;
The earlier wiki was installed on MediaWiki 1.45, which is more forgiving of&lt;br /&gt;
reverse‑proxy setups. MediaWiki 1.43 requires explicit configuration to trust&lt;br /&gt;
the proxy and accept secure cookies.&lt;br /&gt;
&lt;br /&gt;
==== Fix ====&lt;br /&gt;
Add the following lines to LocalSettings.php:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgUseCdn = true;&lt;br /&gt;
$wgUseReverseProxy = true;&lt;br /&gt;
$wgCdnServers = [ &amp;quot;192.168.33.233&amp;quot; ];  # nginx reverse-proxy&lt;br /&gt;
$wgCookieSecure = true;&lt;br /&gt;
$wgCookieSameSite = &amp;quot;None&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If login still fails, clear browser cookies or open the wiki in a private window&lt;br /&gt;
to force a fresh session.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= 16. Summary =&lt;br /&gt;
This procedure installs MediaWiki in two safe phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and validation on VM1  &lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 with HTTPS  &lt;br /&gt;
&lt;br /&gt;
This ensures a clean, reproducible, low‑risk deployment workflow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Apache fails to start after adding a new Listen port (SELinux) ==&lt;br /&gt;
&lt;br /&gt;
When adding a new Apache &amp;lt;code&amp;gt;Listen&amp;lt;/code&amp;gt; directive on AlmaLinux (e.g. &amp;lt;code&amp;gt;Listen 8081&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Listen 8888&amp;lt;/code&amp;gt;), &lt;br /&gt;
&amp;lt;code&amp;gt;httpd&amp;lt;/code&amp;gt; may refuse to start even though &amp;lt;code&amp;gt;apachectl configtest&amp;lt;/code&amp;gt; reports &amp;lt;code&amp;gt;Syntax OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Typical symptoms:&lt;br /&gt;
* &amp;lt;code&amp;gt;httpd&amp;lt;/code&amp;gt; does not restart after adding a new port&lt;br /&gt;
* &amp;lt;code&amp;gt;configtest&amp;lt;/code&amp;gt; shows no errors&lt;br /&gt;
* &amp;lt;code&amp;gt;journalctl -xe&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/var/log/audit/audit.log&amp;lt;/code&amp;gt; contains an AVC denial similar to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
type=AVC msg=audit(...): avc:  denied  { name_bind } for  pid=XXXX comm=&amp;quot;httpd&amp;quot; \&lt;br /&gt;
scontext=system_u:system_r:httpd_t:s0 \&lt;br /&gt;
tcontext=system_u:object_r:unreserved_port_t:s0 \&lt;br /&gt;
tclass=tcp_socket permissive=0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Cause ===&lt;br /&gt;
SELinux only allows Apache (&amp;lt;code&amp;gt;httpd_t&amp;lt;/code&amp;gt;) to bind to ports labeled as &amp;lt;code&amp;gt;http_port_t&amp;lt;/code&amp;gt;.  &lt;br /&gt;
By default, this includes ports such as 80, 443, 8008, 8009, 8443, and sometimes 8080.&lt;br /&gt;
&lt;br /&gt;
Any new port (e.g. 8081, 8888) is labeled as &amp;lt;code&amp;gt;unreserved_port_t&amp;lt;/code&amp;gt; and is therefore blocked by SELinux.  &lt;br /&gt;
Apache reports this as a binding failure, often misleadingly shown as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
No such device or address&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Check which ports Apache is allowed to use ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
semanage port -l | grep http_port_t&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fix: allow Apache to bind to the new port ===&lt;br /&gt;
Add the port to the SELinux &amp;lt;code&amp;gt;http_port_t&amp;lt;/code&amp;gt; type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo semanage port -a -t http_port_t -p tcp 8081&lt;br /&gt;
sudo semanage port -a -t http_port_t -p tcp 8888&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the port already exists under a different SELinux type, modify it instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo semanage port -m -t http_port_t -p tcp 8081&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Restart Apache ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apache will now start normally and bind to the newly added port.&lt;br /&gt;
&lt;br /&gt;
=== Notes for maintainers ===&lt;br /&gt;
* This behaviour is normal on RHEL/AlmaLinux systems with SELinux in enforcing mode.&lt;br /&gt;
* Always document newly opened ports in the infrastructure notes to avoid confusion for successors.&lt;br /&gt;
* Avoid disabling SELinux; adjusting port labels is the correct and safe approach.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_upgrade_1.43.8_to_1.46_LTS&amp;diff=1619</id>
		<title>ICT:MW upgrade 1.43.8 to 1.46 LTS</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_upgrade_1.43.8_to_1.46_LTS&amp;diff=1619"/>
		<updated>2026-04-04T13:59:08Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= MediaWiki Upgrade Procedure — 1.43.8 → 1.46 LTS = &amp;#039;&amp;#039;&amp;#039;Document version:&amp;#039;&amp;#039;&amp;#039; 2025‑04‑04   &amp;#039;&amp;#039;&amp;#039;Author:&amp;#039;&amp;#039;&amp;#039; Martin   &amp;#039;&amp;#039;&amp;#039;Purpose:&amp;#039;&amp;#039;&amp;#039; Clean, reproducible, successor‑friendly upgrade to MediaWiki 1.46 LTS using a parallel installation in &amp;lt;code&amp;gt;/var/www/mw146&amp;lt;/code&amp;gt; and a reverse‑proxy switch.  This procedure ensures: * Zero downtime   * Instant rollback   * No leftover files from old versions   * Clean, maintainable, successor‑friendly architecture    The upgrade i...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= MediaWiki Upgrade Procedure — 1.43.8 → 1.46 LTS =&lt;br /&gt;
&#039;&#039;&#039;Document version:&#039;&#039;&#039; 2025‑04‑04  &lt;br /&gt;
&#039;&#039;&#039;Author:&#039;&#039;&#039; Martin  &lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Clean, reproducible, successor‑friendly upgrade to MediaWiki 1.46 LTS using a parallel installation in &amp;lt;code&amp;gt;/var/www/mw146&amp;lt;/code&amp;gt; and a reverse‑proxy switch.&lt;br /&gt;
&lt;br /&gt;
This procedure ensures:&lt;br /&gt;
* Zero downtime  &lt;br /&gt;
* Instant rollback  &lt;br /&gt;
* No leftover files from old versions  &lt;br /&gt;
* Clean, maintainable, successor‑friendly architecture  &lt;br /&gt;
&lt;br /&gt;
The upgrade is executed on the &#039;&#039;&#039;web VM&#039;&#039;&#039; with a final switch on the &#039;&#039;&#039;reverse‑proxy VM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Preconditions ==&lt;br /&gt;
* Working MediaWiki &#039;&#039;&#039;1.43.8 LTS&#039;&#039;&#039; installation  &lt;br /&gt;
* Database VM reachable and healthy  &lt;br /&gt;
* PHP &#039;&#039;&#039;8.1 or 8.2&#039;&#039;&#039;  &lt;br /&gt;
* Reverse proxy currently pointing to &amp;lt;code&amp;gt;/var/www/mw143&amp;lt;/code&amp;gt;  &lt;br /&gt;
* VM snapshot taken before starting  &lt;br /&gt;
* SSH access to web VM and reverse‑proxy VM  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 1: Prepare new directory ==&lt;br /&gt;
On the &#039;&#039;&#039;web VM&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
mkdir mw146&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Download and extract MediaWiki 1.46 LTS:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.46/mediawiki-1.46.0.tar.gz&lt;br /&gt;
tar -xzf mediawiki-1.46.0.tar.gz --strip-components=1 -C mw146&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resulting structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/mw146&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 2: Copy required files from old installation ==&lt;br /&gt;
From the existing 1.43 installation (example path &amp;lt;code&amp;gt;/var/www/mw143&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
=== Copy LocalSettings.php ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /var/www/mw143/LocalSettings.php /var/www/mw146/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Copy images directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp -R /var/www/mw143/images /var/www/mw146/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Copy custom logos (if stored outside images) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /var/www/mw143/resources/assets/*logo* /var/www/mw146/resources/assets/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do &#039;&#039;&#039;not&#039;&#039;&#039; copy extensions yet.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 3: Install or update extensions ==&lt;br /&gt;
MediaWiki 1.46 requires updated extensions.&lt;br /&gt;
&lt;br /&gt;
For each extension used in 1.43:&lt;br /&gt;
&lt;br /&gt;
# Download the &#039;&#039;&#039;1.46‑compatible&#039;&#039;&#039; version  &lt;br /&gt;
# Place it in &amp;lt;code&amp;gt;/var/www/mw146/extensions/&amp;lt;/code&amp;gt;  &lt;br /&gt;
# Keep the same configuration lines in &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
Vector‑2022 is bundled in 1.46 → no action needed.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 4: Run the database upgrade ==&lt;br /&gt;
On the web VM:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www/mw146&lt;br /&gt;
php maintenance/update.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This upgrades the schema &#039;&#039;&#039;in place&#039;&#039;&#039;.  &lt;br /&gt;
No data is lost.  &lt;br /&gt;
No new database is created.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 5: Local testing before switching reverse proxy ==&lt;br /&gt;
Access the new installation directly via the web VM’s IP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://&amp;amp;lt;web-vm-ip&amp;amp;gt;/mw146/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
* Login  &lt;br /&gt;
* Namespaces and subpages  &lt;br /&gt;
* Uploads  &lt;br /&gt;
* SMTP  &lt;br /&gt;
* Dark mode  &lt;br /&gt;
* Vector‑2022 behaviour  &lt;br /&gt;
* Special pages (filter box present)  &lt;br /&gt;
* Search  &lt;br /&gt;
* Logo  &lt;br /&gt;
&lt;br /&gt;
Proceed only when everything is correct.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 6: Switch reverse proxy ==&lt;br /&gt;
On the &#039;&#039;&#039;reverse‑proxy VM&#039;&#039;&#039;, update the root path.&lt;br /&gt;
&lt;br /&gt;
Example (Nginx):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
root /var/www/mw146;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The wiki now runs on &#039;&#039;&#039;1.46 LTS&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Rollback is instant: point back to &amp;lt;code&amp;gt;/var/www/mw143&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 7: Post‑upgrade checks ==&lt;br /&gt;
* Check error logs on web VM  &lt;br /&gt;
* Check reverse‑proxy logs  &lt;br /&gt;
* Verify job queue  &lt;br /&gt;
* Verify uploads  &lt;br /&gt;
* Verify Special pages  &lt;br /&gt;
* Verify dark mode  &lt;br /&gt;
* Verify email notifications  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 8: Cleanup ==&lt;br /&gt;
After several days of stable operation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rm -rf /var/www/mw143&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or archive it for long‑term reference.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Rollback Procedure ==&lt;br /&gt;
If any issue occurs after switching:&lt;br /&gt;
&lt;br /&gt;
# Edit reverse‑proxy config  &lt;br /&gt;
# Point root back to &amp;lt;code&amp;gt;/var/www/mw143&amp;lt;/code&amp;gt;  &lt;br /&gt;
# Reload proxy  &lt;br /&gt;
# Wiki is instantly restored  &lt;br /&gt;
&lt;br /&gt;
Database remains compatible.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
* MediaWiki 1.46 includes native dark mode  &lt;br /&gt;
* Vector‑2022 settings may be simplified  &lt;br /&gt;
* The Special pages filter box is available in 1.46  &lt;br /&gt;
* No database recreation is required for upgrades&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1618</id>
		<title>ICT:MW Installation procedure 1.43 LTS -v2</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1618"/>
		<updated>2026-04-03T16:56:35Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Installation Procedure for a New MediaWiki Instance (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible workflow for deploying a new MediaWiki instance on the existing 3‑VM infrastructure.&lt;br /&gt;
&lt;br /&gt;
The installation is intentionally split into two phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and testing on VM1 (Apache on port 8080)&lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 (nginx reverse‑proxy + HTTPS)&lt;br /&gt;
&lt;br /&gt;
This staged approach ensures safe testing before the wiki becomes accessible from the internet.&lt;br /&gt;
&lt;br /&gt;
== VM Overview ==&lt;br /&gt;
* &#039;&#039;&#039;VM1 (Web Layer):&#039;&#039;&#039; 192.168.33.231  &lt;br /&gt;
* &#039;&#039;&#039;VM2 (Database Layer):&#039;&#039;&#039; 192.168.33.232  &lt;br /&gt;
* &#039;&#039;&#039;VM3 (Reverse‑Proxy Layer):&#039;&#039;&#039; 192.168.33.233  &lt;br /&gt;
* &#039;&#039;&#039;DB user host address (VM1 → VM2):&#039;&#039;&#039; 10.10.10.1  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 1. Create the Database (VM2 – MariaDB) =&lt;br /&gt;
All commands in this section run on VM2 (192.168.33.232).&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Connect to MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql -u root -p&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Create the database ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki&lt;br /&gt;
  CHARACTER SET utf8mb4&lt;br /&gt;
  COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Create the database user ===&lt;br /&gt;
VM1 connects to MariaDB using its internal DB‑facing address: &#039;&#039;&#039;10.10.10.1&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;10.10.10.1&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Grant privileges ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;10.10.10.1&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.5 Exit ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 2. Prepare the Installation Directory (VM1 – Web Layer) =&lt;br /&gt;
All commands below run on VM1 (192.168.33.231).&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Move into /var/www ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 3. Download MediaWiki =&lt;br /&gt;
Example: MediaWiki 1.43.8 LTS.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 4. Extract MediaWiki =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 5. Move Extracted Files into newMW =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 6. Ownership and Permissions =&lt;br /&gt;
Allows editing via VS Code while keeping Apache functional.&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Directory/file permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.4 Enable group inheritance (setgid) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 7. Apache Configuration on VM1 (Port 8080) =&lt;br /&gt;
&lt;br /&gt;
== 7.1 Enable Apache to listen on port 8080 ==&lt;br /&gt;
Edit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/etc/httpd/conf/httpd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Listen 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.2 Validate and restart ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apachectl configtest&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.3 Verify Apache is listening ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ss -tlnp | grep httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0.0.0.0:80&lt;br /&gt;
0.0.0.0:8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.4 Firewall rule (VM1) ==&lt;br /&gt;
Allow port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
firewall-cmd --add-port=8080/tcp --permanent&lt;br /&gt;
firewall-cmd --reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.5 Local‑only Apache VirtualHost ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newmw.local&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newmw-local-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newmw-local-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 8. Run the Installer Locally =&lt;br /&gt;
Access:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://192.168.33.231:8080&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;http://newmw.local:8080&amp;lt;/code&amp;gt; (if added to /etc/hosts)&lt;br /&gt;
&lt;br /&gt;
== Internal DB Network Logic (10.10.10.x) ==&lt;br /&gt;
The internal network between VM1 and VM2 uses dedicated addresses:&lt;br /&gt;
&lt;br /&gt;
* VM1 (Web Layer): &#039;&#039;&#039;10.10.10.1&#039;&#039;&#039;&lt;br /&gt;
* VM2 (Database Layer): &#039;&#039;&#039;10.10.10.2&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When MediaWiki (running on VM1) connects to MariaDB on VM2, the connection originates from VM1’s internal NIC.  &lt;br /&gt;
Therefore:&lt;br /&gt;
&lt;br /&gt;
* VM1 connects **to** MariaDB at:  &lt;br /&gt;
  &amp;lt;code&amp;gt;10.10.10.2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* VM2 sees VM1 **coming from**:  &lt;br /&gt;
  &amp;lt;code&amp;gt;10.10.10.1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because MariaDB authorizes users based on the client’s source address, database users must be created as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;wikiuser&#039;@&#039;10.10.10.1&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is why all MediaWiki instances on VM1 use the same host restriction.  &lt;br /&gt;
It ensures that only VM1 can authenticate to MariaDB over the internal network.&lt;br /&gt;
&lt;br /&gt;
== Result after installation flow ==&lt;br /&gt;
&lt;br /&gt;
Installer will generate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;http://192.168.33.231:8080&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Place &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 9. Local Testing =&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
* Page creation  &lt;br /&gt;
* Editing  &lt;br /&gt;
* File uploads  &lt;br /&gt;
* User accounts  &lt;br /&gt;
* Extensions  &lt;br /&gt;
* Permissions  &lt;br /&gt;
* Logging  &lt;br /&gt;
&lt;br /&gt;
Only proceed when everything works locally.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 10. Public Exposure via VM3 (Reverse‑Proxy Layer) =&lt;br /&gt;
&lt;br /&gt;
== 10.1 Create minimal HTTP‑only nginx config (VM3) ==&lt;br /&gt;
File:&lt;br /&gt;
&amp;lt;code&amp;gt;/etc/nginx/conf.d/kb.costasano.club.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTP only)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto http;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 11. Issue the Certificate (VM3) =&lt;br /&gt;
Use the nginx plugin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot --nginx -d kb.costasano.club&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certbot will:&lt;br /&gt;
&lt;br /&gt;
* inject temporary ACME config  &lt;br /&gt;
* validate the domain  &lt;br /&gt;
* obtain the certificate  &lt;br /&gt;
* install it  &lt;br /&gt;
* register it for renewal  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 12. Replace with Full HTTPS Reverse‑Proxy (VM3) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTPS)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
# --- HTTP (redirect + ACME) ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- HTTPS ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/kb.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/kb.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 13. Update LocalSettings.php for Public Access =&lt;br /&gt;
Change:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgCanonicalServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 14. Final Verification =&lt;br /&gt;
* &amp;lt;code&amp;gt;curl -I https://kb.costasano.club&amp;lt;/code&amp;gt; returns 200 or 301  &lt;br /&gt;
* Browser loads the wiki  &lt;br /&gt;
* Editing works  &lt;br /&gt;
* Uploads work  &lt;br /&gt;
* No rewrite errors  &lt;br /&gt;
* nginx proxies correctly  &lt;br /&gt;
* MariaDB connections succeed  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 15. Login Issues After Changing LocalSettings.php =&lt;br /&gt;
MediaWiki is very sensitive to stale cookies and cached sessions, especially&lt;br /&gt;
after changing settings such as:&lt;br /&gt;
&lt;br /&gt;
* $wgServer&lt;br /&gt;
* $wgCookieSecure&lt;br /&gt;
* $wgCookieSameSite&lt;br /&gt;
* $wgUseReverseProxy&lt;br /&gt;
* $wgUseCdn&lt;br /&gt;
&lt;br /&gt;
If login suddenly fails or a Captcha appears even though no Captcha extension&lt;br /&gt;
is enabled, the cause is usually old browser cookies.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fix:&#039;&#039;&#039; open the wiki in a private/incognito window or clear cookies for the&lt;br /&gt;
domain. This forces MediaWiki to create a fresh session and resolves the issue.&lt;br /&gt;
&lt;br /&gt;
=== Differences Between MediaWiki 1.43 and 1.45 (Login, Captcha, Reverse‑Proxy) ===&lt;br /&gt;
MediaWiki 1.43 and 1.45 behave differently when installed behind a reverse‑proxy&lt;br /&gt;
such as nginx. This affects login, session cookies, and the appearance of the&lt;br /&gt;
“phantom Captcha” (a throttle challenge that cannot be solved).&lt;br /&gt;
&lt;br /&gt;
==== MediaWiki 1.45 (more tolerant) ====&lt;br /&gt;
MediaWiki 1.45 includes improved session and cookie handling. It automatically:&lt;br /&gt;
&lt;br /&gt;
* detects HTTPS behind a reverse‑proxy&lt;br /&gt;
* regenerates session cookies when needed&lt;br /&gt;
* accepts SameSite=None cookies more reliably&lt;br /&gt;
* avoids triggering login throttling on fresh installs&lt;br /&gt;
&lt;br /&gt;
As a result, MediaWiki 1.45 usually works immediately behind nginx without&lt;br /&gt;
showing any Captcha or blocking logins.&lt;br /&gt;
&lt;br /&gt;
==== MediaWiki 1.43 (stricter behaviour) ====&lt;br /&gt;
MediaWiki 1.43 uses older, stricter session logic. If any of the following are&lt;br /&gt;
misconfigured:&lt;br /&gt;
&lt;br /&gt;
* $wgServer&lt;br /&gt;
* $wgCookieSecure&lt;br /&gt;
* $wgCookieSameSite&lt;br /&gt;
* $wgUseReverseProxy&lt;br /&gt;
* $wgUseCdn&lt;br /&gt;
* missing $wgCdnServers entry&lt;br /&gt;
&lt;br /&gt;
…MediaWiki may fail to create a valid login session. When this happens, it&lt;br /&gt;
displays a Captcha-like box even though no Captcha extension is enabled. This is&lt;br /&gt;
not a real Captcha but the built‑in login throttle, and it always fails.&lt;br /&gt;
&lt;br /&gt;
==== Why this did not happen on the previous install ====&lt;br /&gt;
The earlier wiki was installed on MediaWiki 1.45, which is more forgiving of&lt;br /&gt;
reverse‑proxy setups. MediaWiki 1.43 requires explicit configuration to trust&lt;br /&gt;
the proxy and accept secure cookies.&lt;br /&gt;
&lt;br /&gt;
==== Fix ====&lt;br /&gt;
Add the following lines to LocalSettings.php:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgUseCdn = true;&lt;br /&gt;
$wgUseReverseProxy = true;&lt;br /&gt;
$wgCdnServers = [ &amp;quot;192.168.33.233&amp;quot; ];  # nginx reverse-proxy&lt;br /&gt;
$wgCookieSecure = true;&lt;br /&gt;
$wgCookieSameSite = &amp;quot;None&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If login still fails, clear browser cookies or open the wiki in a private window&lt;br /&gt;
to force a fresh session.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= 16. Summary =&lt;br /&gt;
This procedure installs MediaWiki in two safe phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and validation on VM1  &lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 with HTTPS  &lt;br /&gt;
&lt;br /&gt;
This ensures a clean, reproducible, low‑risk deployment workflow.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1617</id>
		<title>ICT:MW Installation procedure 1.43 LTS -v2</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1617"/>
		<updated>2026-04-03T16:44:41Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Installation Procedure for a New MediaWiki Instance (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible workflow for deploying a new MediaWiki instance on the existing 3‑VM infrastructure.&lt;br /&gt;
&lt;br /&gt;
The installation is intentionally split into two phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and testing on VM1 (Apache on port 8080)&lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 (nginx reverse‑proxy + HTTPS)&lt;br /&gt;
&lt;br /&gt;
This staged approach ensures safe testing before the wiki becomes accessible from the internet.&lt;br /&gt;
&lt;br /&gt;
== VM Overview ==&lt;br /&gt;
* &#039;&#039;&#039;VM1 (Web Layer):&#039;&#039;&#039; 192.168.33.231  &lt;br /&gt;
* &#039;&#039;&#039;VM2 (Database Layer):&#039;&#039;&#039; 192.168.33.232  &lt;br /&gt;
* &#039;&#039;&#039;VM3 (Reverse‑Proxy Layer):&#039;&#039;&#039; 192.168.33.233  &lt;br /&gt;
* &#039;&#039;&#039;DB user host address (VM1 → VM2):&#039;&#039;&#039; 10.10.10.1  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 1. Create the Database (VM2 – MariaDB) =&lt;br /&gt;
All commands in this section run on VM2 (192.168.33.232).&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Connect to MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql -u root -p&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Create the database ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki&lt;br /&gt;
  CHARACTER SET utf8mb4&lt;br /&gt;
  COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Create the database user ===&lt;br /&gt;
VM1 connects to MariaDB using its internal DB‑facing address: &#039;&#039;&#039;10.10.10.1&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;10.10.10.1&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Grant privileges ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;10.10.10.1&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.5 Exit ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 2. Prepare the Installation Directory (VM1 – Web Layer) =&lt;br /&gt;
All commands below run on VM1 (192.168.33.231).&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Move into /var/www ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 3. Download MediaWiki =&lt;br /&gt;
Example: MediaWiki 1.43.8 LTS.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 4. Extract MediaWiki =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 5. Move Extracted Files into newMW =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 6. Ownership and Permissions =&lt;br /&gt;
Allows editing via VS Code while keeping Apache functional.&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Directory/file permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.4 Enable group inheritance (setgid) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 7. Apache Configuration on VM1 (Port 8080) =&lt;br /&gt;
&lt;br /&gt;
== 7.1 Enable Apache to listen on port 8080 ==&lt;br /&gt;
Edit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/etc/httpd/conf/httpd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Listen 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.2 Validate and restart ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apachectl configtest&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.3 Verify Apache is listening ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ss -tlnp | grep httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0.0.0.0:80&lt;br /&gt;
0.0.0.0:8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.4 Firewall rule (VM1) ==&lt;br /&gt;
Allow port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
firewall-cmd --add-port=8080/tcp --permanent&lt;br /&gt;
firewall-cmd --reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.5 Local‑only Apache VirtualHost ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newmw.local&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newmw-local-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newmw-local-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 8. Run the Installer Locally =&lt;br /&gt;
Access:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://192.168.33.231:8080&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;http://newmw.local:8080&amp;lt;/code&amp;gt; (if added to /etc/hosts)&lt;br /&gt;
&lt;br /&gt;
== Internal DB Network Logic (10.10.10.x) ==&lt;br /&gt;
The internal network between VM1 and VM2 uses dedicated addresses:&lt;br /&gt;
&lt;br /&gt;
* VM1 (Web Layer): &#039;&#039;&#039;10.10.10.1&#039;&#039;&#039;&lt;br /&gt;
* VM2 (Database Layer): &#039;&#039;&#039;10.10.10.2&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When MediaWiki (running on VM1) connects to MariaDB on VM2, the connection originates from VM1’s internal NIC.  &lt;br /&gt;
Therefore:&lt;br /&gt;
&lt;br /&gt;
* VM1 connects **to** MariaDB at:  &lt;br /&gt;
  &amp;lt;code&amp;gt;10.10.10.2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* VM2 sees VM1 **coming from**:  &lt;br /&gt;
  &amp;lt;code&amp;gt;10.10.10.1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because MariaDB authorizes users based on the client’s source address, database users must be created as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;wikiuser&#039;@&#039;10.10.10.1&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is why all MediaWiki instances on VM1 use the same host restriction.  &lt;br /&gt;
It ensures that only VM1 can authenticate to MariaDB over the internal network.&lt;br /&gt;
&lt;br /&gt;
== Result after installation flow ==&lt;br /&gt;
&lt;br /&gt;
Installer will generate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;http://192.168.33.231:8080&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Place &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 9. Local Testing =&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
* Page creation  &lt;br /&gt;
* Editing  &lt;br /&gt;
* File uploads  &lt;br /&gt;
* User accounts  &lt;br /&gt;
* Extensions  &lt;br /&gt;
* Permissions  &lt;br /&gt;
* Logging  &lt;br /&gt;
&lt;br /&gt;
Only proceed when everything works locally.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 10. Public Exposure via VM3 (Reverse‑Proxy Layer) =&lt;br /&gt;
&lt;br /&gt;
== 10.1 Create minimal HTTP‑only nginx config (VM3) ==&lt;br /&gt;
File:&lt;br /&gt;
&amp;lt;code&amp;gt;/etc/nginx/conf.d/kb.costasano.club.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTP only)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto http;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 11. Issue the Certificate (VM3) =&lt;br /&gt;
Use the nginx plugin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot --nginx -d kb.costasano.club&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certbot will:&lt;br /&gt;
&lt;br /&gt;
* inject temporary ACME config  &lt;br /&gt;
* validate the domain  &lt;br /&gt;
* obtain the certificate  &lt;br /&gt;
* install it  &lt;br /&gt;
* register it for renewal  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 12. Replace with Full HTTPS Reverse‑Proxy (VM3) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTPS)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
# --- HTTP (redirect + ACME) ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- HTTPS ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/kb.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/kb.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 13. Update LocalSettings.php for Public Access =&lt;br /&gt;
Change:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgCanonicalServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 14. Final Verification =&lt;br /&gt;
* &amp;lt;code&amp;gt;curl -I https://kb.costasano.club&amp;lt;/code&amp;gt; returns 200 or 301  &lt;br /&gt;
* Browser loads the wiki  &lt;br /&gt;
* Editing works  &lt;br /&gt;
* Uploads work  &lt;br /&gt;
* No rewrite errors  &lt;br /&gt;
* nginx proxies correctly  &lt;br /&gt;
* MariaDB connections succeed  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 15. Login Issues After Changing LocalSettings.php =&lt;br /&gt;
MediaWiki is very sensitive to stale cookies and cached sessions, especially&lt;br /&gt;
after changing settings such as:&lt;br /&gt;
&lt;br /&gt;
* $wgServer&lt;br /&gt;
* $wgCookieSecure&lt;br /&gt;
* $wgCookieSameSite&lt;br /&gt;
* $wgUseReverseProxy&lt;br /&gt;
* $wgUseCdn&lt;br /&gt;
&lt;br /&gt;
If login suddenly fails or a Captcha appears even though no Captcha extension&lt;br /&gt;
is enabled, the cause is usually old browser cookies.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fix:&#039;&#039;&#039; open the wiki in a private/incognito window or clear cookies for the&lt;br /&gt;
domain. This forces MediaWiki to create a fresh session and resolves the issue.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= 16. Summary =&lt;br /&gt;
This procedure installs MediaWiki in two safe phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and validation on VM1  &lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 with HTTPS  &lt;br /&gt;
&lt;br /&gt;
This ensures a clean, reproducible, low‑risk deployment workflow.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1616</id>
		<title>ICT:MW Installation procedure 1.43 LTS -v2</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1616"/>
		<updated>2026-04-03T16:13:08Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Installation Procedure for a New MediaWiki Instance (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible workflow for deploying a new MediaWiki instance on the existing 3‑VM infrastructure.&lt;br /&gt;
&lt;br /&gt;
The installation is intentionally split into two phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and testing on VM1 (Apache on port 8080)&lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 (nginx reverse‑proxy + HTTPS)&lt;br /&gt;
&lt;br /&gt;
This staged approach ensures safe testing before the wiki becomes accessible from the internet.&lt;br /&gt;
&lt;br /&gt;
== VM Overview ==&lt;br /&gt;
* &#039;&#039;&#039;VM1 (Web Layer):&#039;&#039;&#039; 192.168.33.231  &lt;br /&gt;
* &#039;&#039;&#039;VM2 (Database Layer):&#039;&#039;&#039; 192.168.33.232  &lt;br /&gt;
* &#039;&#039;&#039;VM3 (Reverse‑Proxy Layer):&#039;&#039;&#039; 192.168.33.233  &lt;br /&gt;
* &#039;&#039;&#039;DB user host address (VM1 → VM2):&#039;&#039;&#039; 10.10.10.1  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 1. Create the Database (VM2 – MariaDB) =&lt;br /&gt;
All commands in this section run on VM2 (192.168.33.232).&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Connect to MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql -u root -p&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Create the database ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki&lt;br /&gt;
  CHARACTER SET utf8mb4&lt;br /&gt;
  COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Create the database user ===&lt;br /&gt;
VM1 connects to MariaDB using its internal DB‑facing address: &#039;&#039;&#039;10.10.10.1&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;10.10.10.1&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Grant privileges ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;10.10.10.1&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.5 Exit ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 2. Prepare the Installation Directory (VM1 – Web Layer) =&lt;br /&gt;
All commands below run on VM1 (192.168.33.231).&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Move into /var/www ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 3. Download MediaWiki =&lt;br /&gt;
Example: MediaWiki 1.43.8 LTS.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 4. Extract MediaWiki =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 5. Move Extracted Files into newMW =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 6. Ownership and Permissions =&lt;br /&gt;
Allows editing via VS Code while keeping Apache functional.&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Directory/file permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.4 Enable group inheritance (setgid) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 7. Apache Configuration on VM1 (Port 8080) =&lt;br /&gt;
&lt;br /&gt;
== 7.1 Enable Apache to listen on port 8080 ==&lt;br /&gt;
Edit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/etc/httpd/conf/httpd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Listen 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.2 Validate and restart ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apachectl configtest&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.3 Verify Apache is listening ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ss -tlnp | grep httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0.0.0.0:80&lt;br /&gt;
0.0.0.0:8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.4 Firewall rule (VM1) ==&lt;br /&gt;
Allow port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
firewall-cmd --add-port=8080/tcp --permanent&lt;br /&gt;
firewall-cmd --reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.5 Local‑only Apache VirtualHost ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newmw.local&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newmw-local-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newmw-local-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 8. Run the Installer Locally =&lt;br /&gt;
Access:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://192.168.33.231:8080&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;http://newmw.local:8080&amp;lt;/code&amp;gt; (if added to /etc/hosts)&lt;br /&gt;
&lt;br /&gt;
== Internal DB Network Logic (10.10.10.x) ==&lt;br /&gt;
The internal network between VM1 and VM2 uses dedicated addresses:&lt;br /&gt;
&lt;br /&gt;
* VM1 (Web Layer): &#039;&#039;&#039;10.10.10.1&#039;&#039;&#039;&lt;br /&gt;
* VM2 (Database Layer): &#039;&#039;&#039;10.10.10.2&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When MediaWiki (running on VM1) connects to MariaDB on VM2, the connection originates from VM1’s internal NIC.  &lt;br /&gt;
Therefore:&lt;br /&gt;
&lt;br /&gt;
* VM1 connects **to** MariaDB at:  &lt;br /&gt;
  &amp;lt;code&amp;gt;10.10.10.2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* VM2 sees VM1 **coming from**:  &lt;br /&gt;
  &amp;lt;code&amp;gt;10.10.10.1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because MariaDB authorizes users based on the client’s source address, database users must be created as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;wikiuser&#039;@&#039;10.10.10.1&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is why all MediaWiki instances on VM1 use the same host restriction.  &lt;br /&gt;
It ensures that only VM1 can authenticate to MariaDB over the internal network.&lt;br /&gt;
&lt;br /&gt;
== Result after installation flow ==&lt;br /&gt;
&lt;br /&gt;
Installer will generate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;http://192.168.33.231:8080&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Place &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 9. Local Testing =&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
* Page creation  &lt;br /&gt;
* Editing  &lt;br /&gt;
* File uploads  &lt;br /&gt;
* User accounts  &lt;br /&gt;
* Extensions  &lt;br /&gt;
* Permissions  &lt;br /&gt;
* Logging  &lt;br /&gt;
&lt;br /&gt;
Only proceed when everything works locally.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 10. Public Exposure via VM3 (Reverse‑Proxy Layer) =&lt;br /&gt;
&lt;br /&gt;
== 10.1 Create minimal HTTP‑only nginx config (VM3) ==&lt;br /&gt;
File:&lt;br /&gt;
&amp;lt;code&amp;gt;/etc/nginx/conf.d/kb.costasano.club.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTP only)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto http;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 11. Issue the Certificate (VM3) =&lt;br /&gt;
Use the nginx plugin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot --nginx -d kb.costasano.club&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certbot will:&lt;br /&gt;
&lt;br /&gt;
* inject temporary ACME config  &lt;br /&gt;
* validate the domain  &lt;br /&gt;
* obtain the certificate  &lt;br /&gt;
* install it  &lt;br /&gt;
* register it for renewal  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 12. Replace with Full HTTPS Reverse‑Proxy (VM3) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTPS)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
# --- HTTP (redirect + ACME) ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- HTTPS ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/kb.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/kb.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 13. Update LocalSettings.php for Public Access =&lt;br /&gt;
Change:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgCanonicalServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 14. Final Verification =&lt;br /&gt;
* &amp;lt;code&amp;gt;curl -I https://kb.costasano.club&amp;lt;/code&amp;gt; returns 200 or 301  &lt;br /&gt;
* Browser loads the wiki  &lt;br /&gt;
* Editing works  &lt;br /&gt;
* Uploads work  &lt;br /&gt;
* No rewrite errors  &lt;br /&gt;
* nginx proxies correctly  &lt;br /&gt;
* MariaDB connections succeed  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 15. Summary =&lt;br /&gt;
This procedure installs MediaWiki in two safe phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and validation on VM1  &lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 with HTTPS  &lt;br /&gt;
&lt;br /&gt;
This ensures a clean, reproducible, low‑risk deployment workflow.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1615</id>
		<title>ICT:MW Installation procedure 1.43 LTS -v2</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS_-v2&amp;diff=1615"/>
		<updated>2026-04-03T16:04:05Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Installation Procedure for a New MediaWiki Instance (MediaWiki 1.43 LTS) = This document describes the clean, reproducible workflow for deploying a new MediaWiki instance on the existing 3‑VM infrastructure.  The installation is intentionally split into two phases:  * &amp;#039;&amp;#039;&amp;#039;Phase 1:&amp;#039;&amp;#039;&amp;#039; Local installation and testing on VM1 (Apache on port 8080) * &amp;#039;&amp;#039;&amp;#039;Phase 2:&amp;#039;&amp;#039;&amp;#039; Public exposure through VM3 (nginx reverse‑proxy + HTTPS)  This staged approach ensures safe testing before...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Installation Procedure for a New MediaWiki Instance (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible workflow for deploying a new MediaWiki instance on the existing 3‑VM infrastructure.&lt;br /&gt;
&lt;br /&gt;
The installation is intentionally split into two phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and testing on VM1 (Apache on port 8080)&lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 (nginx reverse‑proxy + HTTPS)&lt;br /&gt;
&lt;br /&gt;
This staged approach ensures safe testing before the wiki becomes accessible from the internet.&lt;br /&gt;
&lt;br /&gt;
== VM Overview ==&lt;br /&gt;
* &#039;&#039;&#039;VM1 (Web Layer):&#039;&#039;&#039; 192.168.33.231  &lt;br /&gt;
* &#039;&#039;&#039;VM2 (Database Layer):&#039;&#039;&#039; 192.168.33.232  &lt;br /&gt;
* &#039;&#039;&#039;VM3 (Reverse‑Proxy Layer):&#039;&#039;&#039; 192.168.33.233  &lt;br /&gt;
* &#039;&#039;&#039;DB user host address (VM1 → VM2):&#039;&#039;&#039; 10.10.10.1  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 1. Create the Database (VM2 – MariaDB) =&lt;br /&gt;
All commands in this section run on VM2 (192.168.33.232).&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Connect to MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql -u root -p&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Create the database ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki&lt;br /&gt;
  CHARACTER SET utf8mb4&lt;br /&gt;
  COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Create the database user ===&lt;br /&gt;
VM1 connects to MariaDB using its internal DB‑facing address: &#039;&#039;&#039;10.10.10.1&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;10.10.10.1&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Grant privileges ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;10.10.10.1&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.5 Exit ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 2. Prepare the Installation Directory (VM1 – Web Layer) =&lt;br /&gt;
All commands below run on VM1 (192.168.33.231).&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Move into /var/www ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 3. Download MediaWiki =&lt;br /&gt;
Example: MediaWiki 1.43.8 LTS.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 4. Extract MediaWiki =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 5. Move Extracted Files into newMW =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 6. Ownership and Permissions =&lt;br /&gt;
Allows editing via VS Code while keeping Apache functional.&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Directory/file permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.4 Enable group inheritance (setgid) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 7. Apache Configuration on VM1 (Port 8080) =&lt;br /&gt;
&lt;br /&gt;
== 7.1 Enable Apache to listen on port 8080 ==&lt;br /&gt;
Edit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/etc/httpd/conf/httpd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Listen 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.2 Validate and restart ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apachectl configtest&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.3 Verify Apache is listening ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ss -tlnp | grep httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0.0.0.0:80&lt;br /&gt;
0.0.0.0:8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.4 Firewall rule (VM1) ==&lt;br /&gt;
Allow port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
firewall-cmd --add-port=8080/tcp --permanent&lt;br /&gt;
firewall-cmd --reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.5 Local‑only Apache VirtualHost ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newmw.local&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newmw-local-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newmw-local-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 8. Run the Installer Locally =&lt;br /&gt;
Access:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://192.168.33.231:8080&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;http://newmw.local:8080&amp;lt;/code&amp;gt; (if added to /etc/hosts)&lt;br /&gt;
&lt;br /&gt;
Installer will generate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;http://192.168.33.231:8080&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Place &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 9. Local Testing =&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
* Page creation  &lt;br /&gt;
* Editing  &lt;br /&gt;
* File uploads  &lt;br /&gt;
* User accounts  &lt;br /&gt;
* Extensions  &lt;br /&gt;
* Permissions  &lt;br /&gt;
* Logging  &lt;br /&gt;
&lt;br /&gt;
Only proceed when everything works locally.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 10. Public Exposure via VM3 (Reverse‑Proxy Layer) =&lt;br /&gt;
&lt;br /&gt;
== 10.1 Create minimal HTTP‑only nginx config (VM3) ==&lt;br /&gt;
File:&lt;br /&gt;
&amp;lt;code&amp;gt;/etc/nginx/conf.d/kb.costasano.club.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTP only)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto http;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 11. Issue the Certificate (VM3) =&lt;br /&gt;
Use the nginx plugin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot --nginx -d kb.costasano.club&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certbot will:&lt;br /&gt;
&lt;br /&gt;
* inject temporary ACME config  &lt;br /&gt;
* validate the domain  &lt;br /&gt;
* obtain the certificate  &lt;br /&gt;
* install it  &lt;br /&gt;
* register it for renewal  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 12. Replace with Full HTTPS Reverse‑Proxy (VM3) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTPS)&lt;br /&gt;
#  kb.costasano.club&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
# --- HTTP (redirect + ACME) ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- HTTPS ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name kb.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/kb.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/kb.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231:8080;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host kb.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 13. Update LocalSettings.php for Public Access =&lt;br /&gt;
Change:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgCanonicalServer = &amp;quot;https://kb.costasano.club&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 14. Final Verification =&lt;br /&gt;
* &amp;lt;code&amp;gt;curl -I https://kb.costasano.club&amp;lt;/code&amp;gt; returns 200 or 301  &lt;br /&gt;
* Browser loads the wiki  &lt;br /&gt;
* Editing works  &lt;br /&gt;
* Uploads work  &lt;br /&gt;
* No rewrite errors  &lt;br /&gt;
* nginx proxies correctly  &lt;br /&gt;
* MariaDB connections succeed  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= 15. Summary =&lt;br /&gt;
This procedure installs MediaWiki in two safe phases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Phase 1:&#039;&#039;&#039; Local installation and validation on VM1  &lt;br /&gt;
* &#039;&#039;&#039;Phase 2:&#039;&#039;&#039; Public exposure through VM3 with HTTPS  &lt;br /&gt;
&lt;br /&gt;
This ensures a clean, reproducible, low‑risk deployment workflow.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1614</id>
		<title>ICT:MW Installation procedure 1.43 LTS</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1614"/>
		<updated>2026-04-03T15:50:58Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Installation Procedure for newMW (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible installation workflow for&lt;br /&gt;
deploying a new MediaWiki instance (newMW) on the existing 3‑VM infrastructure.&lt;br /&gt;
The installation is intentionally split into two phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1: Local installation and testing on VM1 (no reverse‑proxy required)&lt;br /&gt;
* Phase 2: Public exposure through VM3 (nginx reverse‑proxy)&lt;br /&gt;
&lt;br /&gt;
This staged approach allows safe testing before the wiki becomes accessible&lt;br /&gt;
from the internet.&lt;br /&gt;
&lt;br /&gt;
== 1. Create the newwiki Database (VM2 – Database Layer) ==&lt;br /&gt;
All commands in this section are executed on VM2.&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Connect to MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql -u root -p&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Create the database ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki&lt;br /&gt;
  CHARACTER SET utf8mb4&lt;br /&gt;
  COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Create the database user ===&lt;br /&gt;
Replace &amp;lt;code&amp;gt;VM1-IP&amp;lt;/code&amp;gt; with the internal Hyper‑V IP of the web server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;VM1-IP&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Grant privileges ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;VM1-IP&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.5 Exit MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Prepare the Installation Directory (VM1 – Web Layer) ==&lt;br /&gt;
All commands below are executed on VM1.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Move into the working directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3. Download MediaWiki ==&lt;br /&gt;
Download the latest LTS tarball (example: 1.43.8):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4. Extract MediaWiki ==&lt;br /&gt;
Extract the archive. This creates a directory such as &amp;lt;code&amp;gt;mediawiki-1.43.8/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Move Extracted Files into newMW ==&lt;br /&gt;
Move normal files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Move hidden files (e.g. &amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the now‑empty extraction directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the tarball:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. Ownership and Permissions ==&lt;br /&gt;
Use the preferred ownership model to allow editing via VS Code while keeping&lt;br /&gt;
Apache fully functional.&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Apply clean directory/file permissions ===&lt;br /&gt;
Directories:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.4 Enable group inheritance (setgid) ===&lt;br /&gt;
Ensures new files created inside inherit the &amp;lt;code&amp;gt;apache&amp;lt;/code&amp;gt; group.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7. Enable Apache to Listen on Port 8080 (VM1) &amp;amp; Virtual Host file ==&lt;br /&gt;
Apache does not automatically listen on new ports defined in VirtualHost&lt;br /&gt;
blocks. The port must be explicitly enabled in the main configuration.&lt;br /&gt;
&lt;br /&gt;
=== 7.1 Edit the main Apache configuration ===&lt;br /&gt;
Open the file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/etc/httpd/conf/httpd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Locate the existing Listen directive:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Listen 80&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the following line directly below it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Listen 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.2 Validate the configuration ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apachectl configtest&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Syntax OK&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.3 Restart Apache ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.4 Verify Apache is listening on port 8080 ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ss -tlnp | grep httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected result:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0.0.0.0:80&lt;br /&gt;
0.0.0.0:8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apache is now ready to serve the temporary local-only vhost on port 8080.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.5 Local‑Only Apache Virtual Host (VM1) ===&lt;br /&gt;
This vhost allows installation and testing without involving the reverse‑proxy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newmw.local&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newmw-local-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newmw-local-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 8. Run the Installer Locally ==&lt;br /&gt;
Access the installer using one of:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://VM1-IP:8080&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;http://newmw.local:8080&amp;lt;/code&amp;gt; (if added to /etc/hosts)&lt;br /&gt;
&lt;br /&gt;
Complete the installation.&lt;br /&gt;
&lt;br /&gt;
The installer will generate a &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;http://VM1-IP:8080&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Place the file in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 9. Local Testing ==&lt;br /&gt;
Before exposing the wiki publicly, verify:&lt;br /&gt;
&lt;br /&gt;
* Page creation&lt;br /&gt;
* Editing&lt;br /&gt;
* File uploads (if enabled)&lt;br /&gt;
* User accounts&lt;br /&gt;
* Extensions&lt;br /&gt;
* Permissions&lt;br /&gt;
* Logging&lt;br /&gt;
&lt;br /&gt;
This phase is isolated and safe.&lt;br /&gt;
&lt;br /&gt;
== 10. Prepare for Public Access (VM3 – Reverse‑Proxy Layer) ==&lt;br /&gt;
Only after local testing is complete, add the nginx server block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name newwiki.example.com;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://VM1-IP:8080;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 11. Update LocalSettings.php for Public Access ==&lt;br /&gt;
Change:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgCanonicalServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 12. Final Verification ==&lt;br /&gt;
* Public URL loads correctly&lt;br /&gt;
* &amp;lt;code&amp;gt;/wiki/Main_Page&amp;lt;/code&amp;gt; resolves&lt;br /&gt;
* Editing works&lt;br /&gt;
* Uploads work&lt;br /&gt;
* No rewrite errors in Apache logs&lt;br /&gt;
* nginx proxies correctly&lt;br /&gt;
* MariaDB connections succeed&lt;br /&gt;
&lt;br /&gt;
== 13. Summary ==&lt;br /&gt;
This procedure installs MediaWiki in two safe phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1: Local installation and validation on VM1&lt;br /&gt;
* Phase 2: Public exposure through VM3&lt;br /&gt;
&lt;br /&gt;
This staged approach avoids dependency on the reverse‑proxy during installation&lt;br /&gt;
and allows controlled, low‑risk rollout.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Addendum =&lt;br /&gt;
&lt;br /&gt;
== Adding a New Domain + Certificate on VM3 (nginx + Certbot) ==&lt;br /&gt;
This procedure is used when onboarding a new subdomain to the reverse‑proxy layer on VM3.  &lt;br /&gt;
It avoids the nginx/Certbot “missing certificate” failure by always starting with a minimal HTTP‑only configuration, issuing the certificate, and then enabling HTTPS.&lt;br /&gt;
&lt;br /&gt;
=== 1. Create a minimal HTTP‑only reverse‑proxy config ===&lt;br /&gt;
Create &amp;lt;code&amp;gt;/etc/nginx/conf.d/&amp;amp;lt;domain&amp;amp;gt;.conf&amp;lt;/code&amp;gt; with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTP only)&lt;br /&gt;
#  &amp;lt;domain&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name &amp;lt;domain&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
    # ACME challenge (Certbot uses this)&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Temporary: no HTTPS redirect until certificate exists&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://&amp;lt;backend-ip&amp;gt;:&amp;lt;backend-port&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host &amp;lt;domain&amp;gt;;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto http;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At this point the domain resolves over HTTP and Certbot can validate it.&lt;br /&gt;
&lt;br /&gt;
=== 2. Issue the certificate using the nginx plugin ===&lt;br /&gt;
Run:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot --nginx -d &amp;lt;domain&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certbot will:&lt;br /&gt;
* inject a temporary ACME location  &lt;br /&gt;
* validate the domain  &lt;br /&gt;
* obtain the certificate  &lt;br /&gt;
* install it  &lt;br /&gt;
* register it for automatic renewal  &lt;br /&gt;
&lt;br /&gt;
This step only works if nginx is running without SSL errors.&lt;br /&gt;
&lt;br /&gt;
=== 3. Replace the config with the full HTTPS reverse‑proxy ===&lt;br /&gt;
After the certificate exists, replace the file with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
#  Reverse proxy (HTTPS)&lt;br /&gt;
#  &amp;lt;domain&amp;gt;&lt;br /&gt;
# ============================================&lt;br /&gt;
&lt;br /&gt;
# --- HTTP (redirect + ACME) ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name &amp;lt;domain&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- HTTPS ---&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name &amp;lt;domain&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/&amp;lt;domain&amp;gt;/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/&amp;lt;domain&amp;gt;/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /usr/share/nginx/html;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://&amp;lt;backend-ip&amp;gt;:&amp;lt;backend-port&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host &amp;lt;domain&amp;gt;;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t&lt;br /&gt;
sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. Verification checklist ===&lt;br /&gt;
* &amp;lt;code&amp;gt;curl -I https://&amp;amp;lt;domain&amp;amp;gt;&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;301&amp;lt;/code&amp;gt;  &lt;br /&gt;
* Browser loads the site over HTTPS  &lt;br /&gt;
* Login works  &lt;br /&gt;
* File uploads work  &lt;br /&gt;
* Optional: &amp;lt;code&amp;gt;certbot renew --dry-run&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1613</id>
		<title>ICT:MW Installation procedure 1.43 LTS</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1613"/>
		<updated>2026-04-03T14:38:58Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Installation Procedure for newMW (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible installation workflow for&lt;br /&gt;
deploying a new MediaWiki instance (newMW) on the existing 3‑VM infrastructure.&lt;br /&gt;
The installation is intentionally split into two phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1: Local installation and testing on VM1 (no reverse‑proxy required)&lt;br /&gt;
* Phase 2: Public exposure through VM3 (nginx reverse‑proxy)&lt;br /&gt;
&lt;br /&gt;
This staged approach allows safe testing before the wiki becomes accessible&lt;br /&gt;
from the internet.&lt;br /&gt;
&lt;br /&gt;
== 1. Create the newwiki Database (VM2 – Database Layer) ==&lt;br /&gt;
All commands in this section are executed on VM2.&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Connect to MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql -u root -p&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Create the database ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki&lt;br /&gt;
  CHARACTER SET utf8mb4&lt;br /&gt;
  COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Create the database user ===&lt;br /&gt;
Replace &amp;lt;code&amp;gt;VM1-IP&amp;lt;/code&amp;gt; with the internal Hyper‑V IP of the web server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;VM1-IP&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Grant privileges ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;VM1-IP&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.5 Exit MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Prepare the Installation Directory (VM1 – Web Layer) ==&lt;br /&gt;
All commands below are executed on VM1.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Move into the working directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3. Download MediaWiki ==&lt;br /&gt;
Download the latest LTS tarball (example: 1.43.8):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4. Extract MediaWiki ==&lt;br /&gt;
Extract the archive. This creates a directory such as &amp;lt;code&amp;gt;mediawiki-1.43.8/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Move Extracted Files into newMW ==&lt;br /&gt;
Move normal files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Move hidden files (e.g. &amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the now‑empty extraction directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the tarball:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. Ownership and Permissions ==&lt;br /&gt;
Use the preferred ownership model to allow editing via VS Code while keeping&lt;br /&gt;
Apache fully functional.&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Apply clean directory/file permissions ===&lt;br /&gt;
Directories:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.4 Enable group inheritance (setgid) ===&lt;br /&gt;
Ensures new files created inside inherit the &amp;lt;code&amp;gt;apache&amp;lt;/code&amp;gt; group.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7. Enable Apache to Listen on Port 8080 (VM1) &amp;amp; Virtual Host file ==&lt;br /&gt;
Apache does not automatically listen on new ports defined in VirtualHost&lt;br /&gt;
blocks. The port must be explicitly enabled in the main configuration.&lt;br /&gt;
&lt;br /&gt;
=== 7.1 Edit the main Apache configuration ===&lt;br /&gt;
Open the file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/etc/httpd/conf/httpd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Locate the existing Listen directive:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Listen 80&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the following line directly below it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Listen 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.2 Validate the configuration ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apachectl configtest&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Syntax OK&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.3 Restart Apache ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.4 Verify Apache is listening on port 8080 ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ss -tlnp | grep httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected result:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0.0.0.0:80&lt;br /&gt;
0.0.0.0:8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apache is now ready to serve the temporary local-only vhost on port 8080.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.5 Local‑Only Apache Virtual Host (VM1) ===&lt;br /&gt;
This vhost allows installation and testing without involving the reverse‑proxy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newmw.local&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newmw-local-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newmw-local-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 8. Run the Installer Locally ==&lt;br /&gt;
Access the installer using one of:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://VM1-IP:8080&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;http://newmw.local:8080&amp;lt;/code&amp;gt; (if added to /etc/hosts)&lt;br /&gt;
&lt;br /&gt;
Complete the installation.&lt;br /&gt;
&lt;br /&gt;
The installer will generate a &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;http://VM1-IP:8080&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Place the file in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 9. Local Testing ==&lt;br /&gt;
Before exposing the wiki publicly, verify:&lt;br /&gt;
&lt;br /&gt;
* Page creation&lt;br /&gt;
* Editing&lt;br /&gt;
* File uploads (if enabled)&lt;br /&gt;
* User accounts&lt;br /&gt;
* Extensions&lt;br /&gt;
* Permissions&lt;br /&gt;
* Logging&lt;br /&gt;
&lt;br /&gt;
This phase is isolated and safe.&lt;br /&gt;
&lt;br /&gt;
== 10. Prepare for Public Access (VM3 – Reverse‑Proxy Layer) ==&lt;br /&gt;
Only after local testing is complete, add the nginx server block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name newwiki.example.com;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://VM1-IP:8080;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 11. Update LocalSettings.php for Public Access ==&lt;br /&gt;
Change:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgCanonicalServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 12. Final Verification ==&lt;br /&gt;
* Public URL loads correctly&lt;br /&gt;
* &amp;lt;code&amp;gt;/wiki/Main_Page&amp;lt;/code&amp;gt; resolves&lt;br /&gt;
* Editing works&lt;br /&gt;
* Uploads work&lt;br /&gt;
* No rewrite errors in Apache logs&lt;br /&gt;
* nginx proxies correctly&lt;br /&gt;
* MariaDB connections succeed&lt;br /&gt;
&lt;br /&gt;
== 13. Summary ==&lt;br /&gt;
This procedure installs MediaWiki in two safe phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1: Local installation and validation on VM1&lt;br /&gt;
* Phase 2: Public exposure through VM3&lt;br /&gt;
&lt;br /&gt;
This staged approach avoids dependency on the reverse‑proxy during installation&lt;br /&gt;
and allows controlled, low‑risk rollout.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1612</id>
		<title>ICT:MW Installation procedure 1.43 LTS</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1612"/>
		<updated>2026-04-03T13:36:33Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Installation Procedure for newMW (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible installation workflow for&lt;br /&gt;
deploying a new MediaWiki instance (newMW) on the existing 3‑VM infrastructure.&lt;br /&gt;
The installation is intentionally split into two phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1: Local installation and testing on VM1 (no reverse‑proxy required)&lt;br /&gt;
* Phase 2: Public exposure through VM3 (nginx reverse‑proxy)&lt;br /&gt;
&lt;br /&gt;
This staged approach allows safe testing before the wiki becomes accessible&lt;br /&gt;
from the internet.&lt;br /&gt;
&lt;br /&gt;
== 1. Create the newwiki Database (VM2 – Database Layer) ==&lt;br /&gt;
All commands in this section are executed on VM2.&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Connect to MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql -u root -p&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Create the database ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki&lt;br /&gt;
  CHARACTER SET utf8mb4&lt;br /&gt;
  COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Create the database user ===&lt;br /&gt;
Replace &amp;lt;code&amp;gt;VM1-IP&amp;lt;/code&amp;gt; with the internal Hyper‑V IP of the web server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;VM1-IP&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Grant privileges ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;VM1-IP&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.5 Exit MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Prepare the Installation Directory (VM1 – Web Layer) ==&lt;br /&gt;
All commands below are executed on VM1.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Move into the working directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3. Download MediaWiki ==&lt;br /&gt;
Download the latest LTS tarball (example: 1.43.8):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4. Extract MediaWiki ==&lt;br /&gt;
Extract the archive. This creates a directory such as &amp;lt;code&amp;gt;mediawiki-1.43.8/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Move Extracted Files into newMW ==&lt;br /&gt;
Move normal files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Move hidden files (e.g. &amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the now‑empty extraction directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the tarball:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. Ownership and Permissions ==&lt;br /&gt;
Use the preferred ownership model to allow editing via VS Code while keeping&lt;br /&gt;
Apache fully functional.&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Apply clean directory/file permissions ===&lt;br /&gt;
Directories:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.4 Enable group inheritance (setgid) ===&lt;br /&gt;
Ensures new files created inside inherit the &amp;lt;code&amp;gt;apache&amp;lt;/code&amp;gt; group.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7. Local‑Only Apache Virtual Host (VM1) ==&lt;br /&gt;
This vhost allows installation and testing without involving the reverse‑proxy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newmw.local&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newmw-local-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newmw-local-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 8. Run the Installer Locally ==&lt;br /&gt;
Access the installer using one of:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://VM1-IP:8080&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;http://newmw.local:8080&amp;lt;/code&amp;gt; (if added to /etc/hosts)&lt;br /&gt;
&lt;br /&gt;
Complete the installation.&lt;br /&gt;
&lt;br /&gt;
The installer will generate a &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;http://VM1-IP:8080&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Place the file in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 9. Local Testing ==&lt;br /&gt;
Before exposing the wiki publicly, verify:&lt;br /&gt;
&lt;br /&gt;
* Page creation&lt;br /&gt;
* Editing&lt;br /&gt;
* File uploads (if enabled)&lt;br /&gt;
* User accounts&lt;br /&gt;
* Extensions&lt;br /&gt;
* Permissions&lt;br /&gt;
* Logging&lt;br /&gt;
&lt;br /&gt;
This phase is isolated and safe.&lt;br /&gt;
&lt;br /&gt;
== 10. Prepare for Public Access (VM3 – Reverse‑Proxy Layer) ==&lt;br /&gt;
Only after local testing is complete, add the nginx server block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name newwiki.example.com;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://VM1-IP:8080;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 11. Update LocalSettings.php for Public Access ==&lt;br /&gt;
Change:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgCanonicalServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 12. Final Verification ==&lt;br /&gt;
* Public URL loads correctly&lt;br /&gt;
* &amp;lt;code&amp;gt;/wiki/Main_Page&amp;lt;/code&amp;gt; resolves&lt;br /&gt;
* Editing works&lt;br /&gt;
* Uploads work&lt;br /&gt;
* No rewrite errors in Apache logs&lt;br /&gt;
* nginx proxies correctly&lt;br /&gt;
* MariaDB connections succeed&lt;br /&gt;
&lt;br /&gt;
== 13. Summary ==&lt;br /&gt;
This procedure installs MediaWiki in two safe phases:&lt;br /&gt;
&lt;br /&gt;
* Phase 1: Local installation and validation on VM1&lt;br /&gt;
* Phase 2: Public exposure through VM3&lt;br /&gt;
&lt;br /&gt;
This staged approach avoids dependency on the reverse‑proxy during installation&lt;br /&gt;
and allows controlled, low‑risk rollout.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1611</id>
		<title>ICT:MW Installation procedure 1.43 LTS</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1611"/>
		<updated>2026-04-03T13:31:41Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Installation Procedure for newMW (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible installation workflow for&lt;br /&gt;
deploying a new MediaWiki instance (newMW) on the existing 3‑VM infrastructure.&lt;br /&gt;
It includes directory preparation, file extraction, permissions, database&lt;br /&gt;
creation, and the preferred ownership model for VS Code access.&lt;br /&gt;
&lt;br /&gt;
== 1. Create the newwiki Database (VM2 – Database Layer) ==&lt;br /&gt;
All commands in this section are executed on VM2.&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Connect to MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql -u root -p&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Create the database ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki&lt;br /&gt;
  CHARACTER SET utf8mb4&lt;br /&gt;
  COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Create the database user ===&lt;br /&gt;
Replace &amp;lt;code&amp;gt;VM1-IP&amp;lt;/code&amp;gt; with the internal Hyper‑V IP of the web server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;VM1-IP&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Grant privileges ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;VM1-IP&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.5 Exit MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Prepare the Installation Directory (VM1 – Web Layer) ==&lt;br /&gt;
All commands below are executed on VM1.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Move into the working directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3. Download MediaWiki ==&lt;br /&gt;
Download the latest LTS tarball (example: 1.43.8):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4. Extract MediaWiki ==&lt;br /&gt;
Extract the archive. This creates a directory such as &amp;lt;code&amp;gt;mediawiki-1.43.8/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Move Extracted Files into newMW ==&lt;br /&gt;
Move normal files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Move hidden files (e.g. &amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the now‑empty extraction directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the tarball:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. Ownership and Permissions ==&lt;br /&gt;
Use the preferred ownership model to allow editing via VS Code while keeping&lt;br /&gt;
Apache fully functional.&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Apply clean directory/file permissions ===&lt;br /&gt;
Directories:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 6.4 Enable group inheritance (setgid) ===&lt;br /&gt;
Ensures new files created inside inherit the &amp;lt;code&amp;gt;apache&amp;lt;/code&amp;gt; group.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7. Apache Virtual Host (VM1) ==&lt;br /&gt;
Create a vhost:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newwiki.example.com&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newwiki-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newwiki-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 8. nginx Reverse‑Proxy (VM3) ==&lt;br /&gt;
Add a server block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name newwiki.example.com;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://VM1-IP:8080;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 9. Run the MediaWiki Installer ==&lt;br /&gt;
Navigate to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
https://newwiki.example.com&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Provide:&lt;br /&gt;
* Database: &amp;lt;code&amp;gt;newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
* User: &amp;lt;code&amp;gt;newwikiuser&amp;lt;/code&amp;gt;&lt;br /&gt;
* Password: (as created on VM2)&lt;br /&gt;
* Site name&lt;br /&gt;
* Admin account&lt;br /&gt;
&lt;br /&gt;
Download &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; and place it in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 10. Minimal Post‑Install Configuration ==&lt;br /&gt;
Add or verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
$wgScriptPath = &amp;quot;&amp;quot;;&lt;br /&gt;
$wgArticlePath = &amp;quot;/wiki/$1&amp;quot;;&lt;br /&gt;
$wgUsePathInfo = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable uploads (optional):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgEnableUploads = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable SyntaxHighlight:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wfLoadExtension( &#039;SyntaxHighlight_GeSHi&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 11. Verification Checklist ==&lt;br /&gt;
* Site loads correctly&lt;br /&gt;
* &amp;lt;code&amp;gt;/wiki/Main_Page&amp;lt;/code&amp;gt; resolves&lt;br /&gt;
* Editing works&lt;br /&gt;
* File uploads work (if enabled)&lt;br /&gt;
* No rewrite errors in Apache logs&lt;br /&gt;
* nginx proxies correctly&lt;br /&gt;
* MariaDB connections succeed&lt;br /&gt;
&lt;br /&gt;
== 12. Summary ==&lt;br /&gt;
This procedure provides a clean, repeatable installation workflow for MediaWiki&lt;br /&gt;
1.43 LTS using the preferred ownership model (&amp;lt;code&amp;gt;mngr:apache&amp;lt;/code&amp;gt;) and the&lt;br /&gt;
existing 3‑VM architecture. It ensures clarity, maintainability, and&lt;br /&gt;
successor‑friendly operation.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1610</id>
		<title>ICT:MW Installation procedure 1.43 LTS</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Installation_procedure_1.43_LTS&amp;diff=1610"/>
		<updated>2026-04-03T13:29:43Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Installation Procedure for newMW (MediaWiki 1.43 LTS) = This document describes the clean, reproducible installation workflow for deploying a new MediaWiki instance (newMW) on the existing 3‑VM infrastructure. It includes directory preparation, file extraction, permissions, and the preferred ownership model for VS Code access.  == 1. Preparation == All commands are executed on VM1 (Web Layer).  === 1.1 Create the target directory === &amp;lt;pre&amp;gt; mkdir /var/www/newMW &amp;lt;/pre&amp;gt;...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Installation Procedure for newMW (MediaWiki 1.43 LTS) =&lt;br /&gt;
This document describes the clean, reproducible installation workflow for&lt;br /&gt;
deploying a new MediaWiki instance (newMW) on the existing 3‑VM infrastructure.&lt;br /&gt;
It includes directory preparation, file extraction, permissions, and the&lt;br /&gt;
preferred ownership model for VS Code access.&lt;br /&gt;
&lt;br /&gt;
== 1. Preparation ==&lt;br /&gt;
All commands are executed on VM1 (Web Layer).&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Create the target directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Move into the working directory ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Download MediaWiki ==&lt;br /&gt;
Download the latest LTS tarball (example: 1.43.8):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3. Extract MediaWiki ==&lt;br /&gt;
Extract the archive. This creates a directory such as &amp;lt;code&amp;gt;mediawiki-1.43.8/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tar -xvzf mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4. Move Extracted Files into newMW ==&lt;br /&gt;
Move normal files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/* newMW/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Move hidden files (e.g. &amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mv mediawiki-1.43.8/.* newMW/ 2&amp;gt;/dev/null&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the now‑empty extraction directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rmdir mediawiki-1.43.8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove the tarball:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rm mediawiki-1.43.8.tar.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Ownership and Permissions ==&lt;br /&gt;
Use the preferred ownership model to allow editing via VS Code while keeping&lt;br /&gt;
Apache fully functional.&lt;br /&gt;
&lt;br /&gt;
=== 5.1 Set owner and group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R mngr:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5.2 Ensure group write permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod -R g+w /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5.3 Apply clean directory/file permissions ===&lt;br /&gt;
Directories:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type d -exec chmod 775 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
find /var/www/newMW -type f -exec chmod 664 {} \;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5.4 Enable group inheritance (setgid) ===&lt;br /&gt;
Ensures new files created inside inherit the &amp;lt;code&amp;gt;apache&amp;lt;/code&amp;gt; group.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod g+s /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. Apache Virtual Host ==&lt;br /&gt;
Create a vhost on VM1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newwiki.example.com&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newwiki-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newwiki-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7. nginx Reverse‑Proxy (VM3) ==&lt;br /&gt;
Add a server block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name newwiki.example.com;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://VM1-IP:8080;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 8. Run the MediaWiki Installer ==&lt;br /&gt;
Navigate to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
https://newwiki.example.com&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Provide:&lt;br /&gt;
* Database: &amp;lt;code&amp;gt;newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
* User: &amp;lt;code&amp;gt;newwikiuser&amp;lt;/code&amp;gt;&lt;br /&gt;
* Password: (as created on VM2)&lt;br /&gt;
* Site name&lt;br /&gt;
* Admin account&lt;br /&gt;
&lt;br /&gt;
Download &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; and place it in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 9. Minimal Post‑Install Configuration ==&lt;br /&gt;
Add or verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
$wgScriptPath = &amp;quot;&amp;quot;;&lt;br /&gt;
$wgArticlePath = &amp;quot;/wiki/$1&amp;quot;;&lt;br /&gt;
$wgUsePathInfo = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable uploads (optional):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgEnableUploads = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable SyntaxHighlight:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wfLoadExtension( &#039;SyntaxHighlight_GeSHi&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 10. Verification Checklist ==&lt;br /&gt;
* Site loads correctly&lt;br /&gt;
* &amp;lt;code&amp;gt;/wiki/Main_Page&amp;lt;/code&amp;gt; resolves&lt;br /&gt;
* Editing works&lt;br /&gt;
* File uploads work (if enabled)&lt;br /&gt;
* No rewrite errors in Apache logs&lt;br /&gt;
* nginx proxies correctly&lt;br /&gt;
* MariaDB connections succeed&lt;br /&gt;
&lt;br /&gt;
== 11. Summary ==&lt;br /&gt;
This procedure provides a clean, repeatable installation workflow for MediaWiki&lt;br /&gt;
1.43 LTS using the preferred ownership model (&amp;lt;code&amp;gt;mngr:apache&amp;lt;/code&amp;gt;) and the&lt;br /&gt;
existing 3‑VM architecture. It ensures clarity, maintainability, and&lt;br /&gt;
successor‑friendly operation.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Clean_installation_checklist_version_1.43_LTS&amp;diff=1609</id>
		<title>ICT:MW Clean installation checklist version 1.43 LTS</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Clean_installation_checklist_version_1.43_LTS&amp;diff=1609"/>
		<updated>2026-04-03T11:37:52Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Mngr moved page ICT:MW Clean installaiton checklist version 1.43 LTS to ICT:MW Clean installation checklist version 1.43 LTS without leaving a redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Installation Checklist – MediaWiki 1.43 LTS =&lt;br /&gt;
This document provides a clean, reproducible installation procedure for&lt;br /&gt;
MediaWiki 1.43 LTS on AlmaLinux with Apache, PHP, MariaDB, and an nginx&lt;br /&gt;
reverse-proxy. It is designed for long-term stability and successor-friendly&lt;br /&gt;
maintenance.&lt;br /&gt;
&lt;br /&gt;
== 1. Preparation ==&lt;br /&gt;
Before installing MediaWiki, ensure the server environment is ready.&lt;br /&gt;
&lt;br /&gt;
=== 1.1 System Requirements ===&lt;br /&gt;
* AlmaLinux (or compatible RHEL-based system)&lt;br /&gt;
* Apache (httpd)&lt;br /&gt;
* PHP 8.1 or later&lt;br /&gt;
* MariaDB 10.5 or later&lt;br /&gt;
* nginx reverse-proxy (optional but recommended)&lt;br /&gt;
* SELinux/AppArmor configured or disabled as needed&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Install Required Packages ===&lt;br /&gt;
Install PHP and required extensions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dnf install php php-mbstring php-xml php-intl php-mysqlnd php-gd php-json php-cli php-opcache php-apcu&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install Apache and MariaDB:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dnf install httpd mariadb-server&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable and start services:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl enable --now httpd mariadb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Database Setup ==&lt;br /&gt;
Create a clean database for the new wiki.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Secure MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql_secure_installation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Create Database and User ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;localhost&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;localhost&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3. Download MediaWiki 1.43 LTS ==&lt;br /&gt;
Download and extract the LTS release.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.0.tar.gz&lt;br /&gt;
tar -xvzf mediawiki-1.43.0.tar.gz&lt;br /&gt;
mv mediawiki-1.43.0 newwiki&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set permissions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R apache:apache /var/www/newwiki&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4. Apache Virtual Host ==&lt;br /&gt;
Create a dedicated vhost for the new wiki.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newwiki.example.com&lt;br /&gt;
    DocumentRoot /var/www/newwiki&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newwiki&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newwiki-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newwiki-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable the site and restart Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. nginx Reverse-Proxy ==&lt;br /&gt;
Duplicate the working configuration from the old wiki.&lt;br /&gt;
&lt;br /&gt;
Only change:&lt;br /&gt;
* &amp;lt;code&amp;gt;server_name&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;proxy_pass&amp;lt;/code&amp;gt; target&lt;br /&gt;
* SSL certificate paths&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name newwiki.example.com;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://127.0.0.1:8080;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. Run the MediaWiki Installer ==&lt;br /&gt;
Navigate to:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
https://newwiki.example.com&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Provide:&lt;br /&gt;
* Database name: &amp;lt;code&amp;gt;newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
* Database user: &amp;lt;code&amp;gt;newwikiuser&amp;lt;/code&amp;gt;&lt;br /&gt;
* Database password&lt;br /&gt;
* Site name&lt;br /&gt;
* Admin account&lt;br /&gt;
&lt;br /&gt;
Download the generated &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; and place it in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newwiki/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7. Minimal Post-Install Configuration ==&lt;br /&gt;
Add only the essential customizations.&lt;br /&gt;
&lt;br /&gt;
=== 7.1 URL Structure ===&lt;br /&gt;
Ensure these lines exist:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
$wgScriptPath = &amp;quot;&amp;quot;;&lt;br /&gt;
$wgArticlePath = &amp;quot;/wiki/$1&amp;quot;;&lt;br /&gt;
$wgUsePathInfo = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.2 Enable File Uploads (optional) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgEnableUploads = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.3 Install SyntaxHighlight (recommended) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wfLoadExtension( &#039;SyntaxHighlight_GeSHi&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.4 Create Custom Namespaces (optional) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define(&amp;quot;NS_TECH&amp;quot;, 3000);&lt;br /&gt;
define(&amp;quot;NS_TECH_TALK&amp;quot;, 3001);&lt;br /&gt;
$wgExtraNamespaces[NS_TECH] = &amp;quot;Tech&amp;quot;;&lt;br /&gt;
$wgNamespacesWithSubpages[NS_TECH] = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 8. Testing Checklist ==&lt;br /&gt;
Verify:&lt;br /&gt;
* &amp;lt;code&amp;gt;/wiki/Main_Page&amp;lt;/code&amp;gt; loads correctly&lt;br /&gt;
* Editing works&lt;br /&gt;
* File uploads work (if enabled)&lt;br /&gt;
* Categories work&lt;br /&gt;
* Subpages work in custom namespaces&lt;br /&gt;
* Reverse-proxy preserves Host header&lt;br /&gt;
* No rewrite errors in Apache logs&lt;br /&gt;
&lt;br /&gt;
== 9. Backup Checklist ==&lt;br /&gt;
Create a backup routine for:&lt;br /&gt;
* &amp;lt;code&amp;gt;/var/www/newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt;&lt;br /&gt;
* MariaDB database &amp;lt;code&amp;gt;newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 10. Maintenance Notes ==&lt;br /&gt;
* MediaWiki 1.43 is LTS until December 2027.&lt;br /&gt;
* Apply security updates within the 1.43 branch.&lt;br /&gt;
* Avoid unnecessary extensions to keep the system stable.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_new_MediaWiki_Blueprint&amp;diff=1608</id>
		<title>ICT:MW new MediaWiki Blueprint</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_new_MediaWiki_Blueprint&amp;diff=1608"/>
		<updated>2026-04-03T11:31:56Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Mngr moved page ICT:MW new MediaWIki Blueprint to ICT:MW new MediaWiki Blueprint without leaving a redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Blueprint for Organising the New MediaWiki Instance =&lt;br /&gt;
This document provides a structured approach for building a clean, maintainable,&lt;br /&gt;
successor-friendly MediaWiki installation dedicated to documenting the Drupal&lt;br /&gt;
platform, the server environment, and operational procedures.&lt;br /&gt;
&lt;br /&gt;
The goal is to replace the experimental, Cargo-based wiki with a new,&lt;br /&gt;
purpose-built documentation system that is clear, predictable, and easy to&lt;br /&gt;
maintain.&lt;br /&gt;
&lt;br /&gt;
== 1. Purpose of the New Wiki ==&lt;br /&gt;
The new MediaWiki instance will serve as:&lt;br /&gt;
* A technical maintenance manual for Drupal&lt;br /&gt;
* A user documentation space for historians and editors&lt;br /&gt;
* A server operations manual (AlmaLinux, MariaDB, reverse proxy)&lt;br /&gt;
* A stable, structured knowledge base for long-term stewardship&lt;br /&gt;
&lt;br /&gt;
It replaces the experimental wiki, which accumulated Cargo, PageForms,&lt;br /&gt;
temporary namespaces, and unstructured pages.&lt;br /&gt;
&lt;br /&gt;
== 2. High-Level Structure ==&lt;br /&gt;
The new wiki is organised around three pillars:&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Technical Documentation (System Internals) ===&lt;br /&gt;
Covers:&lt;br /&gt;
* Drupal configuration&lt;br /&gt;
* Views, fields, display modes&lt;br /&gt;
* Custom modules and CSS overrides&lt;br /&gt;
* Multilingual configuration&lt;br /&gt;
* Asset architecture&lt;br /&gt;
* Maintenance workflows&lt;br /&gt;
&lt;br /&gt;
=== 2.2 User Documentation (Functional Use) ===&lt;br /&gt;
Covers:&lt;br /&gt;
* How historians use the system&lt;br /&gt;
* How to add/edit content&lt;br /&gt;
* How to attach assets&lt;br /&gt;
* How to use the publisher view&lt;br /&gt;
* How to navigate multilingual content&lt;br /&gt;
&lt;br /&gt;
=== 2.3 Server Documentation (Infrastructure) ===&lt;br /&gt;
Covers:&lt;br /&gt;
* AlmaLinux installation and updates&lt;br /&gt;
* MariaDB backup/restore&lt;br /&gt;
* Reverse proxy configuration&lt;br /&gt;
* Web server layout&lt;br /&gt;
* Deployment procedures&lt;br /&gt;
&lt;br /&gt;
== 3. Namespaces ==&lt;br /&gt;
Namespaces provide top-level grouping. Suggested namespaces:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Tech:&#039;&#039;&#039; – Technical implementation documentation&lt;br /&gt;
* &#039;&#039;&#039;Guide:&#039;&#039;&#039; – User-facing documentation&lt;br /&gt;
* &#039;&#039;&#039;Server:&#039;&#039;&#039; – Server and infrastructure documentation&lt;br /&gt;
* &#039;&#039;&#039;Admin:&#039;&#039;&#039; – Operational procedures, backups, deployments&lt;br /&gt;
&lt;br /&gt;
These names are placeholders; choose names that fit your style.&lt;br /&gt;
&lt;br /&gt;
== 4. Subpage Hierarchy (Pseudo-Folders) ==&lt;br /&gt;
Subpages create a clear, navigable hierarchy.&lt;br /&gt;
&lt;br /&gt;
=== 4.1 Technical Documentation ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Tech/Overview&lt;br /&gt;
Tech/ContentTypes/Places&lt;br /&gt;
Tech/ContentTypes/Objects&lt;br /&gt;
Tech/Views/RecordView&lt;br /&gt;
Tech/Views/AssetChain&lt;br /&gt;
Tech/Modules/CustomWidgets&lt;br /&gt;
Tech/CSS/Overrides&lt;br /&gt;
Tech/Multilingual/Configuration&lt;br /&gt;
Tech/Permissions/Roles&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.2 User Documentation ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Guide/Overview&lt;br /&gt;
Guide/Editing/AddingRecords&lt;br /&gt;
Guide/Editing/Assets&lt;br /&gt;
Guide/Editing/Translations&lt;br /&gt;
Guide/Publishing/RecordView&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.3 Server Documentation ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Server/AlmaLinux/Installation&lt;br /&gt;
Server/AlmaLinux/Updates&lt;br /&gt;
Server/MariaDB/Backup&lt;br /&gt;
Server/MariaDB/Restore&lt;br /&gt;
Server/ReverseProxy/Configuration&lt;br /&gt;
Server/Web/DirectoryLayout&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Admin Procedures ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Admin/Backup/Drupal&lt;br /&gt;
Admin/Backup/MediaWiki&lt;br /&gt;
Admin/Deployment/Drupal&lt;br /&gt;
Admin/Deployment/Server&lt;br /&gt;
Admin/Monitoring/Checks&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Categories ==&lt;br /&gt;
Categories allow cross-cutting grouping across namespaces.&lt;br /&gt;
&lt;br /&gt;
Suggested categories:&lt;br /&gt;
* [[Category:Drupal]]&lt;br /&gt;
* [[Category:Views]]&lt;br /&gt;
* [[Category:Content Types]]&lt;br /&gt;
* [[Category:Server]]&lt;br /&gt;
* [[Category:Maintenance]]&lt;br /&gt;
* [[Category:User Guide]]&lt;br /&gt;
&lt;br /&gt;
Pages can belong to multiple categories.&lt;br /&gt;
&lt;br /&gt;
== 6. Page Template for Consistency ==&lt;br /&gt;
Use a standard template for all technical pages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
= [Page Title] =&lt;br /&gt;
== Purpose ==&lt;br /&gt;
== Where Implemented ==&lt;br /&gt;
== Configuration Summary ==&lt;br /&gt;
== Dependencies ==&lt;br /&gt;
== Interaction With Other Subsystems ==&lt;br /&gt;
== Known Quirks ==&lt;br /&gt;
== How to Modify Safely ==&lt;br /&gt;
== How to Debug ==&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures clarity and successor-friendliness.&lt;br /&gt;
&lt;br /&gt;
== 7. Migration Strategy ==&lt;br /&gt;
The old wiki remains online as an archive.&lt;br /&gt;
&lt;br /&gt;
Migration steps:&lt;br /&gt;
# Identify important pages worth keeping.&lt;br /&gt;
# Copy/paste them into the new wiki.&lt;br /&gt;
# Rewrite them using the new structure and template.&lt;br /&gt;
# Discard Cargo/PageForms-specific content.&lt;br /&gt;
# Delete or ignore experimental pages in the old wiki.&lt;br /&gt;
&lt;br /&gt;
This avoids contamination of the new clean structure.&lt;br /&gt;
&lt;br /&gt;
== 8. Extensions to Install ==&lt;br /&gt;
Keep the new wiki minimal.&lt;br /&gt;
&lt;br /&gt;
Recommended:&lt;br /&gt;
* SyntaxHighlight (for code/config snippets)&lt;br /&gt;
* CategoryTree (optional)&lt;br /&gt;
* VisualEditor (optional)&lt;br /&gt;
&lt;br /&gt;
Avoid:&lt;br /&gt;
* Cargo&lt;br /&gt;
* PageForms&lt;br /&gt;
* Semantic extensions&lt;br /&gt;
&lt;br /&gt;
== 9. Benefits of This Approach ==&lt;br /&gt;
* Clean, predictable structure&lt;br /&gt;
* Easy navigation via namespaces and subpages&lt;br /&gt;
* Clear separation between technical, user, and server documentation&lt;br /&gt;
* No legacy noise from early experiments&lt;br /&gt;
* Easy onboarding for future maintainers&lt;br /&gt;
* Documentation grows in a controlled, organised way&lt;br /&gt;
&lt;br /&gt;
== 10. Conclusion ==&lt;br /&gt;
This blueprint provides a structured foundation for building a new, clean,&lt;br /&gt;
long-term MediaWiki documentation system. It supports the survival of the&lt;br /&gt;
Drupal platform, the server environment, and the operational knowledge needed&lt;br /&gt;
to maintain them.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:PL_Adding_new_MediaWiki_website&amp;diff=1607</id>
		<title>ICT:PL Adding new MediaWiki website</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:PL_Adding_new_MediaWiki_website&amp;diff=1607"/>
		<updated>2026-04-03T11:29:59Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Mngr moved page ICT:PL Adding new MediaWIki website to ICT:PL Adding new MediaWiki website without leaving a redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Infrastructure Overview and Procedure for Adding a New MediaWiki Instance =&lt;br /&gt;
This document describes the current three‑VM infrastructure used for hosting&lt;br /&gt;
Drupal, MediaWiki, and related services. It also explains how to add a new&lt;br /&gt;
MediaWiki instance (newMW) to the existing environment in a clean,&lt;br /&gt;
reproducible, and low‑risk manner.&lt;br /&gt;
&lt;br /&gt;
== 1. High-Level Architecture ==&lt;br /&gt;
The system is composed of three virtual machines running on Hyper‑V. Each VM&lt;br /&gt;
has a clearly defined responsibility, forming a layered, production‑grade&lt;br /&gt;
architecture.&lt;br /&gt;
&lt;br /&gt;
=== 1.1 VM1 – Web Layer (AlmaLinux) ===&lt;br /&gt;
* Runs Apache (httpd)&lt;br /&gt;
* Runs PHP&lt;br /&gt;
* Hosts:&lt;br /&gt;
** Drupal website&lt;br /&gt;
** oldMW (existing MediaWiki instance)&lt;br /&gt;
** newMW (to be added)&lt;br /&gt;
* Contains all Apache vhosts&lt;br /&gt;
* Contains all rewrite rules (important: rewrites are NOT in nginx)&lt;br /&gt;
* Receives traffic only from VM3 (reverse‑proxy)&lt;br /&gt;
&lt;br /&gt;
=== 1.2 VM2 – Database Layer (AlmaLinux) ===&lt;br /&gt;
* Runs MariaDB&lt;br /&gt;
* Hosts multiple independent databases:&lt;br /&gt;
** drupal&lt;br /&gt;
** oldwiki&lt;br /&gt;
** newwiki (to be created)&lt;br /&gt;
* Accessible only from VM1 (internal Hyper‑V network)&lt;br /&gt;
&lt;br /&gt;
=== 1.3 VM3 – Reverse‑Proxy Layer (AlmaLinux) ===&lt;br /&gt;
* Runs nginx&lt;br /&gt;
* Public-facing entry point&lt;br /&gt;
* Terminates HTTPS&lt;br /&gt;
* Routes incoming requests to VM1 based on domain name&lt;br /&gt;
* Does NOT perform URL rewriting&lt;br /&gt;
* Forwards Host header and client IP&lt;br /&gt;
&lt;br /&gt;
== 2. Traffic Flow ==&lt;br /&gt;
# Client connects to VM3 (nginx) using HTTPS.&lt;br /&gt;
# nginx selects the correct server block based on the domain.&lt;br /&gt;
# nginx forwards the request to VM1 (Apache) using proxy_pass.&lt;br /&gt;
# Apache selects the correct vhost based on ServerName.&lt;br /&gt;
# Apache applies rewrite rules and serves the application.&lt;br /&gt;
# Application (Drupal or MediaWiki) connects to VM2 (MariaDB) as needed.&lt;br /&gt;
&lt;br /&gt;
This separation ensures stability, clarity, and easy maintenance.&lt;br /&gt;
&lt;br /&gt;
== 3. Why This Architecture Works Well ==&lt;br /&gt;
* Each VM has a single responsibility.&lt;br /&gt;
* nginx handles routing and SSL termination.&lt;br /&gt;
* Apache handles application logic and rewrites.&lt;br /&gt;
* MariaDB is isolated and easy to back up.&lt;br /&gt;
* Adding new sites is predictable and low-risk.&lt;br /&gt;
* No component needs to be modified outside its domain.&lt;br /&gt;
&lt;br /&gt;
== 4. Adding a New MediaWiki Instance (newMW) ==&lt;br /&gt;
Adding newMW requires changes on all three VMs, but each change is small,&lt;br /&gt;
isolated, and predictable.&lt;br /&gt;
&lt;br /&gt;
=== 4.1 Step 1 – Create the Database on VM2 (MariaDB) ===&lt;br /&gt;
Connect to MariaDB and create a new database and user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;VM1-IP&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;VM1-IP&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace &amp;lt;code&amp;gt;VM1-IP&amp;lt;/code&amp;gt; with the internal Hyper‑V IP of the web server.&lt;br /&gt;
&lt;br /&gt;
=== 4.2 Step 2 – Install MediaWiki on VM1 (Web Layer) ===&lt;br /&gt;
Download and extract MediaWiki 1.43 LTS:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.0.tar.gz&lt;br /&gt;
tar -xvzf mediawiki-1.43.0.tar.gz&lt;br /&gt;
mv mediawiki-1.43.0 newMW&lt;br /&gt;
chown -R apache:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.3 Step 3 – Create an Apache vhost on VM1 ===&lt;br /&gt;
Create a new vhost file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newwiki.example.com&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newwiki-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newwiki-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Step 4 – Add a Reverse‑Proxy Entry on VM3 (nginx) ===&lt;br /&gt;
Duplicate an existing server block and adjust only the domain and proxy target:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name newwiki.example.com;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://VM1-IP:8080;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.5 Step 5 – Run the MediaWiki Installer ===&lt;br /&gt;
Navigate to:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
https://newwiki.example.com&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Provide:&lt;br /&gt;
* Database: &amp;lt;code&amp;gt;newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
* User: &amp;lt;code&amp;gt;newwikiuser&amp;lt;/code&amp;gt;&lt;br /&gt;
* Password: (as created)&lt;br /&gt;
* Site name&lt;br /&gt;
* Admin account&lt;br /&gt;
&lt;br /&gt;
Download &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; and place it in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.6 Step 6 – Minimal Configuration in LocalSettings.php ===&lt;br /&gt;
Add or verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
$wgScriptPath = &amp;quot;&amp;quot;;&lt;br /&gt;
$wgArticlePath = &amp;quot;/wiki/$1&amp;quot;;&lt;br /&gt;
$wgUsePathInfo = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable uploads if needed:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgEnableUploads = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install SyntaxHighlight:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wfLoadExtension( &#039;SyntaxHighlight_GeSHi&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Verification Checklist ==&lt;br /&gt;
* &amp;lt;code&amp;gt;https://newwiki.example.com&amp;lt;/code&amp;gt; loads correctly.&lt;br /&gt;
* &amp;lt;code&amp;gt;/wiki/Main_Page&amp;lt;/code&amp;gt; resolves without errors.&lt;br /&gt;
* Editing works.&lt;br /&gt;
* File uploads work (if enabled).&lt;br /&gt;
* No rewrite errors in Apache logs.&lt;br /&gt;
* nginx logs show correct proxying.&lt;br /&gt;
* MariaDB logs show successful connections from VM1.&lt;br /&gt;
&lt;br /&gt;
== 6. Summary ==&lt;br /&gt;
Adding a new MediaWiki instance is straightforward because:&lt;br /&gt;
* nginx handles only routing.&lt;br /&gt;
* Apache handles rewrites and vhosts.&lt;br /&gt;
* MariaDB handles isolated databases.&lt;br /&gt;
* Each VM has a clear, stable role.&lt;br /&gt;
* The existing configuration already supports multi‑site operation.&lt;br /&gt;
&lt;br /&gt;
This document provides a reproducible procedure for extending the system&lt;br /&gt;
without reintroducing the complexity or debugging effort experienced during&lt;br /&gt;
the initial setup.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:PL_Adding_new_MediaWiki_website&amp;diff=1606</id>
		<title>ICT:PL Adding new MediaWiki website</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:PL_Adding_new_MediaWiki_website&amp;diff=1606"/>
		<updated>2026-04-03T11:14:42Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Infrastructure Overview and Procedure for Adding a New MediaWiki Instance = This document describes the current three‑VM infrastructure used for hosting Drupal, MediaWiki, and related services. It also explains how to add a new MediaWiki instance (newMW) to the existing environment in a clean, reproducible, and low‑risk manner.  == 1. High-Level Architecture == The system is composed of three virtual machines running on Hyper‑V. Each VM has a clearly defined resp...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Infrastructure Overview and Procedure for Adding a New MediaWiki Instance =&lt;br /&gt;
This document describes the current three‑VM infrastructure used for hosting&lt;br /&gt;
Drupal, MediaWiki, and related services. It also explains how to add a new&lt;br /&gt;
MediaWiki instance (newMW) to the existing environment in a clean,&lt;br /&gt;
reproducible, and low‑risk manner.&lt;br /&gt;
&lt;br /&gt;
== 1. High-Level Architecture ==&lt;br /&gt;
The system is composed of three virtual machines running on Hyper‑V. Each VM&lt;br /&gt;
has a clearly defined responsibility, forming a layered, production‑grade&lt;br /&gt;
architecture.&lt;br /&gt;
&lt;br /&gt;
=== 1.1 VM1 – Web Layer (AlmaLinux) ===&lt;br /&gt;
* Runs Apache (httpd)&lt;br /&gt;
* Runs PHP&lt;br /&gt;
* Hosts:&lt;br /&gt;
** Drupal website&lt;br /&gt;
** oldMW (existing MediaWiki instance)&lt;br /&gt;
** newMW (to be added)&lt;br /&gt;
* Contains all Apache vhosts&lt;br /&gt;
* Contains all rewrite rules (important: rewrites are NOT in nginx)&lt;br /&gt;
* Receives traffic only from VM3 (reverse‑proxy)&lt;br /&gt;
&lt;br /&gt;
=== 1.2 VM2 – Database Layer (AlmaLinux) ===&lt;br /&gt;
* Runs MariaDB&lt;br /&gt;
* Hosts multiple independent databases:&lt;br /&gt;
** drupal&lt;br /&gt;
** oldwiki&lt;br /&gt;
** newwiki (to be created)&lt;br /&gt;
* Accessible only from VM1 (internal Hyper‑V network)&lt;br /&gt;
&lt;br /&gt;
=== 1.3 VM3 – Reverse‑Proxy Layer (AlmaLinux) ===&lt;br /&gt;
* Runs nginx&lt;br /&gt;
* Public-facing entry point&lt;br /&gt;
* Terminates HTTPS&lt;br /&gt;
* Routes incoming requests to VM1 based on domain name&lt;br /&gt;
* Does NOT perform URL rewriting&lt;br /&gt;
* Forwards Host header and client IP&lt;br /&gt;
&lt;br /&gt;
== 2. Traffic Flow ==&lt;br /&gt;
# Client connects to VM3 (nginx) using HTTPS.&lt;br /&gt;
# nginx selects the correct server block based on the domain.&lt;br /&gt;
# nginx forwards the request to VM1 (Apache) using proxy_pass.&lt;br /&gt;
# Apache selects the correct vhost based on ServerName.&lt;br /&gt;
# Apache applies rewrite rules and serves the application.&lt;br /&gt;
# Application (Drupal or MediaWiki) connects to VM2 (MariaDB) as needed.&lt;br /&gt;
&lt;br /&gt;
This separation ensures stability, clarity, and easy maintenance.&lt;br /&gt;
&lt;br /&gt;
== 3. Why This Architecture Works Well ==&lt;br /&gt;
* Each VM has a single responsibility.&lt;br /&gt;
* nginx handles routing and SSL termination.&lt;br /&gt;
* Apache handles application logic and rewrites.&lt;br /&gt;
* MariaDB is isolated and easy to back up.&lt;br /&gt;
* Adding new sites is predictable and low-risk.&lt;br /&gt;
* No component needs to be modified outside its domain.&lt;br /&gt;
&lt;br /&gt;
== 4. Adding a New MediaWiki Instance (newMW) ==&lt;br /&gt;
Adding newMW requires changes on all three VMs, but each change is small,&lt;br /&gt;
isolated, and predictable.&lt;br /&gt;
&lt;br /&gt;
=== 4.1 Step 1 – Create the Database on VM2 (MariaDB) ===&lt;br /&gt;
Connect to MariaDB and create a new database and user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;VM1-IP&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;VM1-IP&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace &amp;lt;code&amp;gt;VM1-IP&amp;lt;/code&amp;gt; with the internal Hyper‑V IP of the web server.&lt;br /&gt;
&lt;br /&gt;
=== 4.2 Step 2 – Install MediaWiki on VM1 (Web Layer) ===&lt;br /&gt;
Download and extract MediaWiki 1.43 LTS:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.0.tar.gz&lt;br /&gt;
tar -xvzf mediawiki-1.43.0.tar.gz&lt;br /&gt;
mv mediawiki-1.43.0 newMW&lt;br /&gt;
chown -R apache:apache /var/www/newMW&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.3 Step 3 – Create an Apache vhost on VM1 ===&lt;br /&gt;
Create a new vhost file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newwiki.example.com&lt;br /&gt;
    DocumentRoot /var/www/newMW&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newMW&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newwiki-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newwiki-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Step 4 – Add a Reverse‑Proxy Entry on VM3 (nginx) ===&lt;br /&gt;
Duplicate an existing server block and adjust only the domain and proxy target:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name newwiki.example.com;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://VM1-IP:8080;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.5 Step 5 – Run the MediaWiki Installer ===&lt;br /&gt;
Navigate to:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
https://newwiki.example.com&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Provide:&lt;br /&gt;
* Database: &amp;lt;code&amp;gt;newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
* User: &amp;lt;code&amp;gt;newwikiuser&amp;lt;/code&amp;gt;&lt;br /&gt;
* Password: (as created)&lt;br /&gt;
* Site name&lt;br /&gt;
* Admin account&lt;br /&gt;
&lt;br /&gt;
Download &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; and place it in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newMW/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.6 Step 6 – Minimal Configuration in LocalSettings.php ===&lt;br /&gt;
Add or verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
$wgScriptPath = &amp;quot;&amp;quot;;&lt;br /&gt;
$wgArticlePath = &amp;quot;/wiki/$1&amp;quot;;&lt;br /&gt;
$wgUsePathInfo = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable uploads if needed:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgEnableUploads = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install SyntaxHighlight:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wfLoadExtension( &#039;SyntaxHighlight_GeSHi&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Verification Checklist ==&lt;br /&gt;
* &amp;lt;code&amp;gt;https://newwiki.example.com&amp;lt;/code&amp;gt; loads correctly.&lt;br /&gt;
* &amp;lt;code&amp;gt;/wiki/Main_Page&amp;lt;/code&amp;gt; resolves without errors.&lt;br /&gt;
* Editing works.&lt;br /&gt;
* File uploads work (if enabled).&lt;br /&gt;
* No rewrite errors in Apache logs.&lt;br /&gt;
* nginx logs show correct proxying.&lt;br /&gt;
* MariaDB logs show successful connections from VM1.&lt;br /&gt;
&lt;br /&gt;
== 6. Summary ==&lt;br /&gt;
Adding a new MediaWiki instance is straightforward because:&lt;br /&gt;
* nginx handles only routing.&lt;br /&gt;
* Apache handles rewrites and vhosts.&lt;br /&gt;
* MariaDB handles isolated databases.&lt;br /&gt;
* Each VM has a clear, stable role.&lt;br /&gt;
* The existing configuration already supports multi‑site operation.&lt;br /&gt;
&lt;br /&gt;
This document provides a reproducible procedure for extending the system&lt;br /&gt;
without reintroducing the complexity or debugging effort experienced during&lt;br /&gt;
the initial setup.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_Clean_installation_checklist_version_1.43_LTS&amp;diff=1605</id>
		<title>ICT:MW Clean installation checklist version 1.43 LTS</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_Clean_installation_checklist_version_1.43_LTS&amp;diff=1605"/>
		<updated>2026-04-03T11:07:58Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Installation Checklist – MediaWiki 1.43 LTS = This document provides a clean, reproducible installation procedure for MediaWiki 1.43 LTS on AlmaLinux with Apache, PHP, MariaDB, and an nginx reverse-proxy. It is designed for long-term stability and successor-friendly maintenance.  == 1. Preparation == Before installing MediaWiki, ensure the server environment is ready.  === 1.1 System Requirements === * AlmaLinux (or compatible RHEL-based system) * Apache (httpd) * PH...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Installation Checklist – MediaWiki 1.43 LTS =&lt;br /&gt;
This document provides a clean, reproducible installation procedure for&lt;br /&gt;
MediaWiki 1.43 LTS on AlmaLinux with Apache, PHP, MariaDB, and an nginx&lt;br /&gt;
reverse-proxy. It is designed for long-term stability and successor-friendly&lt;br /&gt;
maintenance.&lt;br /&gt;
&lt;br /&gt;
== 1. Preparation ==&lt;br /&gt;
Before installing MediaWiki, ensure the server environment is ready.&lt;br /&gt;
&lt;br /&gt;
=== 1.1 System Requirements ===&lt;br /&gt;
* AlmaLinux (or compatible RHEL-based system)&lt;br /&gt;
* Apache (httpd)&lt;br /&gt;
* PHP 8.1 or later&lt;br /&gt;
* MariaDB 10.5 or later&lt;br /&gt;
* nginx reverse-proxy (optional but recommended)&lt;br /&gt;
* SELinux/AppArmor configured or disabled as needed&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Install Required Packages ===&lt;br /&gt;
Install PHP and required extensions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dnf install php php-mbstring php-xml php-intl php-mysqlnd php-gd php-json php-cli php-opcache php-apcu&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install Apache and MariaDB:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dnf install httpd mariadb-server&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable and start services:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl enable --now httpd mariadb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Database Setup ==&lt;br /&gt;
Create a clean database for the new wiki.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Secure MariaDB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql_secure_installation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Create Database and User ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DATABASE newwiki CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;&lt;br /&gt;
CREATE USER &#039;newwikiuser&#039;@&#039;localhost&#039; IDENTIFIED BY &#039;strongpassword&#039;;&lt;br /&gt;
GRANT ALL PRIVILEGES ON newwiki.* TO &#039;newwikiuser&#039;@&#039;localhost&#039;;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3. Download MediaWiki 1.43 LTS ==&lt;br /&gt;
Download and extract the LTS release.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
wget https://releases.wikimedia.org/mediawiki/1.43/mediawiki-1.43.0.tar.gz&lt;br /&gt;
tar -xvzf mediawiki-1.43.0.tar.gz&lt;br /&gt;
mv mediawiki-1.43.0 newwiki&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set permissions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chown -R apache:apache /var/www/newwiki&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4. Apache Virtual Host ==&lt;br /&gt;
Create a dedicated vhost for the new wiki.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:8080&amp;gt;&lt;br /&gt;
    ServerName newwiki.example.com&lt;br /&gt;
    DocumentRoot /var/www/newwiki&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;Directory /var/www/newwiki&amp;gt;&lt;br /&gt;
        AllowOverride All&lt;br /&gt;
        Require all granted&lt;br /&gt;
    &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    ErrorLog /var/log/httpd/newwiki-error.log&lt;br /&gt;
    CustomLog /var/log/httpd/newwiki-access.log combined&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enable the site and restart Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. nginx Reverse-Proxy ==&lt;br /&gt;
Duplicate the working configuration from the old wiki.&lt;br /&gt;
&lt;br /&gt;
Only change:&lt;br /&gt;
* &amp;lt;code&amp;gt;server_name&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;proxy_pass&amp;lt;/code&amp;gt; target&lt;br /&gt;
* SSL certificate paths&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name newwiki.example.com;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://127.0.0.1:8080;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. Run the MediaWiki Installer ==&lt;br /&gt;
Navigate to:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
https://newwiki.example.com&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Provide:&lt;br /&gt;
* Database name: &amp;lt;code&amp;gt;newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
* Database user: &amp;lt;code&amp;gt;newwikiuser&amp;lt;/code&amp;gt;&lt;br /&gt;
* Database password&lt;br /&gt;
* Site name&lt;br /&gt;
* Admin account&lt;br /&gt;
&lt;br /&gt;
Download the generated &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt; and place it in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/var/www/newwiki/LocalSettings.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7. Minimal Post-Install Configuration ==&lt;br /&gt;
Add only the essential customizations.&lt;br /&gt;
&lt;br /&gt;
=== 7.1 URL Structure ===&lt;br /&gt;
Ensure these lines exist:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgServer = &amp;quot;https://newwiki.example.com&amp;quot;;&lt;br /&gt;
$wgScriptPath = &amp;quot;&amp;quot;;&lt;br /&gt;
$wgArticlePath = &amp;quot;/wiki/$1&amp;quot;;&lt;br /&gt;
$wgUsePathInfo = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.2 Enable File Uploads (optional) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$wgEnableUploads = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.3 Install SyntaxHighlight (recommended) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wfLoadExtension( &#039;SyntaxHighlight_GeSHi&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7.4 Create Custom Namespaces (optional) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define(&amp;quot;NS_TECH&amp;quot;, 3000);&lt;br /&gt;
define(&amp;quot;NS_TECH_TALK&amp;quot;, 3001);&lt;br /&gt;
$wgExtraNamespaces[NS_TECH] = &amp;quot;Tech&amp;quot;;&lt;br /&gt;
$wgNamespacesWithSubpages[NS_TECH] = true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 8. Testing Checklist ==&lt;br /&gt;
Verify:&lt;br /&gt;
* &amp;lt;code&amp;gt;/wiki/Main_Page&amp;lt;/code&amp;gt; loads correctly&lt;br /&gt;
* Editing works&lt;br /&gt;
* File uploads work (if enabled)&lt;br /&gt;
* Categories work&lt;br /&gt;
* Subpages work in custom namespaces&lt;br /&gt;
* Reverse-proxy preserves Host header&lt;br /&gt;
* No rewrite errors in Apache logs&lt;br /&gt;
&lt;br /&gt;
== 9. Backup Checklist ==&lt;br /&gt;
Create a backup routine for:&lt;br /&gt;
* &amp;lt;code&amp;gt;/var/www/newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;LocalSettings.php&amp;lt;/code&amp;gt;&lt;br /&gt;
* MariaDB database &amp;lt;code&amp;gt;newwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 10. Maintenance Notes ==&lt;br /&gt;
* MediaWiki 1.43 is LTS until December 2027.&lt;br /&gt;
* Apply security updates within the 1.43 branch.&lt;br /&gt;
* Avoid unnecessary extensions to keep the system stable.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:MW_new_MediaWiki_Blueprint&amp;diff=1604</id>
		<title>ICT:MW new MediaWiki Blueprint</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:MW_new_MediaWiki_Blueprint&amp;diff=1604"/>
		<updated>2026-04-03T10:40:33Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Blueprint for Organising the New MediaWiki Instance = This document provides a structured approach for building a clean, maintainable, successor-friendly MediaWiki installation dedicated to documenting the Drupal platform, the server environment, and operational procedures.  The goal is to replace the experimental, Cargo-based wiki with a new, purpose-built documentation system that is clear, predictable, and easy to maintain.  == 1. Purpose of the New Wiki == The new...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Blueprint for Organising the New MediaWiki Instance =&lt;br /&gt;
This document provides a structured approach for building a clean, maintainable,&lt;br /&gt;
successor-friendly MediaWiki installation dedicated to documenting the Drupal&lt;br /&gt;
platform, the server environment, and operational procedures.&lt;br /&gt;
&lt;br /&gt;
The goal is to replace the experimental, Cargo-based wiki with a new,&lt;br /&gt;
purpose-built documentation system that is clear, predictable, and easy to&lt;br /&gt;
maintain.&lt;br /&gt;
&lt;br /&gt;
== 1. Purpose of the New Wiki ==&lt;br /&gt;
The new MediaWiki instance will serve as:&lt;br /&gt;
* A technical maintenance manual for Drupal&lt;br /&gt;
* A user documentation space for historians and editors&lt;br /&gt;
* A server operations manual (AlmaLinux, MariaDB, reverse proxy)&lt;br /&gt;
* A stable, structured knowledge base for long-term stewardship&lt;br /&gt;
&lt;br /&gt;
It replaces the experimental wiki, which accumulated Cargo, PageForms,&lt;br /&gt;
temporary namespaces, and unstructured pages.&lt;br /&gt;
&lt;br /&gt;
== 2. High-Level Structure ==&lt;br /&gt;
The new wiki is organised around three pillars:&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Technical Documentation (System Internals) ===&lt;br /&gt;
Covers:&lt;br /&gt;
* Drupal configuration&lt;br /&gt;
* Views, fields, display modes&lt;br /&gt;
* Custom modules and CSS overrides&lt;br /&gt;
* Multilingual configuration&lt;br /&gt;
* Asset architecture&lt;br /&gt;
* Maintenance workflows&lt;br /&gt;
&lt;br /&gt;
=== 2.2 User Documentation (Functional Use) ===&lt;br /&gt;
Covers:&lt;br /&gt;
* How historians use the system&lt;br /&gt;
* How to add/edit content&lt;br /&gt;
* How to attach assets&lt;br /&gt;
* How to use the publisher view&lt;br /&gt;
* How to navigate multilingual content&lt;br /&gt;
&lt;br /&gt;
=== 2.3 Server Documentation (Infrastructure) ===&lt;br /&gt;
Covers:&lt;br /&gt;
* AlmaLinux installation and updates&lt;br /&gt;
* MariaDB backup/restore&lt;br /&gt;
* Reverse proxy configuration&lt;br /&gt;
* Web server layout&lt;br /&gt;
* Deployment procedures&lt;br /&gt;
&lt;br /&gt;
== 3. Namespaces ==&lt;br /&gt;
Namespaces provide top-level grouping. Suggested namespaces:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Tech:&#039;&#039;&#039; – Technical implementation documentation&lt;br /&gt;
* &#039;&#039;&#039;Guide:&#039;&#039;&#039; – User-facing documentation&lt;br /&gt;
* &#039;&#039;&#039;Server:&#039;&#039;&#039; – Server and infrastructure documentation&lt;br /&gt;
* &#039;&#039;&#039;Admin:&#039;&#039;&#039; – Operational procedures, backups, deployments&lt;br /&gt;
&lt;br /&gt;
These names are placeholders; choose names that fit your style.&lt;br /&gt;
&lt;br /&gt;
== 4. Subpage Hierarchy (Pseudo-Folders) ==&lt;br /&gt;
Subpages create a clear, navigable hierarchy.&lt;br /&gt;
&lt;br /&gt;
=== 4.1 Technical Documentation ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Tech/Overview&lt;br /&gt;
Tech/ContentTypes/Places&lt;br /&gt;
Tech/ContentTypes/Objects&lt;br /&gt;
Tech/Views/RecordView&lt;br /&gt;
Tech/Views/AssetChain&lt;br /&gt;
Tech/Modules/CustomWidgets&lt;br /&gt;
Tech/CSS/Overrides&lt;br /&gt;
Tech/Multilingual/Configuration&lt;br /&gt;
Tech/Permissions/Roles&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.2 User Documentation ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Guide/Overview&lt;br /&gt;
Guide/Editing/AddingRecords&lt;br /&gt;
Guide/Editing/Assets&lt;br /&gt;
Guide/Editing/Translations&lt;br /&gt;
Guide/Publishing/RecordView&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.3 Server Documentation ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Server/AlmaLinux/Installation&lt;br /&gt;
Server/AlmaLinux/Updates&lt;br /&gt;
Server/MariaDB/Backup&lt;br /&gt;
Server/MariaDB/Restore&lt;br /&gt;
Server/ReverseProxy/Configuration&lt;br /&gt;
Server/Web/DirectoryLayout&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Admin Procedures ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Admin/Backup/Drupal&lt;br /&gt;
Admin/Backup/MediaWiki&lt;br /&gt;
Admin/Deployment/Drupal&lt;br /&gt;
Admin/Deployment/Server&lt;br /&gt;
Admin/Monitoring/Checks&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Categories ==&lt;br /&gt;
Categories allow cross-cutting grouping across namespaces.&lt;br /&gt;
&lt;br /&gt;
Suggested categories:&lt;br /&gt;
* [[Category:Drupal]]&lt;br /&gt;
* [[Category:Views]]&lt;br /&gt;
* [[Category:Content Types]]&lt;br /&gt;
* [[Category:Server]]&lt;br /&gt;
* [[Category:Maintenance]]&lt;br /&gt;
* [[Category:User Guide]]&lt;br /&gt;
&lt;br /&gt;
Pages can belong to multiple categories.&lt;br /&gt;
&lt;br /&gt;
== 6. Page Template for Consistency ==&lt;br /&gt;
Use a standard template for all technical pages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
= [Page Title] =&lt;br /&gt;
== Purpose ==&lt;br /&gt;
== Where Implemented ==&lt;br /&gt;
== Configuration Summary ==&lt;br /&gt;
== Dependencies ==&lt;br /&gt;
== Interaction With Other Subsystems ==&lt;br /&gt;
== Known Quirks ==&lt;br /&gt;
== How to Modify Safely ==&lt;br /&gt;
== How to Debug ==&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures clarity and successor-friendliness.&lt;br /&gt;
&lt;br /&gt;
== 7. Migration Strategy ==&lt;br /&gt;
The old wiki remains online as an archive.&lt;br /&gt;
&lt;br /&gt;
Migration steps:&lt;br /&gt;
# Identify important pages worth keeping.&lt;br /&gt;
# Copy/paste them into the new wiki.&lt;br /&gt;
# Rewrite them using the new structure and template.&lt;br /&gt;
# Discard Cargo/PageForms-specific content.&lt;br /&gt;
# Delete or ignore experimental pages in the old wiki.&lt;br /&gt;
&lt;br /&gt;
This avoids contamination of the new clean structure.&lt;br /&gt;
&lt;br /&gt;
== 8. Extensions to Install ==&lt;br /&gt;
Keep the new wiki minimal.&lt;br /&gt;
&lt;br /&gt;
Recommended:&lt;br /&gt;
* SyntaxHighlight (for code/config snippets)&lt;br /&gt;
* CategoryTree (optional)&lt;br /&gt;
* VisualEditor (optional)&lt;br /&gt;
&lt;br /&gt;
Avoid:&lt;br /&gt;
* Cargo&lt;br /&gt;
* PageForms&lt;br /&gt;
* Semantic extensions&lt;br /&gt;
&lt;br /&gt;
== 9. Benefits of This Approach ==&lt;br /&gt;
* Clean, predictable structure&lt;br /&gt;
* Easy navigation via namespaces and subpages&lt;br /&gt;
* Clear separation between technical, user, and server documentation&lt;br /&gt;
* No legacy noise from early experiments&lt;br /&gt;
* Easy onboarding for future maintainers&lt;br /&gt;
* Documentation grows in a controlled, organised way&lt;br /&gt;
&lt;br /&gt;
== 10. Conclusion ==&lt;br /&gt;
This blueprint provides a structured foundation for building a new, clean,&lt;br /&gt;
long-term MediaWiki documentation system. It supports the survival of the&lt;br /&gt;
Drupal platform, the server environment, and the operational knowledge needed&lt;br /&gt;
to maintain them.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Maintanance_-_WHAT_to_document&amp;diff=1603</id>
		<title>ICT:D Maintanance - WHAT to document</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Maintanance_-_WHAT_to_document&amp;diff=1603"/>
		<updated>2026-04-03T10:18:59Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= What to Document in the Drupal Implementation and Why = This document explains what should be documented in the technical wiki and why it is essential for long-term maintenance, debugging, and successor friendliness.  == 1. Why Documentation Is Needed == As the system grows, it becomes impossible to remember: * which View controls which output * which field is translatable * which form mode hides which fields * which CSS hides which buttons * which custom module alters...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= What to Document in the Drupal Implementation and Why =&lt;br /&gt;
This document explains what should be documented in the technical wiki and&lt;br /&gt;
why it is essential for long-term maintenance, debugging, and successor&lt;br /&gt;
friendliness.&lt;br /&gt;
&lt;br /&gt;
== 1. Why Documentation Is Needed ==&lt;br /&gt;
As the system grows, it becomes impossible to remember:&lt;br /&gt;
* which View controls which output&lt;br /&gt;
* which field is translatable&lt;br /&gt;
* which form mode hides which fields&lt;br /&gt;
* which CSS hides which buttons&lt;br /&gt;
* which custom module alters which form&lt;br /&gt;
* which display mode is used where&lt;br /&gt;
&lt;br /&gt;
Without documentation, debugging becomes guesswork.&lt;br /&gt;
&lt;br /&gt;
== 2. What Must Be Documented ==&lt;br /&gt;
The following categories cover the entire Drupal implementation.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Content Types ===&lt;br /&gt;
For each content type:&lt;br /&gt;
* Fields (name, type, translatability)&lt;br /&gt;
* Form display (widgets, hidden fields)&lt;br /&gt;
* Display modes (default, teaser, publisher view)&lt;br /&gt;
* Special behaviors (auto-generated values, computed fields)&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Views ===&lt;br /&gt;
Create a master index listing:&lt;br /&gt;
* View name&lt;br /&gt;
* Purpose&lt;br /&gt;
* Displays (page, block, feed)&lt;br /&gt;
* Contextual filters&lt;br /&gt;
* Relationships&lt;br /&gt;
* Where it is used (menu, block, link)&lt;br /&gt;
&lt;br /&gt;
Each View gets its own technical page using the template.&lt;br /&gt;
&lt;br /&gt;
=== 2.3 Custom Modules ===&lt;br /&gt;
Document:&lt;br /&gt;
* What each module does&lt;br /&gt;
* Which hooks it implements&lt;br /&gt;
* Which forms it alters&lt;br /&gt;
* Which fields it touches&lt;br /&gt;
* Which Views it interacts with&lt;br /&gt;
&lt;br /&gt;
This prevents “mystery behavior”.&lt;br /&gt;
&lt;br /&gt;
=== 2.4 CSS Overrides ===&lt;br /&gt;
List:&lt;br /&gt;
* File name&lt;br /&gt;
* Selectors used&lt;br /&gt;
* Purpose of each rule&lt;br /&gt;
&lt;br /&gt;
CSS is easy to forget and hard to debug.&lt;br /&gt;
&lt;br /&gt;
=== 2.5 Multilingual Configuration ===&lt;br /&gt;
Document:&lt;br /&gt;
* Which fields are translatable&lt;br /&gt;
* Which are shared&lt;br /&gt;
* How translation workflow works&lt;br /&gt;
* Which tabs are hidden&lt;br /&gt;
* Language negotiation settings&lt;br /&gt;
&lt;br /&gt;
This prevents multilingual chaos.&lt;br /&gt;
&lt;br /&gt;
=== 2.6 Permissions and Roles ===&lt;br /&gt;
Document:&lt;br /&gt;
* Which role can edit what&lt;br /&gt;
* Which role can translate&lt;br /&gt;
* Which role can see publisher view&lt;br /&gt;
* Which role can manage assets&lt;br /&gt;
&lt;br /&gt;
Permissions often explain “why something is missing”.&lt;br /&gt;
&lt;br /&gt;
=== 2.7 Asset Architecture ===&lt;br /&gt;
Document:&lt;br /&gt;
* How assets are linked to records&lt;br /&gt;
* Which View displays them&lt;br /&gt;
* How the asset-chain works&lt;br /&gt;
* How the modal behaves (if any)&lt;br /&gt;
&lt;br /&gt;
=== 2.8 UI Customizations ===&lt;br /&gt;
Document:&lt;br /&gt;
* Hidden buttons&lt;br /&gt;
* Hidden tabs&lt;br /&gt;
* Custom form modes&lt;br /&gt;
* Custom display modes&lt;br /&gt;
&lt;br /&gt;
These are easy to forget and hard to rediscover.&lt;br /&gt;
&lt;br /&gt;
== 3. How to Organize the Documentation ==&lt;br /&gt;
Recommended structure:&lt;br /&gt;
&lt;br /&gt;
* /Technical/Content Types/&lt;br /&gt;
* /Technical/Views/&lt;br /&gt;
* /Technical/Modules/&lt;br /&gt;
* /Technical/CSS/&lt;br /&gt;
* /Technical/Multilingual/&lt;br /&gt;
* /Technical/Permissions/&lt;br /&gt;
* /Technical/Assets/&lt;br /&gt;
* /Technical/Record View/&lt;br /&gt;
&lt;br /&gt;
Each page uses the same template for consistency.&lt;br /&gt;
&lt;br /&gt;
== 4. When to Document ==&lt;br /&gt;
Document immediately after implementing a feature:&lt;br /&gt;
* not the design&lt;br /&gt;
* not the intention&lt;br /&gt;
* but the actual implementation&lt;br /&gt;
&lt;br /&gt;
This prevents forgetting crucial details.&lt;br /&gt;
&lt;br /&gt;
== 5. Benefits ==&lt;br /&gt;
* Faster debugging&lt;br /&gt;
* Clear overview of system behavior&lt;br /&gt;
* Successor-friendly architecture&lt;br /&gt;
* Reduced risk of breaking workflows&lt;br /&gt;
* Confidence when making changes&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_-_Maintenance_TEMPLATE&amp;diff=1602</id>
		<title>ICT:D - Maintenance TEMPLATE</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_-_Maintenance_TEMPLATE&amp;diff=1602"/>
		<updated>2026-04-03T10:17:37Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= [Subsystem Name] – Technical Implementation Documentation = This page documents the *actual implementation* of this subsystem in Drupal. It is not about design intentions, but about how the system is configured, where it lives, and how to maintain or debug it.  == 1. Purpose == Describe in 2–3 lines what this subsystem is supposed to do. Example: * Provides a read-only publisher view of a record. * Displays the asset table for the selected node.  == 2. Where It Is...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= [Subsystem Name] – Technical Implementation Documentation =&lt;br /&gt;
This page documents the *actual implementation* of this subsystem in Drupal.&lt;br /&gt;
It is not about design intentions, but about how the system is configured,&lt;br /&gt;
where it lives, and how to maintain or debug it.&lt;br /&gt;
&lt;br /&gt;
== 1. Purpose ==&lt;br /&gt;
Describe in 2–3 lines what this subsystem is supposed to do.&lt;br /&gt;
Example:&lt;br /&gt;
* Provides a read-only publisher view of a record.&lt;br /&gt;
* Displays the asset table for the selected node.&lt;br /&gt;
&lt;br /&gt;
== 2. Where It Is Implemented ==&lt;br /&gt;
List exact Drupal UI locations and file paths.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Drupal UI Locations ===&lt;br /&gt;
* Content type: &amp;lt;code&amp;gt;[Content Type Name]&amp;lt;/code&amp;gt;&lt;br /&gt;
* Manage fields: &amp;lt;code&amp;gt;[Field Name]&amp;lt;/code&amp;gt;&lt;br /&gt;
* Manage form display: &amp;lt;code&amp;gt;[Form Mode]&amp;lt;/code&amp;gt;&lt;br /&gt;
* Manage display: &amp;lt;code&amp;gt;[View Mode]&amp;lt;/code&amp;gt;&lt;br /&gt;
* View: &amp;lt;code&amp;gt;[View Name] → [Display Name]&amp;lt;/code&amp;gt;&lt;br /&gt;
* Block placement: &amp;lt;code&amp;gt;[Theme] → [Region]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Custom Code Locations (if any) ===&lt;br /&gt;
* Module: &amp;lt;code&amp;gt;custom_[module_name]&amp;lt;/code&amp;gt;&lt;br /&gt;
* File: &amp;lt;code&amp;gt;src/Plugin/.../...&amp;lt;/code&amp;gt;&lt;br /&gt;
* Hook: &amp;lt;code&amp;gt;hook_form_alter()&amp;lt;/code&amp;gt; or similar&lt;br /&gt;
&lt;br /&gt;
=== 2.3 CSS Overrides (if any) ===&lt;br /&gt;
* File: &amp;lt;code&amp;gt;themes/custom/[theme]/css/[file].css&amp;lt;/code&amp;gt;&lt;br /&gt;
* Selectors used:&lt;br /&gt;
  * &amp;lt;code&amp;gt;.some-selector { display: none; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3. Configuration Summary ==&lt;br /&gt;
Bullet list of the important settings.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
* Field &amp;lt;code&amp;gt;field_assets&amp;lt;/code&amp;gt; is NOT translatable.&lt;br /&gt;
* View uses contextual filter &amp;lt;code&amp;gt;Content: Nid&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Display mode “Publisher view” hides metadata fields.&lt;br /&gt;
&lt;br /&gt;
== 4. Dependencies ==&lt;br /&gt;
List modules, fields, or other subsystems this depends on.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
* Depends on: Views, Media, Asset-chain View.&lt;br /&gt;
* Requires field &amp;lt;code&amp;gt;field_assets&amp;lt;/code&amp;gt; to exist.&lt;br /&gt;
&lt;br /&gt;
== 5. Interaction With Other Subsystems ==&lt;br /&gt;
Explain how this subsystem connects to others.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
* The asset table is embedded using the Asset-chain View.&lt;br /&gt;
* The admin listing links to this view via a custom URL.&lt;br /&gt;
&lt;br /&gt;
== 6. Known Quirks ==&lt;br /&gt;
Document anything Drupal does that is “special”.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
* Contextual filter fails if node is unpublished.&lt;br /&gt;
* Translation tab appears even though no fields are translatable.&lt;br /&gt;
&lt;br /&gt;
== 7. How to Modify It Safely ==&lt;br /&gt;
Step-by-step instructions for future changes.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
# Edit the View “Record View – Places”.&lt;br /&gt;
# Do NOT change the contextual filter.&lt;br /&gt;
# Add new fields only at the bottom of the field list.&lt;br /&gt;
&lt;br /&gt;
== 8. How to Debug ==&lt;br /&gt;
Where to look when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
* If assets do not appear → check relationship in the View.&lt;br /&gt;
* If the page is empty → check contextual filter.&lt;br /&gt;
* If translation behaves oddly → check field translatability.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Publish_ready_view_of_a_record&amp;diff=1601</id>
		<title>ICT:D Publish ready view of a record</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Publish_ready_view_of_a_record&amp;diff=1601"/>
		<updated>2026-04-03T10:01:55Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Publisher-Ready Single Record View (Drupal UI Only) = This document describes how to create a clean, read-only “record view” for any content type. The goal is to provide publishers with a page that shows:  * The most important fields of the record * A full description text * A table of attached assets * Links to the asset-chain view * No edit buttons, no tabs, no clutter  All steps use Drupal UI only. No custom code is required.  == 1. Create a New View for Single-...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Publisher-Ready Single Record View (Drupal UI Only) =&lt;br /&gt;
This document describes how to create a clean, read-only “record view” for any&lt;br /&gt;
content type. The goal is to provide publishers with a page that shows:&lt;br /&gt;
&lt;br /&gt;
* The most important fields of the record&lt;br /&gt;
* A full description text&lt;br /&gt;
* A table of attached assets&lt;br /&gt;
* Links to the asset-chain view&lt;br /&gt;
* No edit buttons, no tabs, no clutter&lt;br /&gt;
&lt;br /&gt;
All steps use Drupal UI only. No custom code is required.&lt;br /&gt;
&lt;br /&gt;
== 1. Create a New View for Single-Record Display ==&lt;br /&gt;
Navigate to:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Structure → Views → Add view&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Configure the new View:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;View name:&#039;&#039;&#039; Record View – [Your Content Type]&lt;br /&gt;
* &#039;&#039;&#039;Show:&#039;&#039;&#039; Content&lt;br /&gt;
* &#039;&#039;&#039;Of type:&#039;&#039;&#039; Your content type&lt;br /&gt;
* &#039;&#039;&#039;Create a page:&#039;&#039;&#039; Yes&lt;br /&gt;
* &#039;&#039;&#039;Page path:&#039;&#039;&#039; &amp;lt;code&amp;gt;/record-view/%&amp;lt;/code&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Page display:&#039;&#039;&#039; Unformatted list (or Fields)&lt;br /&gt;
* &#039;&#039;&#039;Items per page:&#039;&#039;&#039; 1&lt;br /&gt;
* &#039;&#039;&#039;Menu:&#039;&#039;&#039; None&lt;br /&gt;
* &#039;&#039;&#039;Pager:&#039;&#039;&#039; None&lt;br /&gt;
&lt;br /&gt;
This creates a page that will display exactly one record.&lt;br /&gt;
&lt;br /&gt;
== 2. Add a Contextual Filter to Load the Correct Record ==&lt;br /&gt;
In the View:&lt;br /&gt;
&lt;br /&gt;
1. Add contextual filter: &#039;&#039;&#039;Content: Nid&#039;&#039;&#039;&lt;br /&gt;
2. Configure it as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
When the filter value is NOT in the URL → Display nothing&lt;br /&gt;
When the filter value IS in the URL → Load the corresponding record&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This makes the View show the record whose node ID appears in the URL.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/record-view/123&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
shows record 123.&lt;br /&gt;
&lt;br /&gt;
== 3. Choose Which Fields to Display ==&lt;br /&gt;
Remove all default fields.&lt;br /&gt;
&lt;br /&gt;
Add only the fields you want publishers to see, for example:&lt;br /&gt;
&lt;br /&gt;
* Title&lt;br /&gt;
* Description (full text)&lt;br /&gt;
* Summary&lt;br /&gt;
* Key metadata fields&lt;br /&gt;
* Location&lt;br /&gt;
* Dates&lt;br /&gt;
* Any other important fields&lt;br /&gt;
&lt;br /&gt;
Arrange them in a logical order.&lt;br /&gt;
&lt;br /&gt;
This creates a clean, read-only presentation.&lt;br /&gt;
&lt;br /&gt;
== 4. Add the Attached Assets Table ==&lt;br /&gt;
There are two possible methods.&lt;br /&gt;
&lt;br /&gt;
=== 4.1 Method A: Using a Relationship (if assets are referenced directly) ===&lt;br /&gt;
If your content type has a media reference field:&lt;br /&gt;
&lt;br /&gt;
1. Add relationship: &#039;&#039;&#039;Content → [Your Media Field]&#039;&#039;&#039;&lt;br /&gt;
2. Add fields from the Media entity:&lt;br /&gt;
   * Media name&lt;br /&gt;
   * Media type&lt;br /&gt;
   * File URL&lt;br /&gt;
   * Thumbnail&lt;br /&gt;
3. Set the View format to &#039;&#039;&#039;Table&#039;&#039;&#039;&lt;br /&gt;
4. Group or sort as needed&lt;br /&gt;
&lt;br /&gt;
This produces a table of assets under the record.&lt;br /&gt;
&lt;br /&gt;
=== 4.2 Method B: Embedding Your Existing Asset-Chain View ===&lt;br /&gt;
If you already have a View that lists assets for a record:&lt;br /&gt;
&lt;br /&gt;
1. Add a new field: &#039;&#039;&#039;View field&#039;&#039;&#039;&lt;br /&gt;
2. Select your asset-chain View&lt;br /&gt;
3. Pass the node ID as contextual filter:&lt;br /&gt;
   * Use token: &amp;lt;code&amp;gt;{{ nid }}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This embeds the asset-chain table directly into the record view.&lt;br /&gt;
&lt;br /&gt;
== 5. Add a Link from the Content Overview Table ==&lt;br /&gt;
Navigate to:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Structure → Views → Content&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the admin listing of all records.&lt;br /&gt;
&lt;br /&gt;
Add a new field:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Global: Custom text&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Configure it:&lt;br /&gt;
&lt;br /&gt;
1. Enable “Rewrite results”&lt;br /&gt;
2. Use custom text such as:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;/record-view/{{ nid }}&amp;quot;&amp;gt;View&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
3. Enable “Use replacement tokens”&lt;br /&gt;
4. Insert the token for node ID&lt;br /&gt;
&lt;br /&gt;
This adds a “View” link next to each record.&lt;br /&gt;
&lt;br /&gt;
== 6. Optional: Show the View Link Only for Completed Records ==&lt;br /&gt;
If you have a boolean field “Completed”:&lt;br /&gt;
&lt;br /&gt;
1. Add the field to the admin View&lt;br /&gt;
2. In the “View” link field:&lt;br /&gt;
   * Enable “Rewrite results”&lt;br /&gt;
   * Add a condition:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% if completed == &#039;1&#039; %}&lt;br /&gt;
  &amp;lt;a href=&amp;quot;/record-view/{{ nid }}&amp;quot;&amp;gt;View&amp;lt;/a&amp;gt;&lt;br /&gt;
{% endif %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures only finished records show the publisher view link.&lt;br /&gt;
&lt;br /&gt;
== 7. Result ==&lt;br /&gt;
You now have:&lt;br /&gt;
&lt;br /&gt;
* A clean, read-only page for each record&lt;br /&gt;
* A full description field&lt;br /&gt;
* A table of attached assets&lt;br /&gt;
* Links to the asset-chain&lt;br /&gt;
* A link from the admin table to this page&lt;br /&gt;
* No edit buttons, no tabs, no clutter&lt;br /&gt;
* A publisher-ready output suitable for drafting publications or website pages&lt;br /&gt;
&lt;br /&gt;
== 8. Notes ==&lt;br /&gt;
* This solution uses only Drupal UI.&lt;br /&gt;
* No custom code, no theming, no templates.&lt;br /&gt;
* Works for multilingual sites.&lt;br /&gt;
* Works for all content types.&lt;br /&gt;
* Fully successor-friendly.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Multilingual_aspect&amp;diff=1600</id>
		<title>ICT:D Multilingual aspect</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Multilingual_aspect&amp;diff=1600"/>
		<updated>2026-04-03T09:48:54Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Drupal Multilingual Architecture: What Appears, What Is Possible, and How Translation Works =  This document summarizes the changes that appear after enabling multilingual support in Drupal, and explains how both *foreground* (content) and *background* (configuration/UI) translation works. It also identifies the source of new interface clutter and what can be simplified later.  == 1. What Appears After Enabling Multilingual == Enabling multilingual introduces several n...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Drupal Multilingual Architecture: What Appears, What Is Possible, and How Translation Works =&lt;br /&gt;
&lt;br /&gt;
This document summarizes the changes that appear after enabling multilingual&lt;br /&gt;
support in Drupal, and explains how both *foreground* (content) and *background*&lt;br /&gt;
(configuration/UI) translation works. It also identifies the source of new&lt;br /&gt;
interface clutter and what can be simplified later.&lt;br /&gt;
&lt;br /&gt;
== 1. What Appears After Enabling Multilingual ==&lt;br /&gt;
Enabling multilingual introduces several new subsystems:&lt;br /&gt;
&lt;br /&gt;
=== 1.1 Language Negotiation ===&lt;br /&gt;
Drupal determines the language of each page using:&lt;br /&gt;
* URL prefix (e.g., /en, /nl)&lt;br /&gt;
* User preference&lt;br /&gt;
* Browser preference&lt;br /&gt;
* Session&lt;br /&gt;
* Default site language&lt;br /&gt;
&lt;br /&gt;
This affects how content is displayed and which translations are loaded.&lt;br /&gt;
&lt;br /&gt;
=== 1.2 Translatable Fields ===&lt;br /&gt;
Every field can be marked as:&lt;br /&gt;
* Translatable&lt;br /&gt;
* Not translatable&lt;br /&gt;
&lt;br /&gt;
If a field is translatable, Drupal creates a separate value per language.&lt;br /&gt;
&lt;br /&gt;
This causes:&lt;br /&gt;
* Additional language selectors&lt;br /&gt;
* Additional translation tabs&lt;br /&gt;
* Duplicate fieldsets in the edit form&lt;br /&gt;
&lt;br /&gt;
=== 1.3 Translation Tabs on Nodes ===&lt;br /&gt;
Each node now shows:&lt;br /&gt;
* &#039;&#039;&#039;Edit&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Translate&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Revisions&#039;&#039;&#039;&lt;br /&gt;
* Language-specific edit pages (e.g., “Edit (Dutch)”)&lt;br /&gt;
&lt;br /&gt;
This is normal but creates visual clutter.&lt;br /&gt;
&lt;br /&gt;
=== 1.4 Interface Translation (UI Strings) ===&lt;br /&gt;
Drupal adds:&lt;br /&gt;
* A new “Translate Interface” screen&lt;br /&gt;
* A new “Languages” configuration page&lt;br /&gt;
* Automatic import of .po files for core and modules&lt;br /&gt;
&lt;br /&gt;
This handles translation of system text, labels, and UI strings.&lt;br /&gt;
&lt;br /&gt;
== 2. What Is Possible in Multilingual Drupal ==&lt;br /&gt;
Drupal supports two major translation domains:&lt;br /&gt;
&lt;br /&gt;
=== 2.1 Foreground Translation (Content Translation) ===&lt;br /&gt;
This includes:&lt;br /&gt;
* Node titles&lt;br /&gt;
* Body text&lt;br /&gt;
* Field values&lt;br /&gt;
* Media fields&lt;br /&gt;
* Taxonomy terms (if enabled)&lt;br /&gt;
* Custom entities (if enabled)&lt;br /&gt;
&lt;br /&gt;
Each language gets its own version of the content.&lt;br /&gt;
&lt;br /&gt;
=== 2.2 Background Translation (Configuration Translation) ===&lt;br /&gt;
This includes:&lt;br /&gt;
* Field labels&lt;br /&gt;
* View labels&lt;br /&gt;
* Menu items&lt;br /&gt;
* Block titles&lt;br /&gt;
* Interface strings&lt;br /&gt;
* System messages&lt;br /&gt;
&lt;br /&gt;
These translations are stored in configuration, not in content.&lt;br /&gt;
&lt;br /&gt;
=== 2.3 Mixed Translation (Shared vs. Per-Language Fields) ===&lt;br /&gt;
Fields can be configured as:&lt;br /&gt;
* &#039;&#039;&#039;Translatable&#039;&#039;&#039; → separate value per language&lt;br /&gt;
* &#039;&#039;&#039;Non-translatable&#039;&#039;&#039; → shared across all languages&lt;br /&gt;
&lt;br /&gt;
Choosing correctly reduces clutter.&lt;br /&gt;
&lt;br /&gt;
== 3. How Foreground (Content) Translation Works ==&lt;br /&gt;
Content translation creates a separate “translation set” for each node.&lt;br /&gt;
&lt;br /&gt;
=== 3.1 Workflow ===&lt;br /&gt;
1. Create the node in the default language.&lt;br /&gt;
2. Click “Translate”.&lt;br /&gt;
3. Add a translation (e.g., Dutch).&lt;br /&gt;
4. Only translatable fields appear for translation.&lt;br /&gt;
5. Non-translatable fields are shared.&lt;br /&gt;
&lt;br /&gt;
=== 3.2 Storage Model ===&lt;br /&gt;
Each translation is stored as:&lt;br /&gt;
* A separate revision&lt;br /&gt;
* A separate language entry&lt;br /&gt;
* Linked to the same node ID&lt;br /&gt;
&lt;br /&gt;
=== 3.3 Editor Experience ===&lt;br /&gt;
Editors see:&lt;br /&gt;
* A language selector&lt;br /&gt;
* A “Translate” tab&lt;br /&gt;
* Additional vertical tabs for language settings&lt;br /&gt;
&lt;br /&gt;
== 4. How Background (Configuration/UI) Translation Works ==&lt;br /&gt;
Configuration translation handles:&lt;br /&gt;
* Field labels&lt;br /&gt;
* View labels&lt;br /&gt;
* Block titles&lt;br /&gt;
* Menu items&lt;br /&gt;
* Interface strings&lt;br /&gt;
&lt;br /&gt;
=== 4.1 Where It Happens ===&lt;br /&gt;
Go to:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuration → Regional and Language → Configuration translation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.2 How It Works ===&lt;br /&gt;
Drupal extracts all translatable strings from:&lt;br /&gt;
* YAML configuration&lt;br /&gt;
* Module-provided strings&lt;br /&gt;
* Theme templates&lt;br /&gt;
&lt;br /&gt;
You translate them once; they apply everywhere.&lt;br /&gt;
&lt;br /&gt;
== 5. Why the Edit Form Now Looks Cluttered ==&lt;br /&gt;
Multilingual mode adds:&lt;br /&gt;
* A “Language” vertical tab&lt;br /&gt;
* A “Translate” tab&lt;br /&gt;
* A language selector on the edit form&lt;br /&gt;
* Duplicate fieldsets for translatable fields&lt;br /&gt;
* Additional metadata fields per language&lt;br /&gt;
* Extra tabs for each translation&lt;br /&gt;
&lt;br /&gt;
This is normal but overwhelming.&lt;br /&gt;
&lt;br /&gt;
== 6. What Can Be Simplified Later ==&lt;br /&gt;
After understanding the behavior, you can safely:&lt;br /&gt;
&lt;br /&gt;
=== 6.1 Hide Unneeded Tabs ===&lt;br /&gt;
Using:&lt;br /&gt;
* Form modes&lt;br /&gt;
* Permissions&lt;br /&gt;
* hook_form_alter()&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
* Hide “Authoring information”&lt;br /&gt;
* Hide “Promotion options”&lt;br /&gt;
* Hide “URL alias”&lt;br /&gt;
* Hide “Menu settings”&lt;br /&gt;
&lt;br /&gt;
=== 6.2 Make Only a Few Fields Translatable ===&lt;br /&gt;
This reduces 70% of the clutter.&lt;br /&gt;
&lt;br /&gt;
Typical fields to translate:&lt;br /&gt;
* Title&lt;br /&gt;
* Body&lt;br /&gt;
* Summary&lt;br /&gt;
* A few descriptive fields&lt;br /&gt;
&lt;br /&gt;
Fields that usually stay shared:&lt;br /&gt;
* Images&lt;br /&gt;
* Dates&lt;br /&gt;
* Geolocation&lt;br /&gt;
* Internal metadata&lt;br /&gt;
&lt;br /&gt;
=== 6.3 Simplify the Translation Workflow ===&lt;br /&gt;
Options:&lt;br /&gt;
* Remove the “Translate” tab&lt;br /&gt;
* Use inline translation only&lt;br /&gt;
* Limit languages per content type&lt;br /&gt;
&lt;br /&gt;
== 7. Summary ==&lt;br /&gt;
Enabling multilingual introduces:&lt;br /&gt;
* New tabs&lt;br /&gt;
* New field behaviors&lt;br /&gt;
* New translation workflows&lt;br /&gt;
* More UI elements&lt;br /&gt;
&lt;br /&gt;
This is expected. Once the behavior is understood, the interface can be&lt;br /&gt;
cleaned up significantly by:&lt;br /&gt;
* Reducing translatable fields&lt;br /&gt;
* Hiding unnecessary tabs&lt;br /&gt;
* Simplifying the translation workflow&lt;br /&gt;
&lt;br /&gt;
This results in a clean, historian-friendly interface with minimal clutter.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Multilingual_Setup&amp;diff=1598</id>
		<title>ICT:D Multilingual Setup</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Multilingual_Setup&amp;diff=1598"/>
		<updated>2026-04-02T16:29:42Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Drupal Multilingual Setup Workflow (English → Dutch) =  == Overview == This guide explains how to safely add Dutch to an existing English Drupal site.  Steps: # Add Dutch language # Enable translation modules # Make content translatable # Configure fields # Add language switcher # Test with real content  ----  == Step 1 — Add Dutch language ==  Go to:  Configuration → Regional and language → Languages  * Click &amp;#039;&amp;#039;&amp;#039;Add language&amp;#039;&amp;#039;&amp;#039; * Select: &amp;#039;&amp;#039;&amp;#039;Dutch (Nederlands)&amp;#039;...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Drupal Multilingual Setup Workflow (English → Dutch) =&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
This guide explains how to safely add Dutch to an existing English Drupal site.&lt;br /&gt;
&lt;br /&gt;
Steps:&lt;br /&gt;
# Add Dutch language&lt;br /&gt;
# Enable translation modules&lt;br /&gt;
# Make content translatable&lt;br /&gt;
# Configure fields&lt;br /&gt;
# Add language switcher&lt;br /&gt;
# Test with real content&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 1 — Add Dutch language ==&lt;br /&gt;
&lt;br /&gt;
Go to:&lt;br /&gt;
 Configuration → Regional and language → Languages&lt;br /&gt;
&lt;br /&gt;
* Click &#039;&#039;&#039;Add language&#039;&#039;&#039;&lt;br /&gt;
* Select: &#039;&#039;&#039;Dutch (Nederlands)&#039;&#039;&#039;&lt;br /&gt;
* Save&lt;br /&gt;
&lt;br /&gt;
Drupal will automatically download translations.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 2 — Enable required modules ==&lt;br /&gt;
&lt;br /&gt;
Enable the following core modules:&lt;br /&gt;
&lt;br /&gt;
* Language&lt;br /&gt;
* Content Translation&lt;br /&gt;
* Interface Translation&lt;br /&gt;
&lt;br /&gt;
(Optional but recommended):&lt;br /&gt;
* Configuration Translation&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 3 — Enable translation for content type ==&lt;br /&gt;
&lt;br /&gt;
Go to:&lt;br /&gt;
 Structure → Content types → YOUR CONTENT TYPE → Edit&lt;br /&gt;
&lt;br /&gt;
* Open &#039;&#039;&#039;Language settings&#039;&#039;&#039;&lt;br /&gt;
* Enable:&lt;br /&gt;
  * ✔ Make this content type translatable&lt;br /&gt;
&lt;br /&gt;
Save.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 4 — Configure translatable fields ==&lt;br /&gt;
&lt;br /&gt;
Go to:&lt;br /&gt;
 Structure → Content types → YOUR CONTENT TYPE → Manage fields&lt;br /&gt;
&lt;br /&gt;
For each field:&lt;br /&gt;
&lt;br /&gt;
=== Make translatable ===&lt;br /&gt;
* Description&lt;br /&gt;
* Note&lt;br /&gt;
* Link (especially link title)&lt;br /&gt;
&lt;br /&gt;
=== Usually NOT translatable ===&lt;br /&gt;
* IDs&lt;br /&gt;
* Technical/system fields&lt;br /&gt;
&lt;br /&gt;
Rule of thumb:&lt;br /&gt;
* If humans read it → translatable&lt;br /&gt;
* If system uses it → not translatable&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 5 — Configure language settings ==&lt;br /&gt;
&lt;br /&gt;
Go to:&lt;br /&gt;
 Configuration → Regional → Content language&lt;br /&gt;
&lt;br /&gt;
* Enable translation for your content type&lt;br /&gt;
* Set default language:&lt;br /&gt;
  * English&lt;br /&gt;
&lt;br /&gt;
Save configuration.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 6 — Add language switcher ==&lt;br /&gt;
&lt;br /&gt;
Go to:&lt;br /&gt;
 Structure → Block layout&lt;br /&gt;
&lt;br /&gt;
* Add block:&lt;br /&gt;
  * Language switcher&lt;br /&gt;
* Place it in:&lt;br /&gt;
  * Header or sidebar&lt;br /&gt;
&lt;br /&gt;
Save block placement.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 7 — Test with one content item ==&lt;br /&gt;
&lt;br /&gt;
* Open an existing node&lt;br /&gt;
* Click the &#039;&#039;&#039;Translate&#039;&#039;&#039; tab&lt;br /&gt;
* Add Dutch translation&lt;br /&gt;
* Fill in translated fields:&lt;br /&gt;
  * Description (NL)&lt;br /&gt;
  * Note (NL)&lt;br /&gt;
&lt;br /&gt;
Save.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Step 8 — Translate interface elements ==&lt;br /&gt;
&lt;br /&gt;
Go to:&lt;br /&gt;
 Configuration → Regional → Translate interface&lt;br /&gt;
&lt;br /&gt;
Translate:&lt;br /&gt;
* Field labels&lt;br /&gt;
* Help texts&lt;br /&gt;
* UI elements if needed&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== How it works ==&lt;br /&gt;
&lt;br /&gt;
Each content item becomes:&lt;br /&gt;
&lt;br /&gt;
* English version (default)&lt;br /&gt;
* Dutch version (translation)&lt;br /&gt;
&lt;br /&gt;
Users can switch language using the language switcher.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Important notes ==&lt;br /&gt;
&lt;br /&gt;
* If no Dutch translation exists:&lt;br /&gt;
  * Drupal may show English (fallback)&lt;br /&gt;
* If translation exists:&lt;br /&gt;
  * Drupal shows translated content&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Recommended approach ==&lt;br /&gt;
&lt;br /&gt;
* Keep English as default language&lt;br /&gt;
* Add Dutch as translation&lt;br /&gt;
* Test with a few nodes before full rollout&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Editorial workflow ==&lt;br /&gt;
&lt;br /&gt;
# Create content (English or Dutch)&lt;br /&gt;
# Click &#039;&#039;&#039;Translate&#039;&#039;&#039;&lt;br /&gt;
# Add second language&lt;br /&gt;
# Save&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
* Drupal multilingual is stable and powerful&lt;br /&gt;
* Always translate entire content items (not separate fields per language)&lt;br /&gt;
* Start simple and expand gradually&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_JSON_based_Multi-Link_Field&amp;diff=1597</id>
		<title>ICT:D JSON based Multi-Link Field</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_JSON_based_Multi-Link_Field&amp;diff=1597"/>
		<updated>2026-04-02T15:02:28Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= JSON-Based Multi-Link Field Architecture = This document summarizes a proposed architecture for storing and managing 0…N website links per record in Drupal, using a single text field containing JSON and a modal-based custom widget.  The goal is: * A clean edit form (one line only) * A modal to manage multiple links * Deterministic behavior (no handler surprises) * Easy rendering in Views and Twig * Reusable across all content types  == 1. Overview == Instead of using...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= JSON-Based Multi-Link Field Architecture =&lt;br /&gt;
This document summarizes a proposed architecture for storing and managing&lt;br /&gt;
0…N website links per record in Drupal, using a single text field containing&lt;br /&gt;
JSON and a modal-based custom widget.&lt;br /&gt;
&lt;br /&gt;
The goal is:&lt;br /&gt;
* A clean edit form (one line only)&lt;br /&gt;
* A modal to manage multiple links&lt;br /&gt;
* Deterministic behavior (no handler surprises)&lt;br /&gt;
* Easy rendering in Views and Twig&lt;br /&gt;
* Reusable across all content types&lt;br /&gt;
&lt;br /&gt;
== 1. Overview ==&lt;br /&gt;
Instead of using Drupal’s multi-value Link field (which clutters the edit form),&lt;br /&gt;
a single text field is used to store all links in JSON format.&lt;br /&gt;
&lt;br /&gt;
Each record stores its own JSON string. There is no global file.&lt;br /&gt;
&lt;br /&gt;
Example JSON stored in the field:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
  { &amp;quot;label&amp;quot;: &amp;quot;Foo&amp;quot;, &amp;quot;url&amp;quot;: &amp;quot;https://foo.com&amp;quot; },&lt;br /&gt;
  { &amp;quot;label&amp;quot;: &amp;quot;Bar&amp;quot;, &amp;quot;url&amp;quot;: &amp;quot;https://bar.com&amp;quot; }&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Edit Form UX ==&lt;br /&gt;
The edit form shows only one line:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Website links: [ Manage links ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Clicking the button opens a modal where the editor can:&lt;br /&gt;
* Add a link&lt;br /&gt;
* Edit a link&lt;br /&gt;
* Remove a link&lt;br /&gt;
* Reorder links&lt;br /&gt;
&lt;br /&gt;
When the modal is saved, the JSON is written back into the field.&lt;br /&gt;
&lt;br /&gt;
== 3. Storage Model ==&lt;br /&gt;
A single field (e.g., &amp;lt;code&amp;gt;field_links_json&amp;lt;/code&amp;gt;) stores the JSON string.&lt;br /&gt;
&lt;br /&gt;
Characteristics:&lt;br /&gt;
* One JSON string per record&lt;br /&gt;
* No shared storage&lt;br /&gt;
* No extra entities required&lt;br /&gt;
* Deterministic and simple&lt;br /&gt;
&lt;br /&gt;
== 4. Rendering in Twig ==&lt;br /&gt;
Twig can decode and loop through the JSON:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% set links = node.field_links_json.value|json_decode %}&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
{% for link in links %}&lt;br /&gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;{{ link.url }}&amp;quot;&amp;gt;{{ link.label }}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
{% endfor %}&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5. Rendering in Views ==&lt;br /&gt;
A small custom Views field handler is used.&lt;br /&gt;
&lt;br /&gt;
Responsibilities:&lt;br /&gt;
* Read the JSON string&lt;br /&gt;
* Decode it&lt;br /&gt;
* Render 0…N links as HTML or a render array&lt;br /&gt;
&lt;br /&gt;
This keeps Views clean and avoids exposing raw JSON.&lt;br /&gt;
&lt;br /&gt;
== 6. Reusability ==&lt;br /&gt;
This architecture works for:&lt;br /&gt;
* All records in a content type&lt;br /&gt;
* Any number of content types&lt;br /&gt;
* Any custom entity type&lt;br /&gt;
&lt;br /&gt;
The field and widget are generic and reusable.&lt;br /&gt;
&lt;br /&gt;
== 7. Module Complexity Estimate ==&lt;br /&gt;
Approximate code size:&lt;br /&gt;
* Field widget: 80–120 lines&lt;br /&gt;
* Modal form: 100–150 lines&lt;br /&gt;
* Views field handler: 60–80 lines&lt;br /&gt;
* Module scaffolding: 20–40 lines&lt;br /&gt;
&lt;br /&gt;
Total: ~260–390 lines of code.&lt;br /&gt;
&lt;br /&gt;
== 8. Advantages ==&lt;br /&gt;
* Clean edit form (one line only)&lt;br /&gt;
* Deterministic behavior&lt;br /&gt;
* No multi-value field clutter&lt;br /&gt;
* No handler switching&lt;br /&gt;
* Easy to render in Twig and Views&lt;br /&gt;
* Successor-friendly and maintainable&lt;br /&gt;
* Reusable across the entire system&lt;br /&gt;
&lt;br /&gt;
== 9. Considerations ==&lt;br /&gt;
* Links are not reusable across records (by design)&lt;br /&gt;
* Requires a small custom module&lt;br /&gt;
* JSON must be validated in the modal&lt;br /&gt;
&lt;br /&gt;
== 10. Conclusion ==&lt;br /&gt;
This JSON-based approach provides a clean, predictable, and maintainable&lt;br /&gt;
solution for managing multiple website links per record, without cluttering&lt;br /&gt;
the edit form or relying on Drupal’s multi-value field behavior.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=Research:Drupal_Glossary&amp;diff=1596</id>
		<title>Research:Drupal Glossary</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=Research:Drupal_Glossary&amp;diff=1596"/>
		<updated>2026-03-31T16:22:24Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= &#039;&#039;&#039;Drupal Glossary&#039;&#039;&#039; =&lt;br /&gt;
&#039;&#039;&#039;Ajax&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A web technology used to exchange data with a server to dynamically update parts of a web page (for example, forms) without needing entire page reloads.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alias&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A user-friendly name to replace the internal path that the system assigns to a URL on the site. For example, you might assign an alias of &#039;&#039;/about&#039;&#039; to the About page on your site, to replace the internal path &#039;&#039;/node/5&#039;&#039;. This would give the page a URL of &#039;&#039;&amp;lt;nowiki&amp;gt;http://example.com/about&amp;lt;/nowiki&amp;gt;&#039;&#039; instead of &#039;&#039;&amp;lt;nowiki&amp;gt;http://example.com/node/5&amp;lt;/nowiki&amp;gt;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anonymous&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A person (user) interacting with the site who is not logged in. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Authenticated&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A person (user) interacting with the site who is logged in. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A chunk of content (text, images, links, etc.) that can be displayed on a page of a site. Blocks are displayed in regions. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Breakpoint&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Breakpoints are used to separate the height or width of browser screens, printers, and other media output types into steps. A responsive site adjusts its presentation at these breakpoints. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bundle&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Synonym for Entity subtype.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cache&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The site’s internal cache stores the output of time-consuming calculations, such as computing output for an HTML page request, and then retrieves them instead of recalculating the next time they are needed. External caching systems can also be used on the web server to speed up a site’s response. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Coding standards&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Coding standards are the rules for programmers that define best practices, formatting, and various other rules, so that everyone uses the same conventions and has the same expectations when they see code. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Composer&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The PHP dependency manager used by Drupal, Drush, the Symfony framework and others. It is the preferred means of installing Drupal projects. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CMS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Acronym for Content Management System.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Configuration&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Information about your site that is not content, and is meant to be more permanent than state information, such as the name of your site, the content types and views you have defined, etc. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Content&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Information meant to be displayed on your site, such as text, images, downloads, etc. See also Configuration and State. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Content item&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An item of content that is typically meant to be displayed as the main content of a page on your site. This is an entity type. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Content Management System (CMS)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A collection of tools designed to allow the creation, modification, organization, search, retrieval and removal of information on a website. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Content type&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An entity subtype for the content item entity type. Each content type is used for some particular purpose on the site, and each has its own fields. For example, a site for a farmers market might have a content type for simple pages, and another for a vendor listing page. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Contextual Filter (in a View)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Limits the data to be output in a view, based on the context of the view display, such as the full URL of the page. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Contextual link&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A link to an administrative page for editing or configuring a feature of the site, shown in the context where that feature is displayed. Example: a link to configure a menu that is shown when you hover your mouse over the menu. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Contributed&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Modules, themes, and distributions that are not part of the Drupal core download, and that can be downloaded separately from the &#039;&#039;Drupal.org&#039;&#039; website.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cron&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On some operating systems, &#039;&#039;cron&#039;&#039; is a command scheduler application that executes commands or scripts periodically. Your site defines periodic tasks, also known as cron tasks, that need to be triggered either by an operating system cron scheduler, or internally. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cross Site Scripting&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Security vulnerability typically found in websites. In a site that is not well protected, malicious users can enter script into web pages that are viewed by other users.   &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Devel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Module that helps with development tasks such as debugging and inspecting code, analyzing database queries, and generating dummy content. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Development site&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Copy of the live website that is used for developing, updating, and testing the website. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Display (in a View)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Type of output of a view, for example a page, a block or a feed. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Distribution&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A single download that provides a shortcut for setting up a specific type of site, such as a website for a club or for e-commerce. A distribution contains Drupal core, along with contributed modules and/or themes; many distributions also pre-configure the site or even create sample content upon installation. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Drupal Association&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Non-profit organization dedicated to supporting the Drupal project and community. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Drupal core&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The files, themes, profiles, and modules included with the standard project software download. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Drush&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Command line shell and scripting interface for Drupal. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Editorial Workflow&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Process to create, review, edit, and publish content. Multiple people in different roles (for example content creators and editors) can be part of the process. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An item of either content or configuration data, although in common usage, the term often refers to content entities. Examples include content items, custom blocks, taxonomy terms, and definitions of content types; the first three are content entities, and the last is a configuration entity. See also Entity type, Entity subtype, and Field. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity subtype&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Within a content entity type, a grouping of entities that share the same fields. For example, within the content item entity type, a farmers market site might have subtypes (known as content types) for static pages and vendor pages, each with its own group of fields. You may also see the term &#039;&#039;bundle&#039;&#039; used (especially in programmer documentation) as a synonym of entity subtype.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity type&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The overall type of an entity; in common usage, it is only applied to a content entity. Examples include content types, taxonomy terms, and custom blocks.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Data of a certain type that is attached to a content entity. For instance, on a farmers market site’s vendor content type, you might have fields for an image, the vendor description, and a taxonomy term.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field bundle&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Synonym for Entity subtype.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field formatter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Configuration that defines how the data in a field is displayed. For example, a text field could be displayed with a prefix and/or suffix, and it could have its HTML tags stripped out or limited. See also View mode and Field widget. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field widget&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Configuration that defines how someone can enter or edit data for a field on a data entry form. For example, a text field could use a single-line or multi-line entry box, and there could be a setting for the size of the box. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Filter (in a View)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Limits the data to be output in a view, based on criteria such as publication status, type of content, or field value.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Formatter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
See Field formatter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FOSS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Acronym for &#039;&#039;Free and Open Source Software&#039;&#039;, meaning software that is developed by a community of people and released under a non-commercial license. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Git&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Version control system used by Drupal developers to coordinate their individual code changes. Git records everyone’s changes to a given project in a directory tree called a git repository.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;GPL&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Acronym for the &#039;&#039;GNU General Public License&#039;&#039;, a non-commercial software license. All software downloaded from the &#039;&#039;Drupal.org&#039;&#039; website is licensed under the &amp;quot;GNU General Public License, version 2&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Image style&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A set of processing steps that transform a base image into a new image; typical processing includes scaling and cropping. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Installing&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Preparing Drupal core or any contributed theme or module for usage. In the case of Drupal core this means downloading the necessary files, creating the database, configuring and running the installation script.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LAMP&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Acronym for &#039;&#039;Linux, Apache, MySQL, and PHP&#039;&#039;: the software on the web server that the scripts commonly run on (although it can use other operating systems, web servers, and databases). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Log&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A list of recorded events on the site, such as usage data, performance data, errors, warnings, and operational information.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Menu&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A set of links used for navigation on a site, which may be arranged in a hierarchy. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Module&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Software (usually PHP, JavaScript, and/or CSS) that extends site features and adds functionality. The Drupal project distinguishes between &#039;&#039;core&#039;&#039; and &#039;&#039;contributed&#039;&#039; modules. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Path&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The unique, last part of the internal URL that the system assigns to a page on the site, which can be a visitor-facing page or an administrative page. For example, the internal URL for the About page on your site might be &#039;&#039;&amp;lt;nowiki&amp;gt;http://example.com/node/5&amp;lt;/nowiki&amp;gt;&#039;&#039;, and in this case, the path is &#039;&#039;node/5&#039;&#039;. See also Alias. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Permission&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The ability to perform some action on the site, such as editing a particular type of content, or viewing user profiles. See also Role. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Reference field&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A field that represents a relationship between an entity and one or more other entities, which may be the same entity type or a different type. For example, on a farmers market site, a recipe content item might have a reference field to the vendor (also a content item) that posted the recipe. Taxonomy term fields are also reference fields. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Region&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A defined area of a page where content can be placed, such as the header, footer, main content area, left sidebar, etc. Regions are defined by themes, and the content displayed in each region is contained in blocks. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Relationship (in a View)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Expansion of the data that is displayed in a view, by relating the base content to other content entities. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Repository&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Location where a version control system stores all the files and directories for a project.    &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Responsive&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A site or theme is said to be responsive if it adjusts its presentation in response to the size of the browser screen, printer, or other media output type. See also Breakpoint. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Revision&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A record of the past or present state of a content entity, as it is edited over time. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Role&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A named set of permissions that can be applied to a user account. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Security update&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An update that fixes a security-related bug, such as a hacking vulnerability. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Session&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Information about individual site visitors&#039; interactions with the site, such as whether they are logged in and their cookies. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Staging site&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Copy of the live website that can be used for testing, or presenting the changes to the client for approval. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;State&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Information of a temporary nature about the current state of your site, such as the time when cron was last run, etc. See also Content and Configuration. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Taxonomy&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The process of classifying content. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Taxonomy term&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A term used to classify content, such as a tag or a category. See also Vocabulary. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Text format&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Configuration that defines the processing that happens to user-entered text before it is shown in the browser. This might include stripping or limiting HTML tags, or turning URLs into links. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Theme&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Software and asset files (images, CSS, PHP code, and/or templates) that determine the style and layout of the site. The Drupal project distinguishes between &#039;&#039;core&#039;&#039; and &#039;&#039;contributed&#039;&#039; themes. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UI&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Acronym for &#039;&#039;User Interface&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Update&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A newer version of your site’s software, either Drupal core or a module or theme. See also Security update. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;URL&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A web page’s unique address on the web. For example &amp;lt;nowiki&amp;gt;https://example.com/node/7&amp;lt;/nowiki&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;User&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A person interacting with the site, either logged-in or anonymous. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;User interface&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The text, styles, and images that are visible on a site, separated logically into the user interface for site visitors and the administrative user interface.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;User one (User 1)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The initial user account that is created when you install the site (whose ID number is 1). It automatically has all permissions, even if it is not assigned an administrative role.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Version Control System&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Software that keeps copies of files and revision history in a repository, and allows you to add, delete, and update files.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;View&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A formatted listing of data; typically, the data comes from content entities. For example, on a farmers market site, you might create a content item for each vendor. You could then make view that generates a listing page that shows a thumbnail image and short description of each vendor, linking to the full-page content item. Using the same data, you could also make a view that generates a new vendors block, which would show information from the most recently added vendors.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;View mode&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A set of field formatter configuration for all of the fields of a content entity, some of which may be hidden. Each entity subtype can have one or more view modes defined; for example, content types typically have &#039;&#039;Full&#039;&#039; and &#039;&#039;Teaser&#039;&#039; view modes, where the &#039;&#039;Teaser&#039;&#039; view mode displays fewer or trimmed-down fields. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vocabulary&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A group of taxonomy terms to choose from when classifying content in a particular way, such as the list of all of the vendor categories on a farmers market site. Technically, vocabularies are the entity subtype for the taxonomy term entity type. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Widget&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Configuration that defines how someone can enter or edit data for a field on a data entry form. For example, a text field could use a single-line or multi-line entry box, and there could be a setting for the size of the box. See also Field formatter. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wizard&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A web form that allows you to fill in a few values, and creates something with sensible defaults based on the values you chose. For example, there are wizards for creating views of different types. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WYSIWYG&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Acronym for &#039;&#039;What You See is What You Get&#039;&#039;, meaning a method for editing content where what you see on the editing screen closely resembles the final product. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Workflow&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
See Editorial Workflow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;XSS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Acronym for Cross Site Scripting.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=Research:Drupal_Glossary&amp;diff=1595</id>
		<title>Research:Drupal Glossary</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=Research:Drupal_Glossary&amp;diff=1595"/>
		<updated>2026-03-31T16:20:55Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;dd&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;dd&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Final_Chapter_Architecture&amp;diff=1594</id>
		<title>ICT:Final Chapter Architecture</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Final_Chapter_Architecture&amp;diff=1594"/>
		<updated>2026-03-31T13:24:56Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Final Chapter Architecture =  This page documents the final, endorsed structure for CHAPTER, PLACE, and ORGANISATION within the research platform.   It reflects the architectural simplification reached after analysis and Excel validation.  == 1. CHAPTER Structure (Flat, Non‑Recursive) ==  The CHAPTER entity is now:  * Flat (no recursion) * Non‑hierarchical * Narrative-only * Max. 3 characters * Two families: ** Txx — time‑based narrative chapters ** Xxx — the...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Final Chapter Architecture =&lt;br /&gt;
&lt;br /&gt;
This page documents the final, endorsed structure for CHAPTER, PLACE, and ORGANISATION within the research platform.  &lt;br /&gt;
It reflects the architectural simplification reached after analysis and Excel validation.&lt;br /&gt;
&lt;br /&gt;
== 1. CHAPTER Structure (Flat, Non‑Recursive) ==&lt;br /&gt;
&lt;br /&gt;
The CHAPTER entity is now:&lt;br /&gt;
&lt;br /&gt;
* Flat (no recursion)&lt;br /&gt;
* Non‑hierarchical&lt;br /&gt;
* Narrative-only&lt;br /&gt;
* Max. 3 characters&lt;br /&gt;
* Two families:&lt;br /&gt;
** Txx — time‑based narrative chapters&lt;br /&gt;
** Xxx — thematic or contextual chapters&lt;br /&gt;
&lt;br /&gt;
There are no subchapters such as T00a, T00b, etc.  &lt;br /&gt;
All narrative subdivision is handled by PLACE and ORGANISATION.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
 T00 – Before the main period&lt;br /&gt;
 T01 – Early developments&lt;br /&gt;
 T02 – First migrations&lt;br /&gt;
 T06 – WW2&lt;br /&gt;
 T07 – Post‑war period&lt;br /&gt;
&lt;br /&gt;
 X00 – Methodology&lt;br /&gt;
 X01 – Sources&lt;br /&gt;
 X02 – Transport &amp;amp; technology&lt;br /&gt;
 X03 – Family context&lt;br /&gt;
&lt;br /&gt;
CHAPTER codes remain stable and short to keep asset numbering clean.&lt;br /&gt;
&lt;br /&gt;
== 2. PLACE Structure (Recursive, 6‑Character Codes) ==&lt;br /&gt;
&lt;br /&gt;
PLACE is the primary engine for narrative subdivision.  &lt;br /&gt;
It remains:&lt;br /&gt;
&lt;br /&gt;
* Recursive (multi‑level hierarchy)&lt;br /&gt;
* Geographical&lt;br /&gt;
* User‑defined&lt;br /&gt;
* Using full 6‑character codes&lt;br /&gt;
* The natural source of “subchapter‑like” separation&lt;br /&gt;
&lt;br /&gt;
PLACE captures geographical storylines such as:&lt;br /&gt;
&lt;br /&gt;
* WW2 in Ostende&lt;br /&gt;
* Children in Alton&lt;br /&gt;
* Children in Bristol&lt;br /&gt;
* Sanas in Italy and Germany&lt;br /&gt;
&lt;br /&gt;
=== Example PLACE Hierarchy ===&lt;br /&gt;
&lt;br /&gt;
 EUROPE&lt;br /&gt;
   UK&lt;br /&gt;
     ALTON&lt;br /&gt;
     BRIST&lt;br /&gt;
   BELGIUM&lt;br /&gt;
     OSTEND&lt;br /&gt;
 ITALY&lt;br /&gt;
 GERMANY&lt;br /&gt;
&lt;br /&gt;
PLACE codes (6 chars) are used directly in asset numbering.&lt;br /&gt;
&lt;br /&gt;
== 3. ORGANISATION Structure (Recursive, 6‑Character Codes) ==&lt;br /&gt;
&lt;br /&gt;
ORGANISATION is also recursive and captures institutional storylines.&lt;br /&gt;
&lt;br /&gt;
It remains:&lt;br /&gt;
&lt;br /&gt;
* Recursive&lt;br /&gt;
* User‑defined&lt;br /&gt;
* 6‑character codes&lt;br /&gt;
* Parallel to PLACE in structure and purpose&lt;br /&gt;
&lt;br /&gt;
=== Example ORGANISATION Hierarchy ===&lt;br /&gt;
&lt;br /&gt;
 SANAS&lt;br /&gt;
   SANAS-IT&lt;br /&gt;
   SANAS-DE&lt;br /&gt;
&lt;br /&gt;
This allows assets to be grouped by institutional context.&lt;br /&gt;
&lt;br /&gt;
== 4. Asset Numbering (Clean, Stable) ==&lt;br /&gt;
&lt;br /&gt;
Asset numbering uses:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;Chapter.Code&amp;gt; – &amp;lt;Place.Code OR Organisation.Code&amp;gt; – &amp;lt;Counter(5)&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because CHAPTER is flat and fixed-length, numbering remains clean and aligned.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
 T06-OSTEND-00012&lt;br /&gt;
 T00-ALTON-00013&lt;br /&gt;
 T00-BRIST-00014&lt;br /&gt;
 T00-UKPRE-00015&lt;br /&gt;
&lt;br /&gt;
This structure automatically groups assets by:&lt;br /&gt;
&lt;br /&gt;
* Chapter (narrative period)&lt;br /&gt;
* Place (geographical storyline)&lt;br /&gt;
* Organisation (institutional storyline)&lt;br /&gt;
&lt;br /&gt;
No subchapter codes are needed.&lt;br /&gt;
&lt;br /&gt;
== 5. Why Subchapters Are No Longer Needed ==&lt;br /&gt;
&lt;br /&gt;
Subchapters were originally used to subdivide narrative chapters (e.g., CH06a, CH00b).  &lt;br /&gt;
After analysis, it became clear that:&lt;br /&gt;
&lt;br /&gt;
* These subdivisions were geographical or institutional, not structural.&lt;br /&gt;
* PLACE and ORGANISATION already provide natural, recursive subdivision.&lt;br /&gt;
* Adding subchapter codes would pollute asset numbering.&lt;br /&gt;
* CHAPTER should remain a top‑level narrative container only.&lt;br /&gt;
&lt;br /&gt;
Therefore:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Subchapters are now expressed through PLACE and ORGANISATION, not through CHAPTER.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Benefits of the Final Architecture ==&lt;br /&gt;
&lt;br /&gt;
* Clean, stable CHAPTER codes&lt;br /&gt;
* Powerful recursive PLACE and ORGANISATION hierarchies&lt;br /&gt;
* Natural narrative subdivision without extra fields&lt;br /&gt;
* Clean asset numbering&lt;br /&gt;
* Strong filtering for publications (Chapter + Place + Organisation + Keywords)&lt;br /&gt;
* Easy to explain to club members&lt;br /&gt;
* Successor‑friendly and future‑proof&lt;br /&gt;
&lt;br /&gt;
== 7. Status ==&lt;br /&gt;
&lt;br /&gt;
This structure has been validated through Excel analysis and is ready for presentation to the club members for endorsement.  &lt;br /&gt;
Once approved, it becomes the final, stable architecture for all future research, publications, and digital heritage workflows.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Asset-Chain_Children_Button_logic_(with_boolean_field)&amp;diff=1593</id>
		<title>ICT:Asset-Chain Children Button logic (with boolean field)</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Asset-Chain_Children_Button_logic_(with_boolean_field)&amp;diff=1593"/>
		<updated>2026-03-30T16:44:41Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Asset Hierarchy: &amp;quot;Children&amp;quot; Button Logic ==&lt;br /&gt;
&lt;br /&gt;
=== Overview ===&lt;br /&gt;
This implementation displays a clickable button in the Top Assets table only when an asset has at least one child. It avoids complex and performance-heavy Views relationships by using a data-driven boolean flag.&lt;br /&gt;
&lt;br /&gt;
=== Data Model ===&lt;br /&gt;
* &#039;&#039;&#039;Entity Type:&#039;&#039;&#039; Asset (or Node)&lt;br /&gt;
* &#039;&#039;&#039;Parent Field:&#039;&#039;&#039; &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; (Entity Reference pointing to the parent).&lt;br /&gt;
* &#039;&#039;&#039;Indicator Field:&#039;&#039;&#039; &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; (Boolean).&lt;br /&gt;
** Default: Off (0)&lt;br /&gt;
** Logic: Marks if a Top Asset is a parent.&lt;br /&gt;
&lt;br /&gt;
=== Automation (The Tweak Module) ===&lt;br /&gt;
The logic is handled automatically by the &#039;&#039;&#039;Heritage Tweaks&#039;&#039;&#039; module via &amp;lt;code&amp;gt;hook_entity_presave&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Trigger:&#039;&#039;&#039; When a child asset is saved.&lt;br /&gt;
* &#039;&#039;&#039;Action:&#039;&#039;&#039; The code identifies the parent referenced in &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Result:&#039;&#039;&#039; It programmatically flips the parent&#039;s &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; to &#039;&#039;&#039;True (1)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Views Configuration ===&lt;br /&gt;
The button is rendered using a &#039;&#039;&#039;Global: Custom Text&#039;&#039;&#039; field in the Assets View.&lt;br /&gt;
&lt;br /&gt;
==== Logic ====&lt;br /&gt;
The button uses Twig to check the boolean state before rendering:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;twig&amp;quot;&amp;gt;&lt;br /&gt;
{% if field_as_has_children %}&lt;br /&gt;
  &amp;lt;a href=&amp;quot;/admin/asset-chain/{{ nid }}&amp;quot; class=&amp;quot;button button--primary&amp;quot;&amp;gt;Children&amp;lt;/a&amp;gt;&lt;br /&gt;
{% endif %}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Key Settings ====&lt;br /&gt;
* &#039;&#039;&#039;Filters:&#039;&#039;&#039; &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; is EMPTY (Ensures only Top Assets are listed).&lt;br /&gt;
* &#039;&#039;&#039;Gin Styling:&#039;&#039;&#039; Uses &amp;lt;code&amp;gt;button button--primary&amp;lt;/code&amp;gt; classes to match the Gin Admin Theme accent colors.&lt;br /&gt;
* &#039;&#039;&#039;Field Order:&#039;&#039;&#039; The &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; field must be positioned &#039;&#039;&#039;above&#039;&#039;&#039; the Custom Text field and set to &amp;quot;Exclude from display.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Maintenance ===&lt;br /&gt;
* &#039;&#039;&#039;Adding Children:&#039;&#039;&#039; The button appears automatically the moment the first child is saved.&lt;br /&gt;
* &#039;&#039;&#039;Existing Data:&#039;&#039;&#039; To &amp;quot;wake up&amp;quot; a button for a Top Asset that already has children, simply Edit and Save one of its child records.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hierarchy Integrity Rule ===&lt;br /&gt;
To enforce the &amp;quot;Single Level&amp;quot; hierarchy design:&lt;br /&gt;
* &#039;&#039;&#039;Logic:&#039;&#039;&#039; The &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; field is hidden from the Edit form if &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; is TRUE.&lt;br /&gt;
* &#039;&#039;&#039;Result:&#039;&#039;&#039; Prevents a Parent from being assigned a Parent (Nested recursion).&lt;br /&gt;
* &#039;&#039;&#039;Exception:&#039;&#039;&#039; Top Assets without children can still be edited to assign a parent, allowing for data entry error correction.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Heritage Project: Asset Hierarchy System ==&lt;br /&gt;
&lt;br /&gt;
=== Overview ===&lt;br /&gt;
This system manages a single-level recursive asset hierarchy. It uses a custom boolean field to drive the User Interface (showing buttons) and data integrity (locking the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Data Structure ===&lt;br /&gt;
* &#039;&#039;&#039;Entity Types:&#039;&#039;&#039; Supports standard &amp;lt;code&amp;gt;node&amp;lt;/code&amp;gt; and custom &amp;lt;code&amp;gt;asset&amp;lt;/code&amp;gt; entities.&lt;br /&gt;
* &#039;&#039;&#039;Field: Parent Asset&#039;&#039;&#039; (&amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt;): An Entity Reference field on child assets pointing to their Top Asset.&lt;br /&gt;
* &#039;&#039;&#039;Field: Has Children&#039;&#039;&#039; (&amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt;): A boolean field on Top Assets used as a flag for hierarchy state.&lt;br /&gt;
&lt;br /&gt;
=== Implementation Logic (heritage_tweaks.module) ===&lt;br /&gt;
&lt;br /&gt;
The module automates the hierarchy through two main PHP hooks.&lt;br /&gt;
&lt;br /&gt;
==== 1. Hierarchy Automation (The Switch) ====&lt;br /&gt;
The &amp;lt;code&amp;gt;heritage_tweaks_entity_presave()&amp;lt;/code&amp;gt; function acts as a &amp;quot;detector&amp;quot; during the save process.&lt;br /&gt;
* &#039;&#039;&#039;Trigger:&#039;&#039;&#039; When any asset is saved.&lt;br /&gt;
* &#039;&#039;&#039;Action:&#039;&#039;&#039; If &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; is not empty, the code identifies the parent entity.&lt;br /&gt;
* &#039;&#039;&#039;Result:&#039;&#039;&#039; It programmatically sets the parent&#039;s &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; to &#039;&#039;&#039;1 (TRUE)&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Outcome:&#039;&#039;&#039; This &amp;quot;wakes up&amp;quot; the children button in the main list view automatically.&lt;br /&gt;
&lt;br /&gt;
==== 2. Hierarchy Protection (The Safety Rail) ====&lt;br /&gt;
The &amp;lt;code&amp;gt;heritage_tweaks_form_alter()&amp;lt;/code&amp;gt; function protects data integrity during editing.&lt;br /&gt;
* &#039;&#039;&#039;Trigger:&#039;&#039;&#039; When an existing asset is opened in the Edit form.&lt;br /&gt;
* &#039;&#039;&#039;Logic:&#039;&#039;&#039; It checks the &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; value.&lt;br /&gt;
* &#039;&#039;&#039;Constraint:&#039;&#039;&#039; If the value is &#039;&#039;&#039;1&#039;&#039;&#039;, the &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; field is hidden (&amp;lt;code&amp;gt;#access = FALSE&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Outcome:&#039;&#039;&#039; This prevents a Parent from being assigned to another Parent, strictly enforcing the &#039;&#039;&#039;single-level design rule&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Views Configuration ===&lt;br /&gt;
The &amp;quot;Children&amp;quot; button is rendered via a &#039;&#039;&#039;Global: Custom Text&#039;&#039;&#039; field using Twig logic:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;twig&amp;quot;&amp;gt;&lt;br /&gt;
{% if field_as_has_children %}&lt;br /&gt;
  &amp;lt;a href=&amp;quot;/admin/asset-chain/{{ nid }}&amp;quot; class=&amp;quot;button button--primary&amp;quot;&amp;gt;Children&amp;lt;/a&amp;gt;&lt;br /&gt;
{% endif %}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== User Experience (UX) Guidelines ===&lt;br /&gt;
* &#039;&#039;&#039;Single-Tab Navigation:&#039;&#039;&#039; The &amp;quot;Children&amp;quot; button opens in the current tab (no &amp;lt;code&amp;gt;_blank&amp;lt;/code&amp;gt; target).&lt;br /&gt;
* &#039;&#039;&#039;Gin Styling:&#039;&#039;&#039; Primary actions (Forward) use &amp;lt;code&amp;gt;button--primary&amp;lt;/code&amp;gt;. Secondary actions (Back) use &amp;lt;code&amp;gt;button--secondary&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Error Correction:&#039;&#039;&#039; Top Assets without children can still be edited to assign a parent if they were created as &amp;quot;Top&amp;quot; by mistake.&lt;br /&gt;
&lt;br /&gt;
=== Maintenance &amp;amp; Troubleshooting ===&lt;br /&gt;
* &#039;&#039;&#039;Syncing Existing Data:&#039;&#039;&#039; To update assets created before this module, simply Edit and Save one child record for each Top Asset.&lt;br /&gt;
* &#039;&#039;&#039;Cache:&#039;&#039;&#039; After any module code changes, perform a &amp;lt;code&amp;gt;drush cr&amp;lt;/code&amp;gt; to refresh the hook registry.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Asset-Chain_Children_Button_logic_(with_boolean_field)&amp;diff=1592</id>
		<title>ICT:Asset-Chain Children Button logic (with boolean field)</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Asset-Chain_Children_Button_logic_(with_boolean_field)&amp;diff=1592"/>
		<updated>2026-03-30T16:39:23Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Asset Hierarchy: &amp;quot;Children&amp;quot; Button Logic ==&lt;br /&gt;
&lt;br /&gt;
=== Overview ===&lt;br /&gt;
This implementation displays a clickable button in the Top Assets table only when an asset has at least one child. It avoids complex and performance-heavy Views relationships by using a data-driven boolean flag.&lt;br /&gt;
&lt;br /&gt;
=== Data Model ===&lt;br /&gt;
* &#039;&#039;&#039;Entity Type:&#039;&#039;&#039; Asset (or Node)&lt;br /&gt;
* &#039;&#039;&#039;Parent Field:&#039;&#039;&#039; &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; (Entity Reference pointing to the parent).&lt;br /&gt;
* &#039;&#039;&#039;Indicator Field:&#039;&#039;&#039; &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; (Boolean).&lt;br /&gt;
** Default: Off (0)&lt;br /&gt;
** Logic: Marks if a Top Asset is a parent.&lt;br /&gt;
&lt;br /&gt;
=== Automation (The Tweak Module) ===&lt;br /&gt;
The logic is handled automatically by the &#039;&#039;&#039;Heritage Tweaks&#039;&#039;&#039; module via &amp;lt;code&amp;gt;hook_entity_presave&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Trigger:&#039;&#039;&#039; When a child asset is saved.&lt;br /&gt;
* &#039;&#039;&#039;Action:&#039;&#039;&#039; The code identifies the parent referenced in &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Result:&#039;&#039;&#039; It programmatically flips the parent&#039;s &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; to &#039;&#039;&#039;True (1)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Views Configuration ===&lt;br /&gt;
The button is rendered using a &#039;&#039;&#039;Global: Custom Text&#039;&#039;&#039; field in the Assets View.&lt;br /&gt;
&lt;br /&gt;
==== Logic ====&lt;br /&gt;
The button uses Twig to check the boolean state before rendering:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;twig&amp;quot;&amp;gt;&lt;br /&gt;
{% if field_as_has_children %}&lt;br /&gt;
  &amp;lt;a href=&amp;quot;/admin/asset-chain/{{ nid }}&amp;quot; class=&amp;quot;button button--primary&amp;quot;&amp;gt;Children&amp;lt;/a&amp;gt;&lt;br /&gt;
{% endif %}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Key Settings ====&lt;br /&gt;
* &#039;&#039;&#039;Filters:&#039;&#039;&#039; &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; is EMPTY (Ensures only Top Assets are listed).&lt;br /&gt;
* &#039;&#039;&#039;Gin Styling:&#039;&#039;&#039; Uses &amp;lt;code&amp;gt;button button--primary&amp;lt;/code&amp;gt; classes to match the Gin Admin Theme accent colors.&lt;br /&gt;
* &#039;&#039;&#039;Field Order:&#039;&#039;&#039; The &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; field must be positioned &#039;&#039;&#039;above&#039;&#039;&#039; the Custom Text field and set to &amp;quot;Exclude from display.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Maintenance ===&lt;br /&gt;
* &#039;&#039;&#039;Adding Children:&#039;&#039;&#039; The button appears automatically the moment the first child is saved.&lt;br /&gt;
* &#039;&#039;&#039;Existing Data:&#039;&#039;&#039; To &amp;quot;wake up&amp;quot; a button for a Top Asset that already has children, simply Edit and Save one of its child records.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hierarchy Integrity Rule ===&lt;br /&gt;
To enforce the &amp;quot;Single Level&amp;quot; hierarchy design:&lt;br /&gt;
* &#039;&#039;&#039;Logic:&#039;&#039;&#039; The &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; field is hidden from the Edit form if &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; is TRUE.&lt;br /&gt;
* &#039;&#039;&#039;Result:&#039;&#039;&#039; Prevents a Parent from being assigned a Parent (Nested recursion).&lt;br /&gt;
* &#039;&#039;&#039;Exception:&#039;&#039;&#039; Top Assets without children can still be edited to assign a parent, allowing for data entry error correction.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Asset-Chain_Children_Button_logic_(with_boolean_field)&amp;diff=1591</id>
		<title>ICT:Asset-Chain Children Button logic (with boolean field)</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Asset-Chain_Children_Button_logic_(with_boolean_field)&amp;diff=1591"/>
		<updated>2026-03-30T16:26:23Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;== Asset Hierarchy: &amp;quot;Children&amp;quot; Button Logic ==  === Overview === This implementation displays a clickable button in the Top Assets table only when an asset has at least one child. It avoids complex and performance-heavy Views relationships by using a data-driven boolean flag.  === Data Model === * &amp;#039;&amp;#039;&amp;#039;Entity Type:&amp;#039;&amp;#039;&amp;#039; Asset (or Node) * &amp;#039;&amp;#039;&amp;#039;Parent Field:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; (Entity Reference pointing to the parent). * &amp;#039;&amp;#039;&amp;#039;Indicator Field:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;field_as_h...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Asset Hierarchy: &amp;quot;Children&amp;quot; Button Logic ==&lt;br /&gt;
&lt;br /&gt;
=== Overview ===&lt;br /&gt;
This implementation displays a clickable button in the Top Assets table only when an asset has at least one child. It avoids complex and performance-heavy Views relationships by using a data-driven boolean flag.&lt;br /&gt;
&lt;br /&gt;
=== Data Model ===&lt;br /&gt;
* &#039;&#039;&#039;Entity Type:&#039;&#039;&#039; Asset (or Node)&lt;br /&gt;
* &#039;&#039;&#039;Parent Field:&#039;&#039;&#039; &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; (Entity Reference pointing to the parent).&lt;br /&gt;
* &#039;&#039;&#039;Indicator Field:&#039;&#039;&#039; &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; (Boolean).&lt;br /&gt;
** Default: Off (0)&lt;br /&gt;
** Logic: Marks if a Top Asset is a parent.&lt;br /&gt;
&lt;br /&gt;
=== Automation (The Tweak Module) ===&lt;br /&gt;
The logic is handled automatically by the &#039;&#039;&#039;Heritage Tweaks&#039;&#039;&#039; module via &amp;lt;code&amp;gt;hook_entity_presave&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Trigger:&#039;&#039;&#039; When a child asset is saved.&lt;br /&gt;
* &#039;&#039;&#039;Action:&#039;&#039;&#039; The code identifies the parent referenced in &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Result:&#039;&#039;&#039; It programmatically flips the parent&#039;s &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; to &#039;&#039;&#039;True (1)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Views Configuration ===&lt;br /&gt;
The button is rendered using a &#039;&#039;&#039;Global: Custom Text&#039;&#039;&#039; field in the Assets View.&lt;br /&gt;
&lt;br /&gt;
==== Logic ====&lt;br /&gt;
The button uses Twig to check the boolean state before rendering:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;twig&amp;quot;&amp;gt;&lt;br /&gt;
{% if field_as_has_children %}&lt;br /&gt;
  &amp;lt;a href=&amp;quot;/admin/asset-chain/{{ nid }}&amp;quot; class=&amp;quot;button button--primary&amp;quot;&amp;gt;Children&amp;lt;/a&amp;gt;&lt;br /&gt;
{% endif %}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Key Settings ====&lt;br /&gt;
* &#039;&#039;&#039;Filters:&#039;&#039;&#039; &amp;lt;code&amp;gt;field_as_parent_asset&amp;lt;/code&amp;gt; is EMPTY (Ensures only Top Assets are listed).&lt;br /&gt;
* &#039;&#039;&#039;Gin Styling:&#039;&#039;&#039; Uses &amp;lt;code&amp;gt;button button--primary&amp;lt;/code&amp;gt; classes to match the Gin Admin Theme accent colors.&lt;br /&gt;
* &#039;&#039;&#039;Field Order:&#039;&#039;&#039; The &amp;lt;code&amp;gt;field_as_has_children&amp;lt;/code&amp;gt; field must be positioned &#039;&#039;&#039;above&#039;&#039;&#039; the Custom Text field and set to &amp;quot;Exclude from display.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Maintenance ===&lt;br /&gt;
* &#039;&#039;&#039;Adding Children:&#039;&#039;&#039; The button appears automatically the moment the first child is saved.&lt;br /&gt;
* &#039;&#039;&#039;Existing Data:&#039;&#039;&#039; To &amp;quot;wake up&amp;quot; a button for a Top Asset that already has children, simply Edit and Save one of its child records.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Asset-Chain_header_arrangement_with_top_asset&amp;diff=1590</id>
		<title>ICT:D Asset-Chain header arrangement with top asset</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Asset-Chain_header_arrangement_with_top_asset&amp;diff=1590"/>
		<updated>2026-03-29T22:20:37Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Drupal 11: Displaying Parent Asset Metadata in a Recursive View Header ==&lt;br /&gt;
&lt;br /&gt;
This guide explains how to display fields (Thumbnail, Caption) from a parent &amp;quot;Digital Asset&amp;quot; entity in the header of a View that lists its children. This uses the &amp;quot;View Area&amp;quot; method to maintain a single View configuration.&lt;br /&gt;
&lt;br /&gt;
=== Step 1: Create the &amp;quot;Header&amp;quot; Block Display ===&lt;br /&gt;
Instead of a new View, we add a specialized display to the existing one to keep the project organized.&lt;br /&gt;
&lt;br /&gt;
# Open your &#039;&#039;&#039;AssetChain&#039;&#039;&#039; View.&lt;br /&gt;
# Click the &#039;&#039;&#039;+ Add&#039;&#039;&#039; button and select &#039;&#039;&#039;Block&#039;&#039;&#039;.&lt;br /&gt;
# &#039;&#039;&#039;Crucial:&#039;&#039;&#039; Change the &amp;quot;Display name&amp;quot; (found under Display Settings) to &amp;quot;Parent Header Block&amp;quot;.&lt;br /&gt;
# In the center column, look for the dropdown that says &amp;quot;All displays&amp;quot; and change it to &#039;&#039;&#039;This block (override)&#039;&#039;&#039;.&lt;br /&gt;
# &#039;&#039;&#039;Fields:&#039;&#039;&#039; &lt;br /&gt;
#* Add your Thumbnail field.&lt;br /&gt;
#* Add your Asset Caption field.&lt;br /&gt;
# &#039;&#039;&#039;Pager:&#039;&#039;&#039; Set to &amp;quot;Display a specified number of items&amp;quot; and set the value to &#039;&#039;&#039;1&#039;&#039;&#039;.&lt;br /&gt;
# &#039;&#039;&#039;Contextual Filter:&#039;&#039;&#039; &lt;br /&gt;
#* Add &#039;&#039;&#039;Content: ID&#039;&#039;&#039; (This targets the parent asset directly).&lt;br /&gt;
#* Set &amp;quot;When the filter value is NOT available&amp;quot; to &#039;&#039;&#039;Hide view&#039;&#039;&#039;.&lt;br /&gt;
# Save the View.&lt;br /&gt;
&lt;br /&gt;
=== Step 2: Format the Header Layout ===&lt;br /&gt;
To make the thumbnail and caption sit side-by-side without a table, use a Custom Text field.&lt;br /&gt;
&lt;br /&gt;
# In the &#039;&#039;&#039;Parent Header Block&#039;&#039;&#039; display, add a &#039;&#039;&#039;Global: Custom text&#039;&#039;&#039; field.&lt;br /&gt;
# Move it to the bottom of the field list.&lt;br /&gt;
# Mark the original Thumbnail and Caption fields as &#039;&#039;&#039;Exclude from display&#039;&#039;&#039;.&lt;br /&gt;
# In the &amp;quot;Custom text&amp;quot; box, use the following HTML (Verify tokens in the &amp;quot;Replacement Patterns&amp;quot; list):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;parent-summary&amp;quot; style=&amp;quot;display: flex; gap: 20px; align-items: flex-start; margin-bottom: 20px; border: 1px solid #ccc; padding: 15px;&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;header-image&amp;quot;&amp;gt;&lt;br /&gt;
    {{ field_media_thumbnail }}&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;header-text&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h3 style=&amp;quot;margin-top: 0;&amp;quot;&amp;gt;Parent Asset Information&amp;lt;/h3&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{ field_asset_caption }}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Step 3: Embed the Block in the Main View Header ===&lt;br /&gt;
This step connects the &amp;quot;Parent Header&amp;quot; to your &amp;quot;Children Table.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Switch back to your &#039;&#039;&#039;Main View&#039;&#039;&#039; (the display that shows the table of children).&lt;br /&gt;
# In the &#039;&#039;&#039;Header&#039;&#039;&#039; section, click &#039;&#039;&#039;Add&#039;&#039;&#039;.&lt;br /&gt;
# Search for and select &#039;&#039;&#039;Global: View area&#039;&#039;&#039;.&lt;br /&gt;
# Configure the View Area as follows:&lt;br /&gt;
#* &#039;&#039;&#039;View to insert:&#039;&#039;&#039; Select &amp;quot;AssetChain&amp;quot;.&lt;br /&gt;
#* &#039;&#039;&#039;Display to insert:&#039;&#039;&#039; Select &amp;quot;Parent Header Block&amp;quot;.&lt;br /&gt;
#* &#039;&#039;&#039;Inherit contextual filters:&#039;&#039;&#039; Check this box. (This passes the ID from the URL to the header).&lt;br /&gt;
# Click &#039;&#039;&#039;Apply&#039;&#039;&#039; and &#039;&#039;&#039;Save&#039;&#039;&#039; the View.&lt;br /&gt;
&lt;br /&gt;
=== ICT Support Notes ===&lt;br /&gt;
* &#039;&#039;&#039;Recursive Logic:&#039;&#039;&#039; The header fetches the entity based on the current URL ID, while the table fetches children based on the reference field filter.&lt;br /&gt;
* &#039;&#039;&#039;Theming:&#039;&#039;&#039; If inline styles are forbidden by site policy, move the CSS from the {{code|&amp;lt;pre&amp;gt;}} block to the site&#039;s global stylesheet.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Asset-Chain_header_arrangement_with_top_asset&amp;diff=1589</id>
		<title>ICT:D Asset-Chain header arrangement with top asset</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Asset-Chain_header_arrangement_with_top_asset&amp;diff=1589"/>
		<updated>2026-03-29T22:16:31Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;== Drupal 11: Displaying Parent Asset Metadata in a Recursive View Header == This guide explains how to display fields (Thumbnail, Caption) from a parent &amp;quot;Digital Asset&amp;quot; entity in the header of a View that lists its children. === Prerequisites === &amp;#039;&amp;#039;&amp;#039;Content Type:&amp;#039;&amp;#039;&amp;#039; Digital Asset (containing fields: {{code|field_media_thumbnail}}, {{code|field_asset_caption}}). &amp;#039;&amp;#039;&amp;#039;Entity Reference:&amp;#039;&amp;#039;&amp;#039; A field (e.g., {{code|field_parent_asset}}) linking children to parents. &amp;#039;&amp;#039;&amp;#039;Main View:...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Drupal 11: Displaying Parent Asset Metadata in a Recursive View Header ==&lt;br /&gt;
This guide explains how to display fields (Thumbnail, Caption) from a parent &amp;quot;Digital Asset&amp;quot; entity&lt;br /&gt;
in the header of a View that lists its children.&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
&#039;&#039;&#039;Content Type:&#039;&#039;&#039; Digital Asset (containing fields: {{code|field_media_thumbnail}}, {{code|field_asset_caption}}).&lt;br /&gt;
&#039;&#039;&#039;Entity Reference:&#039;&#039;&#039; A field (e.g., {{code|field_parent_asset}}) linking children to parents.&lt;br /&gt;
&#039;&#039;&#039;Main View:&#039;&#039;&#039; An existing View (e.g., &#039;&#039;AssetChain&#039;&#039;) showing child assets in a table.&lt;br /&gt;
=== Step 1: Create the &amp;quot;Header&amp;quot; Block Display ===&lt;br /&gt;
Instead of a new View, we add a specialized display to the existing one.&lt;br /&gt;
Open your &#039;&#039;&#039;AssetChain&#039;&#039;&#039; View.&lt;br /&gt;
Click &#039;&#039;&#039;+ Add&#039;&#039;&#039; and select &#039;&#039;&#039;Block&#039;&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Crucial:&#039;&#039;&#039; Change the &amp;quot;Display name&amp;quot; to &amp;quot;Parent Header Block&amp;quot;.&lt;br /&gt;
In the center column, look for the dropdown that says &amp;quot;All displays&amp;quot; and change it to &#039;&#039;&#039;This block (override)&#039;&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Fields:&#039;&#039;&#039;&lt;br /&gt;
#* Add {{code|field_media_thumbnail}}.&lt;br /&gt;
#* Add {{code|field_asset_caption}}.&lt;br /&gt;
#* Remove any fields you don&#039;t want in the header (like the table columns).&lt;br /&gt;
&#039;&#039;&#039;Pager:&#039;&#039;&#039; Set to &amp;quot;Display a specified number of items&amp;quot; and set value to &#039;&#039;&#039;1&#039;&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Contextual Filter:&#039;&#039;&#039;&lt;br /&gt;
#* Add {{code|ID}} (from the Content category).&lt;br /&gt;
#* Set &amp;quot;When the filter value is NOT available&amp;quot; to &#039;&#039;&#039;Hide view&#039;&#039;&#039;.&lt;br /&gt;
Save the View.&lt;br /&gt;
=== Step 2: Format the Header Layout (Optional but Recommended) ===&lt;br /&gt;
To make the thumbnail and caption sit side-by-side:&lt;br /&gt;
In the &#039;&#039;&#039;Parent Header Block&#039;&#039;&#039; display, add a &#039;&#039;&#039;Global: Custom text&#039;&#039;&#039; field.&lt;br /&gt;
Move it to the bottom of the field list.&lt;br /&gt;
Mark the original Thumbnail and Caption fields as &#039;&#039;&#039;Exclude from display&#039;&#039;&#039;.&lt;br /&gt;
In the &amp;quot;Custom text&amp;quot; box, use the following HTML (adjust tokens based on &amp;quot;Replacement Patterns&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{ field_media_thumbnail }}&lt;br /&gt;
&lt;br /&gt;
Parent Asset&lt;br /&gt;
{{ field_asset_caption }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Step 3: Embed the Block in the Main View Header ===&lt;br /&gt;
Switch back to your &#039;&#039;&#039;Main View&#039;&#039;&#039; (the Page or Block that shows the table).&lt;br /&gt;
In the &#039;&#039;&#039;Header&#039;&#039;&#039; section, click &#039;&#039;&#039;Add&#039;&#039;&#039;.&lt;br /&gt;
Search for and select &#039;&#039;&#039;Global: View area&#039;&#039;&#039;.&lt;br /&gt;
Configure the View Area:&lt;br /&gt;
#* &#039;&#039;&#039;View to insert:&#039;&#039;&#039; Select &amp;quot;AssetChain&amp;quot;.&lt;br /&gt;
#* &#039;&#039;&#039;Display to insert:&#039;&#039;&#039; Select &amp;quot;Parent Header Block&amp;quot;.&lt;br /&gt;
#* &#039;&#039;&#039;Inherit contextual filters:&#039;&#039;&#039; [[File:Checked_checkbox.png]] &#039;&#039;&#039;CHECK THIS BOX&#039;&#039;&#039;. (This passes the Parent ID from the URL to the header).&lt;br /&gt;
Click &#039;&#039;&#039;Apply&#039;&#039;&#039; and &#039;&#039;&#039;Save&#039;&#039;&#039; the View.&lt;br /&gt;
=== Troubleshooting ===&lt;br /&gt;
&#039;&#039;&#039;Empty Header:&#039;&#039;&#039; If the header doesn&#039;t appear, check that the &amp;quot;Inherit contextual filters&amp;quot; box is checked in the View Area settings.&lt;br /&gt;
&#039;&#039;&#039;Wrong Asset:&#039;&#039;&#039; Ensure the Contextual Filter in the &amp;quot;Parent Header Block&amp;quot; is targeting the &#039;&#039;&#039;ID&#039;&#039;&#039; of the asset, not the reference field.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Linking_Assets_to_Person&amp;diff=1587</id>
		<title>ICT:D Linking Assets to Person</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Linking_Assets_to_Person&amp;diff=1587"/>
		<updated>2026-03-27T19:29:31Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Drupal: Linking Assets to Persons (MM Relationship Without Custom Code) =&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
This page documents the step‑by‑step implementation of a **many‑to‑many–style&lt;br /&gt;
relationship between Person and Asset entities** in Drupal 11.x.&lt;br /&gt;
&lt;br /&gt;
The objective is to allow editors to:&lt;br /&gt;
* link existing Assets to a Person,&lt;br /&gt;
* manage these links in a table‑based UI,&lt;br /&gt;
* search and select Assets via a modal browser,&lt;br /&gt;
* avoid custom modules and custom database tables.&lt;br /&gt;
&lt;br /&gt;
This solution uses **standard Drupal mechanisms** and is upgrade‑safe.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Design Principles ==&lt;br /&gt;
* Prefer configuration over code&lt;br /&gt;
* Avoid custom schema and join tables&lt;br /&gt;
* Use Drupal’s entity reference system&lt;br /&gt;
* Use contrib modules for UX, not data modeling&lt;br /&gt;
* Ensure maintainability and handover safety&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Conceptual Model ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Entity !! Role&lt;br /&gt;
|-&lt;br /&gt;
| Person || Main entity (e.g. historical person)&lt;br /&gt;
|-&lt;br /&gt;
| Asset || Reusable referenced entity (documents, images, objects, etc.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Relationship:&lt;br /&gt;
* One Person → many Assets&lt;br /&gt;
* One Asset → many Persons (implicit)&lt;br /&gt;
&lt;br /&gt;
Drupal handles this naturally via **entity references**.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Required Modules ==&lt;br /&gt;
Ensure the following modules are enabled:&lt;br /&gt;
&lt;br /&gt;
* Core:&lt;br /&gt;
** Field&lt;br /&gt;
** Views&lt;br /&gt;
* Contrib:&lt;br /&gt;
** Inline Entity Form (IEF)&lt;br /&gt;
** Entity Browser&lt;br /&gt;
&lt;br /&gt;
No custom module is required.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 1: Create the Entity Reference Field ==&lt;br /&gt;
&lt;br /&gt;
On the **Person** entity:&lt;br /&gt;
&lt;br /&gt;
1. Go to:&lt;br /&gt;
   &amp;lt;pre&amp;gt;&lt;br /&gt;
   Structure → Content types → Person → Manage fields&lt;br /&gt;
   &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Add a new field:&lt;br /&gt;
   * Field type: &#039;&#039;&#039;Entity reference&#039;&#039;&#039;&lt;br /&gt;
   * Label: &amp;lt;code&amp;gt;Assets&amp;lt;/code&amp;gt;&lt;br /&gt;
   * Machine name: &amp;lt;code&amp;gt;field_assets&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. Reference settings:&lt;br /&gt;
   * Target type: &amp;lt;code&amp;gt;Asset&amp;lt;/code&amp;gt;&lt;br /&gt;
   * Allowed bundles: select relevant Asset bundles&lt;br /&gt;
   * Cardinality: &#039;&#039;&#039;Unlimited&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Save the field.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 2: Configure the Form Widget (Critical Step) ==&lt;br /&gt;
&lt;br /&gt;
1. Go to:&lt;br /&gt;
   &amp;lt;pre&amp;gt;&lt;br /&gt;
   Manage form display&lt;br /&gt;
   &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. For &amp;lt;code&amp;gt;field_assets&amp;lt;/code&amp;gt;, select widget:&lt;br /&gt;
   * &#039;&#039;&#039;Inline Entity Form – Complex&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
3. Open the widget settings (gear icon):&lt;br /&gt;
&lt;br /&gt;
   * ✅ Allow users to add existing entities&lt;br /&gt;
   * ❌ Allow users to add new entities&lt;br /&gt;
   * Entity Browser: select the Asset browser (configured in Step 3)&lt;br /&gt;
&lt;br /&gt;
4. Save the form display.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039;&lt;br /&gt;
Do NOT select “Entity Browser” as the field widget itself.&lt;br /&gt;
That widget does not provide a table‑based UI.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 3: Create the Entity Browser for Assets ==&lt;br /&gt;
&lt;br /&gt;
1. Go to:&lt;br /&gt;
   &amp;lt;pre&amp;gt;&lt;br /&gt;
   Configuration → Content authoring → Entity Browser&lt;br /&gt;
   &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Create a new browser:&lt;br /&gt;
   * Name: &amp;lt;code&amp;gt;asset_browser&amp;lt;/code&amp;gt;&lt;br /&gt;
   * Display: Modal&lt;br /&gt;
   * Selection mode: Multi‑select&lt;br /&gt;
&lt;br /&gt;
3. Add a widget:&lt;br /&gt;
   * Widget type: View&lt;br /&gt;
&lt;br /&gt;
4. Create or select a View:&lt;br /&gt;
   * Base table: Asset&lt;br /&gt;
   * Display type: Entity Browser&lt;br /&gt;
   * Pagination: 10–25 items per page&lt;br /&gt;
   * Exposed filters:&lt;br /&gt;
     - Title&lt;br /&gt;
     - Type / taxonomy (as needed)&lt;br /&gt;
   * Fields:&lt;br /&gt;
     - Thumbnail (image style)&lt;br /&gt;
     - Title&lt;br /&gt;
     - Optional metadata&lt;br /&gt;
&lt;br /&gt;
5. Save the Entity Browser.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 4: Resulting Editor UX ==&lt;br /&gt;
&lt;br /&gt;
On the Person edit form:&lt;br /&gt;
&lt;br /&gt;
* Assets are displayed in a table&lt;br /&gt;
* Editors can:&lt;br /&gt;
  - Add existing Assets&lt;br /&gt;
  - Remove Assets&lt;br /&gt;
  - Reorder Assets&lt;br /&gt;
* Clicking “Add existing Asset” opens a modal browser&lt;br /&gt;
* Assets are searched and selected visually&lt;br /&gt;
&lt;br /&gt;
This provides a historian‑friendly workflow.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 4a: Table Representation on the Person Edit Form ==&lt;br /&gt;
&lt;br /&gt;
The table‑based representation of linked Assets on the Person edit form&lt;br /&gt;
is provided automatically by the **Inline Entity Form – Complex** widget.&lt;br /&gt;
&lt;br /&gt;
This step documents how the table is configured and what editors see.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== How the Table Is Generated ===&lt;br /&gt;
When the field &amp;lt;code&amp;gt;field_assets&amp;lt;/code&amp;gt; is configured with:&lt;br /&gt;
&lt;br /&gt;
* Widget: &#039;&#039;&#039;Inline Entity Form – Complex&#039;&#039;&#039;&lt;br /&gt;
* Cardinality: Unlimited&lt;br /&gt;
&lt;br /&gt;
Drupal renders the referenced Assets as a **tabular list** instead of&lt;br /&gt;
individual widgets.&lt;br /&gt;
&lt;br /&gt;
No custom Views, no custom code, and no join tables are involved.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Default Table Columns ===&lt;br /&gt;
Out of the box, the table typically contains:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Column !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| Label / Title || Identifies the linked Asset&lt;br /&gt;
|-&lt;br /&gt;
| Type / Bundle || Shows the Asset type (if applicable)&lt;br /&gt;
|-&lt;br /&gt;
| Operations || Remove / Edit actions&lt;br /&gt;
|-&lt;br /&gt;
| Weight || Drag‑and‑drop ordering&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The exact columns depend on:&lt;br /&gt;
* Asset entity type&lt;br /&gt;
* Enabled fields&lt;br /&gt;
* Display settings&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Editor Capabilities ===&lt;br /&gt;
Editors can:&lt;br /&gt;
&lt;br /&gt;
* ✅ Add existing Assets (via Entity Browser)&lt;br /&gt;
* ✅ Remove Assets from the Person&lt;br /&gt;
* ✅ Reorder Assets using drag‑and‑drop&lt;br /&gt;
* ✅ See all linked Assets at once&lt;br /&gt;
&lt;br /&gt;
This makes the relationship **explicit and transparent**, which is&lt;br /&gt;
important for historical research workflows.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Why This Table Matters ===&lt;br /&gt;
Compared to alternative approaches:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Approach !! Result&lt;br /&gt;
|-&lt;br /&gt;
| Autocomplete reference || Poor overview&lt;br /&gt;
|-&lt;br /&gt;
| Pills / tags || No metadata, no ordering&lt;br /&gt;
|-&lt;br /&gt;
| Custom join table || High complexity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;IEF table&#039;&#039;&#039; || ✅ Clear, structured, maintainable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The IEF table offers the best balance between:&lt;br /&gt;
* usability&lt;br /&gt;
* transparency&lt;br /&gt;
* maintainability&lt;br /&gt;
* upgrade safety&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Configuration Notes ===&lt;br /&gt;
* No additional configuration is required beyond selecting&lt;br /&gt;
  &#039;&#039;&#039;Inline Entity Form – Complex&#039;&#039;&#039;.&lt;br /&gt;
* The table appears automatically on the Person edit form.&lt;br /&gt;
* Display styling is handled by the admin theme (Gin).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Extensibility ===&lt;br /&gt;
Later enhancements (optional):&lt;br /&gt;
&lt;br /&gt;
* Add thumbnail columns&lt;br /&gt;
* Show additional Asset metadata&lt;br /&gt;
* Adjust column order or visibility&lt;br /&gt;
&lt;br /&gt;
These enhancements can be achieved without changing the data model.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Step 5: UX Cleanup – Hide the Misleading “Add node” Button ==&lt;br /&gt;
&lt;br /&gt;
Inline Entity Form displays an intermediate chooser containing an&lt;br /&gt;
“Add node” button, which is misleading when creation is disabled.&lt;br /&gt;
&lt;br /&gt;
This is hidden via Gin admin CSS.&lt;br /&gt;
&lt;br /&gt;
=== CSS Location ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sites/default/files/gin-custom.css&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CSS Rule ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* Hide misleading &amp;quot;Add node&amp;quot; button in Inline Entity Form chooser */&lt;br /&gt;
input.ief-entity-submit[data-drupal-selector*=&amp;quot;ief-reference-save&amp;quot;][value=&amp;quot;Add node&amp;quot;] {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This solution:&lt;br /&gt;
* requires no JavaScript&lt;br /&gt;
* survives AJAX rendering&lt;br /&gt;
* is upgrade‑safe&lt;br /&gt;
* does not depend on dialog wrappers&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Why No Join Table Was Used ==&lt;br /&gt;
An experimental approach using a “real” join table was evaluated and rejected because:&lt;br /&gt;
&lt;br /&gt;
* it required a custom module&lt;br /&gt;
* it required schema lifecycle management&lt;br /&gt;
* UI construction required many hooks&lt;br /&gt;
* upgrades would require maintenance work&lt;br /&gt;
&lt;br /&gt;
Drupal’s entity reference system already models the relationship correctly.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Performance Notes ==&lt;br /&gt;
* 1000+ Assets are handled without issue&lt;br /&gt;
* Only referenced Assets are rendered on the Person form&lt;br /&gt;
* Entity Browser uses pagination and indexed Views&lt;br /&gt;
* Thumbnails should always use image styles&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
This implementation achieves:&lt;br /&gt;
&lt;br /&gt;
* ✅ Many‑to‑many relationship semantics&lt;br /&gt;
* ✅ Clean table‑based UI&lt;br /&gt;
* ✅ Visual asset selection&lt;br /&gt;
* ✅ No custom code&lt;br /&gt;
* ✅ No custom schema&lt;br /&gt;
* ✅ Upgrade‑safe architecture&lt;br /&gt;
&lt;br /&gt;
This is the recommended approach for modern Drupal (11.x).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Reusability and Generalization of the Asset Linking Pattern ==&lt;br /&gt;
&lt;br /&gt;
=== Purpose of This Section ===&lt;br /&gt;
The procedure described above is **not limited to Person → Asset**.&lt;br /&gt;
It defines a **reusable architectural pattern** for linking Assets to&lt;br /&gt;
any other entity type in Drupal.&lt;br /&gt;
&lt;br /&gt;
This pattern enables true many‑to‑many semantics without custom code.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Core Idea ===&lt;br /&gt;
The solution separates concerns:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Concern !! Mechanism&lt;br /&gt;
|-&lt;br /&gt;
| Relationship modeling || Entity reference field&lt;br /&gt;
|-&lt;br /&gt;
| Selection UX || Entity Browser&lt;br /&gt;
|-&lt;br /&gt;
| Management UX || Inline Entity Form (Complex)&lt;br /&gt;
|-&lt;br /&gt;
| Data storage || Drupal core entity system&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because of this separation, the same components can be reused.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Reusing the Entity Browser ===&lt;br /&gt;
The **Asset Entity Browser**:&lt;br /&gt;
&lt;br /&gt;
* is generic&lt;br /&gt;
* operates on the Asset entity type&lt;br /&gt;
* does not depend on Person&lt;br /&gt;
&lt;br /&gt;
Therefore, it can be reused **as‑is** for other entities.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
* Object → Asset&lt;br /&gt;
* Event → Asset&lt;br /&gt;
* Place → Asset&lt;br /&gt;
* Organisation → Asset&lt;br /&gt;
&lt;br /&gt;
No changes to the browser are required.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Reusing the Pattern for Other Entities ===&lt;br /&gt;
&lt;br /&gt;
To link Assets to another entity (e.g. Object):&lt;br /&gt;
&lt;br /&gt;
1. Add an **Entity reference** field on the target entity:&lt;br /&gt;
   * Target type: Asset&lt;br /&gt;
   * Cardinality: Unlimited&lt;br /&gt;
&lt;br /&gt;
2. Configure the form widget:&lt;br /&gt;
   * Widget: &#039;&#039;&#039;Inline Entity Form – Complex&#039;&#039;&#039;&lt;br /&gt;
   * Allow existing entities: ✅&lt;br /&gt;
   * Allow new entities: ❌&lt;br /&gt;
   * Entity Browser: reuse the existing Asset browser&lt;br /&gt;
&lt;br /&gt;
3. Save the form display.&lt;br /&gt;
&lt;br /&gt;
The result is identical:&lt;br /&gt;
* table‑based Asset management&lt;br /&gt;
* modal Asset selection&lt;br /&gt;
* no custom schema&lt;br /&gt;
* no custom code&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Resulting Many‑to‑Many Relationship ===&lt;br /&gt;
When applied consistently:&lt;br /&gt;
&lt;br /&gt;
* One Asset can be linked to many Persons&lt;br /&gt;
* One Asset can be linked to many Objects&lt;br /&gt;
* One Person can link many Assets&lt;br /&gt;
* One Object can link many Assets&lt;br /&gt;
&lt;br /&gt;
Drupal handles this naturally via entity references.&lt;br /&gt;
&lt;br /&gt;
No explicit join table is required.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Why This Works in Drupal ===&lt;br /&gt;
Drupal entities are first‑class, reusable objects.&lt;br /&gt;
Entity references already implement relational semantics.&lt;br /&gt;
&lt;br /&gt;
By reusing:&lt;br /&gt;
* the same Asset entity&lt;br /&gt;
* the same Entity Browser&lt;br /&gt;
* the same IEF table UI&lt;br /&gt;
&lt;br /&gt;
the system behaves like a relational many‑to‑many model,&lt;br /&gt;
while remaining configuration‑driven and upgrade‑safe.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Architectural Benefit ===&lt;br /&gt;
This approach:&lt;br /&gt;
&lt;br /&gt;
* ✅ avoids schema duplication&lt;br /&gt;
* ✅ avoids custom join tables&lt;br /&gt;
* ✅ avoids module proliferation&lt;br /&gt;
* ✅ enforces UX consistency&lt;br /&gt;
* ✅ scales across entity types&lt;br /&gt;
* ✅ simplifies maintenance and handover&lt;br /&gt;
&lt;br /&gt;
This is the recommended approach for modern Drupal (11.x).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Key Takeaway ===&lt;br /&gt;
Once the Asset linking pattern is implemented once,&lt;br /&gt;
it becomes a **reusable building block**.&lt;br /&gt;
&lt;br /&gt;
New relationships are created by configuration,&lt;br /&gt;
not by programming.&lt;br /&gt;
``&lt;br /&gt;
&lt;br /&gt;
== Key Lesson ==&lt;br /&gt;
If a solution requires:&lt;br /&gt;
* custom join tables&lt;br /&gt;
* extensive hook usage&lt;br /&gt;
* complex UI overrides&lt;br /&gt;
&lt;br /&gt;
…it is often a sign that Drupal already offers a better abstraction.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Linking_Assets_to_Person&amp;diff=1586</id>
		<title>ICT:D Linking Assets to Person</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Linking_Assets_to_Person&amp;diff=1586"/>
		<updated>2026-03-27T19:22:33Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Drupal: Linking Assets to Persons (MM Relationship Without Custom Code) =  == Purpose == This page documents the step‑by‑step implementation of a **many‑to‑many–style relationship between Person and Asset entities** in Drupal 11.x.  The objective is to allow editors to: * link existing Assets to a Person, * manage these links in a table‑based UI, * search and select Assets via a modal browser, * avoid custom modules and custom database tables.  This solutio...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Drupal: Linking Assets to Persons (MM Relationship Without Custom Code) =&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
This page documents the step‑by‑step implementation of a **many‑to‑many–style&lt;br /&gt;
relationship between Person and Asset entities** in Drupal 11.x.&lt;br /&gt;
&lt;br /&gt;
The objective is to allow editors to:&lt;br /&gt;
* link existing Assets to a Person,&lt;br /&gt;
* manage these links in a table‑based UI,&lt;br /&gt;
* search and select Assets via a modal browser,&lt;br /&gt;
* avoid custom modules and custom database tables.&lt;br /&gt;
&lt;br /&gt;
This solution uses **standard Drupal mechanisms** and is upgrade‑safe.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Design Principles ==&lt;br /&gt;
* Prefer configuration over code&lt;br /&gt;
* Avoid custom schema and join tables&lt;br /&gt;
* Use Drupal’s entity reference system&lt;br /&gt;
* Use contrib modules for UX, not data modeling&lt;br /&gt;
* Ensure maintainability and handover safety&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Conceptual Model ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Entity !! Role&lt;br /&gt;
|-&lt;br /&gt;
| Person || Main entity (e.g. historical person)&lt;br /&gt;
|-&lt;br /&gt;
| Asset || Reusable referenced entity (documents, images, objects, etc.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Relationship:&lt;br /&gt;
* One Person → many Assets&lt;br /&gt;
* One Asset → many Persons (implicit)&lt;br /&gt;
&lt;br /&gt;
Drupal handles this naturally via **entity references**.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Required Modules ==&lt;br /&gt;
Ensure the following modules are enabled:&lt;br /&gt;
&lt;br /&gt;
* Core:&lt;br /&gt;
** Field&lt;br /&gt;
** Views&lt;br /&gt;
* Contrib:&lt;br /&gt;
** Inline Entity Form (IEF)&lt;br /&gt;
** Entity Browser&lt;br /&gt;
&lt;br /&gt;
No custom module is required.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 1: Create the Entity Reference Field ==&lt;br /&gt;
&lt;br /&gt;
On the **Person** entity:&lt;br /&gt;
&lt;br /&gt;
1. Go to:&lt;br /&gt;
   &amp;lt;pre&amp;gt;&lt;br /&gt;
   Structure → Content types → Person → Manage fields&lt;br /&gt;
   &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Add a new field:&lt;br /&gt;
   * Field type: &#039;&#039;&#039;Entity reference&#039;&#039;&#039;&lt;br /&gt;
   * Label: &amp;lt;code&amp;gt;Assets&amp;lt;/code&amp;gt;&lt;br /&gt;
   * Machine name: &amp;lt;code&amp;gt;field_assets&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. Reference settings:&lt;br /&gt;
   * Target type: &amp;lt;code&amp;gt;Asset&amp;lt;/code&amp;gt;&lt;br /&gt;
   * Allowed bundles: select relevant Asset bundles&lt;br /&gt;
   * Cardinality: &#039;&#039;&#039;Unlimited&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Save the field.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 2: Configure the Form Widget (Critical Step) ==&lt;br /&gt;
&lt;br /&gt;
1. Go to:&lt;br /&gt;
   &amp;lt;pre&amp;gt;&lt;br /&gt;
   Manage form display&lt;br /&gt;
   &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. For &amp;lt;code&amp;gt;field_assets&amp;lt;/code&amp;gt;, select widget:&lt;br /&gt;
   * &#039;&#039;&#039;Inline Entity Form – Complex&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
3. Open the widget settings (gear icon):&lt;br /&gt;
&lt;br /&gt;
   * ✅ Allow users to add existing entities&lt;br /&gt;
   * ❌ Allow users to add new entities&lt;br /&gt;
   * Entity Browser: select the Asset browser (configured in Step 3)&lt;br /&gt;
&lt;br /&gt;
4. Save the form display.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039;&lt;br /&gt;
Do NOT select “Entity Browser” as the field widget itself.&lt;br /&gt;
That widget does not provide a table‑based UI.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 3: Create the Entity Browser for Assets ==&lt;br /&gt;
&lt;br /&gt;
1. Go to:&lt;br /&gt;
   &amp;lt;pre&amp;gt;&lt;br /&gt;
   Configuration → Content authoring → Entity Browser&lt;br /&gt;
   &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Create a new browser:&lt;br /&gt;
   * Name: &amp;lt;code&amp;gt;asset_browser&amp;lt;/code&amp;gt;&lt;br /&gt;
   * Display: Modal&lt;br /&gt;
   * Selection mode: Multi‑select&lt;br /&gt;
&lt;br /&gt;
3. Add a widget:&lt;br /&gt;
   * Widget type: View&lt;br /&gt;
&lt;br /&gt;
4. Create or select a View:&lt;br /&gt;
   * Base table: Asset&lt;br /&gt;
   * Display type: Entity Browser&lt;br /&gt;
   * Pagination: 10–25 items per page&lt;br /&gt;
   * Exposed filters:&lt;br /&gt;
     - Title&lt;br /&gt;
     - Type / taxonomy (as needed)&lt;br /&gt;
   * Fields:&lt;br /&gt;
     - Thumbnail (image style)&lt;br /&gt;
     - Title&lt;br /&gt;
     - Optional metadata&lt;br /&gt;
&lt;br /&gt;
5. Save the Entity Browser.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 4: Resulting Editor UX ==&lt;br /&gt;
&lt;br /&gt;
On the Person edit form:&lt;br /&gt;
&lt;br /&gt;
* Assets are displayed in a table&lt;br /&gt;
* Editors can:&lt;br /&gt;
  - Add existing Assets&lt;br /&gt;
  - Remove Assets&lt;br /&gt;
  - Reorder Assets&lt;br /&gt;
* Clicking “Add existing Asset” opens a modal browser&lt;br /&gt;
* Assets are searched and selected visually&lt;br /&gt;
&lt;br /&gt;
This provides a historian‑friendly workflow.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Step 5: UX Cleanup – Hide the Misleading “Add node” Button ==&lt;br /&gt;
&lt;br /&gt;
Inline Entity Form displays an intermediate chooser containing an&lt;br /&gt;
“Add node” button, which is misleading when creation is disabled.&lt;br /&gt;
&lt;br /&gt;
This is hidden via Gin admin CSS.&lt;br /&gt;
&lt;br /&gt;
=== CSS Location ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sites/default/files/gin-custom.css&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CSS Rule ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* Hide misleading &amp;quot;Add node&amp;quot; button in Inline Entity Form chooser */&lt;br /&gt;
input.ief-entity-submit[data-drupal-selector*=&amp;quot;ief-reference-save&amp;quot;][value=&amp;quot;Add node&amp;quot;] {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This solution:&lt;br /&gt;
* requires no JavaScript&lt;br /&gt;
* survives AJAX rendering&lt;br /&gt;
* is upgrade‑safe&lt;br /&gt;
* does not depend on dialog wrappers&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Why No Join Table Was Used ==&lt;br /&gt;
An experimental approach using a “real” join table was evaluated and rejected because:&lt;br /&gt;
&lt;br /&gt;
* it required a custom module&lt;br /&gt;
* it required schema lifecycle management&lt;br /&gt;
* UI construction required many hooks&lt;br /&gt;
* upgrades would require maintenance work&lt;br /&gt;
&lt;br /&gt;
Drupal’s entity reference system already models the relationship correctly.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Performance Notes ==&lt;br /&gt;
* 1000+ Assets are handled without issue&lt;br /&gt;
* Only referenced Assets are rendered on the Person form&lt;br /&gt;
* Entity Browser uses pagination and indexed Views&lt;br /&gt;
* Thumbnails should always use image styles&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
This implementation achieves:&lt;br /&gt;
&lt;br /&gt;
* ✅ Many‑to‑many relationship semantics&lt;br /&gt;
* ✅ Clean table‑based UI&lt;br /&gt;
* ✅ Visual asset selection&lt;br /&gt;
* ✅ No custom code&lt;br /&gt;
* ✅ No custom schema&lt;br /&gt;
* ✅ Upgrade‑safe architecture&lt;br /&gt;
&lt;br /&gt;
This is the recommended approach for modern Drupal (11.x).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Key Lesson ==&lt;br /&gt;
If a solution requires:&lt;br /&gt;
* custom join tables&lt;br /&gt;
* extensive hook usage&lt;br /&gt;
* complex UI overrides&lt;br /&gt;
&lt;br /&gt;
…it is often a sign that Drupal already offers a better abstraction.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Cleaning_up_Unused_Custom_Modules_(Composer_-_Drush)&amp;diff=1580</id>
		<title>ICT:D Cleaning up Unused Custom Modules (Composer - Drush)</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Cleaning_up_Unused_Custom_Modules_(Composer_-_Drush)&amp;diff=1580"/>
		<updated>2026-03-27T17:45:43Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Drupal: Cleaning Up Unused Custom Modules (Composer + Drush) =  == Purpose == This page documents the **correct and safe procedure** for removing unused or experimental **custom Drupal modules**, including:  * database cleanup * configuration cleanup * file system cleanup  The procedure avoids common pitfalls and is suitable for long‑lived Drupal installations that must remain upgrade‑safe.  ---  == Important Principles == Drupal deliberately separates:  {| class=&amp;quot;...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Drupal: Cleaning Up Unused Custom Modules (Composer + Drush) =&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
This page documents the **correct and safe procedure** for removing&lt;br /&gt;
unused or experimental **custom Drupal modules**, including:&lt;br /&gt;
&lt;br /&gt;
* database cleanup&lt;br /&gt;
* configuration cleanup&lt;br /&gt;
* file system cleanup&lt;br /&gt;
&lt;br /&gt;
The procedure avoids common pitfalls and is suitable for long‑lived&lt;br /&gt;
Drupal installations that must remain upgrade‑safe.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Important Principles ==&lt;br /&gt;
Drupal deliberately separates:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Aspect !! Managed by&lt;br /&gt;
|-&lt;br /&gt;
| Module enable/disable || Drupal&lt;br /&gt;
|-&lt;br /&gt;
| Configuration &amp;amp; schema cleanup || Module uninstall logic&lt;br /&gt;
|-&lt;br /&gt;
| Module files on disk || You / Composer&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Drupal never deletes module files automatically.&#039;&#039;&#039;&lt;br /&gt;
This is a security and deployment design choice.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Never Do This ==&lt;br /&gt;
* ❌ Do NOT delete module files before uninstalling&lt;br /&gt;
* ❌ Do NOT drop tables while the module is still enabled&lt;br /&gt;
* ❌ Do NOT rely on Drupal to clean up experimental schema automatically&lt;br /&gt;
&lt;br /&gt;
Doing so may leave broken configuration or database artifacts.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Correct Cleanup Procedure ==&lt;br /&gt;
&lt;br /&gt;
=== Step 1: Verify the Module Is No Longer Used ===&lt;br /&gt;
Before removal, ensure:&lt;br /&gt;
* no content depends on the module&lt;br /&gt;
* no fields, entities, or views reference it&lt;br /&gt;
* no other module lists it as a dependency&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Step 2: Uninstall the Module (Mandatory) ===&lt;br /&gt;
&lt;br /&gt;
Uninstalling runs:&lt;br /&gt;
* &amp;lt;code&amp;gt;hook_uninstall()&amp;lt;/code&amp;gt;&lt;br /&gt;
* configuration cleanup&lt;br /&gt;
* entity cleanup (if implemented correctly)&lt;br /&gt;
&lt;br /&gt;
==== Using Drush (recommended) ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
drush pmu my_custom_module&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Using the Admin UI ====&lt;br /&gt;
* Go to &amp;lt;code&amp;gt;Extend → Uninstall&amp;lt;/code&amp;gt;&lt;br /&gt;
* Select the module&lt;br /&gt;
* Confirm uninstall&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039;&lt;br /&gt;
Disabling a module is NOT sufficient.  &lt;br /&gt;
You must uninstall it.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Step 3: Remove the Module Files ===&lt;br /&gt;
&lt;br /&gt;
Drupal will NOT remove files from disk.&lt;br /&gt;
&lt;br /&gt;
==== Custom module (manual) ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rm -rf modules/custom/my_custom_module&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Contrib module (Composer-managed) ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
composer remove drupal/my_module&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This removes:&lt;br /&gt;
* the module directory&lt;br /&gt;
* composer.json / composer.lock references&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Step 4: Clear Caches ===&lt;br /&gt;
After file removal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
drush cr&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures Drupal no longer tries to discover the module.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Database Cleanup (If Needed) ==&lt;br /&gt;
&lt;br /&gt;
=== Custom Tables ===&lt;br /&gt;
Drupal does NOT drop custom tables automatically unless explicitly coded.&lt;br /&gt;
&lt;br /&gt;
If the module created a “real” join table or custom schema:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP TABLE my_custom_join_table;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only do this:&lt;br /&gt;
* after uninstalling the module&lt;br /&gt;
* when you are certain the table is unused&lt;br /&gt;
&lt;br /&gt;
This is normal for experimental schema.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== How to Check for Leftovers ===&lt;br /&gt;
Search the database for the module name:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grep my_custom_module database_dump.sql&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or inspect:&lt;br /&gt;
* &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;key_value&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;cache_*&amp;lt;/code&amp;gt; tables (usually cleared by cache rebuild)&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Common Error Scenario ==&lt;br /&gt;
If files are deleted before uninstalling:&lt;br /&gt;
&lt;br /&gt;
* Drupal reports:&lt;br /&gt;
  “The following module is missing from the file system…”&lt;br /&gt;
* Uninstall hooks never ran&lt;br /&gt;
* Manual DB cleanup is required&lt;br /&gt;
&lt;br /&gt;
This is why uninstall must always come first.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Why Drupal Works This Way ==&lt;br /&gt;
Drupal avoids automatic file deletion because:&lt;br /&gt;
&lt;br /&gt;
* web servers should not delete their own code&lt;br /&gt;
* production file systems are often read‑only&lt;br /&gt;
* deployments are controlled externally&lt;br /&gt;
* safety is preferred over convenience&lt;br /&gt;
&lt;br /&gt;
This design supports secure, professional deployments.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Architectural Lesson ==&lt;br /&gt;
Custom modules:&lt;br /&gt;
* create lifecycle responsibility&lt;br /&gt;
* require manual cleanup&lt;br /&gt;
* increase long‑term maintenance cost&lt;br /&gt;
&lt;br /&gt;
Configuration‑driven solutions:&lt;br /&gt;
* leave no schema debris&lt;br /&gt;
* uninstall cleanly&lt;br /&gt;
* survive core upgrades&lt;br /&gt;
&lt;br /&gt;
This cleanup step is not housekeeping — it is architectural alignment.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Summary Checklist ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step !! Required&lt;br /&gt;
|-&lt;br /&gt;
| Uninstall module (pmu) || ✅ Yes&lt;br /&gt;
|-&lt;br /&gt;
| Remove module files || ✅ Yes&lt;br /&gt;
|-&lt;br /&gt;
| Clear caches || ✅ Yes&lt;br /&gt;
|-&lt;br /&gt;
| Drop custom tables (if any) || ✅ Manual&lt;br /&gt;
|-&lt;br /&gt;
| Verify no leftovers || ✅ Recommended&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Final Note ==&lt;br /&gt;
Experimental work is valuable.&lt;br /&gt;
&lt;br /&gt;
Cleaning it up properly:&lt;br /&gt;
* preserves system integrity&lt;br /&gt;
* simplifies upgrades&lt;br /&gt;
* helps your successor&lt;br /&gt;
* reflects professional discipline&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Media-Library_uploading_one_file_in_an_Asset&amp;diff=1579</id>
		<title>ICT:D Media-Library uploading one file in an Asset</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Media-Library_uploading_one_file_in_an_Asset&amp;diff=1579"/>
		<updated>2026-03-27T16:33:49Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Media Library use in Asset for uploading ONE single media file =&lt;br /&gt;
&lt;br /&gt;
== Media field edit in Asset ==&lt;br /&gt;
* is an Entity Reference to Media with types: Audio, Document, Image and Video&lt;br /&gt;
* Required field&lt;br /&gt;
* Referencemethod to default&lt;br /&gt;
* Limited to 1  (very important - one asset one file)&lt;br /&gt;
&lt;br /&gt;
== Media in Manage form display ==&lt;br /&gt;
* Widget = Media Library&lt;br /&gt;
* Tab order: Image, Document, Video, Audio&lt;br /&gt;
&lt;br /&gt;
== Media in Manage display ==&lt;br /&gt;
* View mode = Media Library&lt;br /&gt;
&lt;br /&gt;
After this configurtion there is an important GUI finetuning needed to simplify the interface and mask unneeded filters and buttons.&lt;br /&gt;
Therefor the next chapter is very important.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Media Library Widget UI Simplification (CSS Overrides) =&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
This document records the CSS overrides applied to the **Drupal Media Library widget**&lt;br /&gt;
to simplify the user interface for editors.&lt;br /&gt;
&lt;br /&gt;
The goal is to:&lt;br /&gt;
* reduce cognitive load&lt;br /&gt;
* hide internal mechanics (filters, buttons)&lt;br /&gt;
* provide a clean, focused media selection experience&lt;br /&gt;
* preserve full Media Library functionality&lt;br /&gt;
&lt;br /&gt;
These overrides are **intentional**, **documented**, and **part of the UI contract**.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Context ==&lt;br /&gt;
The Media Library widget is powered by:&lt;br /&gt;
* a Drupal View (for listing media)&lt;br /&gt;
* exposed filters (search, filtering)&lt;br /&gt;
* a jQuery UI modal dialog&lt;br /&gt;
&lt;br /&gt;
By default, the UI exposes:&lt;br /&gt;
* filter forms&lt;br /&gt;
* layout switches&lt;br /&gt;
* confirmation button bars&lt;br /&gt;
&lt;br /&gt;
For this project, these elements are **not required** and confuse users.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Design Principle ==&lt;br /&gt;
Editors should:&lt;br /&gt;
* focus on selecting or replacing a single media item&lt;br /&gt;
* not be exposed to filtering mechanics&lt;br /&gt;
* not be presented with redundant confirmation actions&lt;br /&gt;
&lt;br /&gt;
Filtering and constraints are applied **silently** via Views configuration.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Applied CSS Overrides ==&lt;br /&gt;
&lt;br /&gt;
The following CSS rules are applied to the admin theme (e.g. gin-custom.css).&lt;br /&gt;
&lt;br /&gt;
=== 1. Hide the exposed filter form ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* Hide the Views exposed filter form inside Media Library */&lt;br /&gt;
.media-library-view .views-exposed-form {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effect:&lt;br /&gt;
* Removes the visible filter form from the Media Library modal&lt;br /&gt;
* Filters continue to work internally&lt;br /&gt;
* Prevents users from interacting with unintended controls&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== 2. Hide the dialog button bar (Select / Cancel) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* Hide the bottom button bar in the Media Library modal */&lt;br /&gt;
.ui-dialog--media-library .ui-dialog-buttonpane {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effect:&lt;br /&gt;
* Removes the redundant “Select / Cancel” button pane&lt;br /&gt;
* Selection happens directly via clicking media items&lt;br /&gt;
* Avoids duplicate confirmation paths&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* Media Library uses jQuery UI dialogs&lt;br /&gt;
* Hiding the button pane via CSS is a supported and safe approach&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== 3. Hide the Grid / Table display switch ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* Hide the Grid / Table view switch */&lt;br /&gt;
.media-library-view--display-switch {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effect:&lt;br /&gt;
* Enforces a single, consistent layout&lt;br /&gt;
* Removes unnecessary UI choices&lt;br /&gt;
* Simplifies training and usage&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== What These Overrides Do NOT Do ==&lt;br /&gt;
&lt;br /&gt;
These CSS rules:&lt;br /&gt;
* do NOT affect data&lt;br /&gt;
* do NOT alter Media entities&lt;br /&gt;
* do NOT disable Media Library functionality&lt;br /&gt;
* do NOT change Views filtering logic&lt;br /&gt;
* do NOT interfere with file replacement&lt;br /&gt;
&lt;br /&gt;
They are **presentation‑only**.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Operational Notes ==&lt;br /&gt;
&lt;br /&gt;
* These CSS rules are required for correct UI behavior&lt;br /&gt;
* Removing them may:&lt;br /&gt;
  * reintroduce clutter&lt;br /&gt;
  * confuse editors&lt;br /&gt;
  * appear to “break” Media Library UX&lt;br /&gt;
* They must be restored after experiments or refactoring&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Maintenance Recommendation ==&lt;br /&gt;
Maintain a dedicated section in documentation for:&lt;br /&gt;
* UI‑level CSS overrides&lt;br /&gt;
* why they exist&lt;br /&gt;
* where they are applied&lt;br /&gt;
&lt;br /&gt;
This prevents accidental removal during future development.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Area !! Decision&lt;br /&gt;
|-&lt;br /&gt;
| Media selection UI || Simplified via CSS&lt;br /&gt;
|-&lt;br /&gt;
| Filters || Hidden visually, active internally&lt;br /&gt;
|-&lt;br /&gt;
| Confirmation buttons || Removed to avoid redundancy&lt;br /&gt;
|-&lt;br /&gt;
| Layout switching || Disabled for consistency&lt;br /&gt;
|-&lt;br /&gt;
| Impact || UI only, no data or logic changes&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Appendix: Why CSS UI Simplification Is Required for Media Library Upload/Replace ==&lt;br /&gt;
&lt;br /&gt;
=== Observation ===&lt;br /&gt;
During testing, it was observed that when the Media Library modal UI elements&lt;br /&gt;
(exposed filters, dialog buttons, layout switches) were visible:&lt;br /&gt;
&lt;br /&gt;
* selecting an existing media item&lt;br /&gt;
* and attempting to upload or replace the file&lt;br /&gt;
&lt;br /&gt;
did NOT trigger the expected switch to the upload/replace interface.&lt;br /&gt;
&lt;br /&gt;
After restoring the CSS rules that hide these elements, the upload and replacement&lt;br /&gt;
behavior started working again immediately.&lt;br /&gt;
&lt;br /&gt;
At first sight this appears surprising, because CSS is normally considered&lt;br /&gt;
“presentation only”.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Explanation (Important) ===&lt;br /&gt;
&lt;br /&gt;
The Media Library widget is NOT a single, isolated component.&lt;br /&gt;
It is a composite UI made of:&lt;br /&gt;
&lt;br /&gt;
* a Views-powered listing (grid/table)&lt;br /&gt;
* exposed filter forms&lt;br /&gt;
* a selection state manager&lt;br /&gt;
* jQuery UI dialog controls&lt;br /&gt;
* AJAX-driven JavaScript behaviors&lt;br /&gt;
&lt;br /&gt;
All of these parts communicate via DOM presence, focus events, and click handlers.&lt;br /&gt;
&lt;br /&gt;
In particular:&lt;br /&gt;
&lt;br /&gt;
* the Media Library JavaScript uses **visible UI elements** to determine&lt;br /&gt;
  the current interaction state:&lt;br /&gt;
  * browsing&lt;br /&gt;
  * selecting&lt;br /&gt;
  * uploading&lt;br /&gt;
  * replacing&lt;br /&gt;
* when multiple interaction paths are visible at the same time&lt;br /&gt;
  (filters, dialog buttons, layout switches),&lt;br /&gt;
  the internal state machine can no longer reliably determine&lt;br /&gt;
  which action the user intends&lt;br /&gt;
&lt;br /&gt;
As a result:&lt;br /&gt;
* the transition to the upload/replace state is not triggered&lt;br /&gt;
* the upload box never appears&lt;br /&gt;
* replacement of the currently selected file fails silently&lt;br /&gt;
&lt;br /&gt;
This effect is **amplified inside modal dialogs**, where focus handling&lt;br /&gt;
and AJAX re-rendering are especially fragile.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Why Hiding UI Elements Fixes the Problem ===&lt;br /&gt;
&lt;br /&gt;
The applied CSS rules intentionally remove UI elements that are not needed&lt;br /&gt;
for the single-file Media Library workflow.&lt;br /&gt;
&lt;br /&gt;
By doing so, they:&lt;br /&gt;
&lt;br /&gt;
* eliminate competing interaction paths&lt;br /&gt;
* remove focus and submit-event conflicts&lt;br /&gt;
* prevent redundant confirmation logic&lt;br /&gt;
* reduce layout re-rendering during selection&lt;br /&gt;
&lt;br /&gt;
This forces the Media Library widget into a **deterministic interaction model**&lt;br /&gt;
that matches its expectations for:&lt;br /&gt;
&lt;br /&gt;
* cardinality = 1&lt;br /&gt;
* direct selection&lt;br /&gt;
* upload or replace of a single file&lt;br /&gt;
&lt;br /&gt;
Once the UI state becomes unambiguous, the JavaScript correctly switches&lt;br /&gt;
into upload/replace mode.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Important Conclusion ===&lt;br /&gt;
&lt;br /&gt;
These CSS overrides are **not cosmetic tweaks**.&lt;br /&gt;
&lt;br /&gt;
They are an **integral part of the Media Library configuration** for this project.&lt;br /&gt;
&lt;br /&gt;
Removing them may:&lt;br /&gt;
* reintroduce UI ambiguity&lt;br /&gt;
* break upload or replacement behavior&lt;br /&gt;
* make Media Library appear “corrupt” or inconsistent&lt;br /&gt;
&lt;br /&gt;
Therefore:&lt;br /&gt;
* the CSS must be preserved&lt;br /&gt;
* the CSS must be documented&lt;br /&gt;
* the CSS must be restored after experiments&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Design Principle (Summary) ===&lt;br /&gt;
&lt;br /&gt;
In the Media Library modal:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Hiding unused UI elements is required to stabilize widget behavior,&lt;br /&gt;
not just to simplify appearance.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== End of Appendix ==&lt;br /&gt;
&lt;br /&gt;
== End of document ==&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Media-Library_uploading_one_file_in_an_Asset&amp;diff=1578</id>
		<title>ICT:D Media-Library uploading one file in an Asset</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Media-Library_uploading_one_file_in_an_Asset&amp;diff=1578"/>
		<updated>2026-03-27T16:25:03Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Media Library use in Asset for uploading ONE single media file =  == Media field edit in Asset == * is an Entity Reference to Media with types: Audio, Document, Image and Video * Required field * Referencemethod to default * Limited to 1  (very important - one asset one file)  == Media in Manage form display == * Widget = Media Library * Tab order: Image, Document, Video, Audio  == Media in Manage display == * View mode = Media Library  After this configurtion there is...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Media Library use in Asset for uploading ONE single media file =&lt;br /&gt;
&lt;br /&gt;
== Media field edit in Asset ==&lt;br /&gt;
* is an Entity Reference to Media with types: Audio, Document, Image and Video&lt;br /&gt;
* Required field&lt;br /&gt;
* Referencemethod to default&lt;br /&gt;
* Limited to 1  (very important - one asset one file)&lt;br /&gt;
&lt;br /&gt;
== Media in Manage form display ==&lt;br /&gt;
* Widget = Media Library&lt;br /&gt;
* Tab order: Image, Document, Video, Audio&lt;br /&gt;
&lt;br /&gt;
== Media in Manage display ==&lt;br /&gt;
* View mode = Media Library&lt;br /&gt;
&lt;br /&gt;
After this configurtion there is an important GUI finetuning needed to simplify the interface and mask unneeded filters and buttons.&lt;br /&gt;
Therefor the next chapter is very important.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Media Library Widget UI Simplification (CSS Overrides) =&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
This document records the CSS overrides applied to the **Drupal Media Library widget**&lt;br /&gt;
to simplify the user interface for editors.&lt;br /&gt;
&lt;br /&gt;
The goal is to:&lt;br /&gt;
* reduce cognitive load&lt;br /&gt;
* hide internal mechanics (filters, buttons)&lt;br /&gt;
* provide a clean, focused media selection experience&lt;br /&gt;
* preserve full Media Library functionality&lt;br /&gt;
&lt;br /&gt;
These overrides are **intentional**, **documented**, and **part of the UI contract**.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Context ==&lt;br /&gt;
The Media Library widget is powered by:&lt;br /&gt;
* a Drupal View (for listing media)&lt;br /&gt;
* exposed filters (search, filtering)&lt;br /&gt;
* a jQuery UI modal dialog&lt;br /&gt;
&lt;br /&gt;
By default, the UI exposes:&lt;br /&gt;
* filter forms&lt;br /&gt;
* layout switches&lt;br /&gt;
* confirmation button bars&lt;br /&gt;
&lt;br /&gt;
For this project, these elements are **not required** and confuse users.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Design Principle ==&lt;br /&gt;
Editors should:&lt;br /&gt;
* focus on selecting or replacing a single media item&lt;br /&gt;
* not be exposed to filtering mechanics&lt;br /&gt;
* not be presented with redundant confirmation actions&lt;br /&gt;
&lt;br /&gt;
Filtering and constraints are applied **silently** via Views configuration.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Applied CSS Overrides ==&lt;br /&gt;
&lt;br /&gt;
The following CSS rules are applied to the admin theme (e.g. gin-custom.css).&lt;br /&gt;
&lt;br /&gt;
=== 1. Hide the exposed filter form ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* Hide the Views exposed filter form inside Media Library */&lt;br /&gt;
.media-library-view .views-exposed-form {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effect:&lt;br /&gt;
* Removes the visible filter form from the Media Library modal&lt;br /&gt;
* Filters continue to work internally&lt;br /&gt;
* Prevents users from interacting with unintended controls&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== 2. Hide the dialog button bar (Select / Cancel) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* Hide the bottom button bar in the Media Library modal */&lt;br /&gt;
.ui-dialog--media-library .ui-dialog-buttonpane {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effect:&lt;br /&gt;
* Removes the redundant “Select / Cancel” button pane&lt;br /&gt;
* Selection happens directly via clicking media items&lt;br /&gt;
* Avoids duplicate confirmation paths&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* Media Library uses jQuery UI dialogs&lt;br /&gt;
* Hiding the button pane via CSS is a supported and safe approach&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== 3. Hide the Grid / Table display switch ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* Hide the Grid / Table view switch */&lt;br /&gt;
.media-library-view--display-switch {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effect:&lt;br /&gt;
* Enforces a single, consistent layout&lt;br /&gt;
* Removes unnecessary UI choices&lt;br /&gt;
* Simplifies training and usage&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== What These Overrides Do NOT Do ==&lt;br /&gt;
&lt;br /&gt;
These CSS rules:&lt;br /&gt;
* do NOT affect data&lt;br /&gt;
* do NOT alter Media entities&lt;br /&gt;
* do NOT disable Media Library functionality&lt;br /&gt;
* do NOT change Views filtering logic&lt;br /&gt;
* do NOT interfere with file replacement&lt;br /&gt;
&lt;br /&gt;
They are **presentation‑only**.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Operational Notes ==&lt;br /&gt;
&lt;br /&gt;
* These CSS rules are required for correct UI behavior&lt;br /&gt;
* Removing them may:&lt;br /&gt;
  * reintroduce clutter&lt;br /&gt;
  * confuse editors&lt;br /&gt;
  * appear to “break” Media Library UX&lt;br /&gt;
* They must be restored after experiments or refactoring&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Maintenance Recommendation ==&lt;br /&gt;
Maintain a dedicated section in documentation for:&lt;br /&gt;
* UI‑level CSS overrides&lt;br /&gt;
* why they exist&lt;br /&gt;
* where they are applied&lt;br /&gt;
&lt;br /&gt;
This prevents accidental removal during future development.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Area !! Decision&lt;br /&gt;
|-&lt;br /&gt;
| Media selection UI || Simplified via CSS&lt;br /&gt;
|-&lt;br /&gt;
| Filters || Hidden visually, active internally&lt;br /&gt;
|-&lt;br /&gt;
| Confirmation buttons || Removed to avoid redundancy&lt;br /&gt;
|-&lt;br /&gt;
| Layout switching || Disabled for consistency&lt;br /&gt;
|-&lt;br /&gt;
| Impact || UI only, no data or logic changes&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== End ==&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Asset_Chain_Model_-_definition_and_application&amp;diff=1577</id>
		<title>ICT:Asset Chain Model - definition and application</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Asset_Chain_Model_-_definition_and_application&amp;diff=1577"/>
		<updated>2026-03-27T14:45:33Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Asset Chain Model and Selection Strategy =  == Purpose == This document defines the **Asset Chain model** used in this system and explains: * how Assets are versioned * how “top assets” are defined * how Assets are selected easily by users * how the full Asset chain can be displayed consistently  The model is designed to: * remain simple for historian users * avoid complex custom code * scale to large collections (1000+ Assets) * keep editorial intent explicit and...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Asset Chain Model and Selection Strategy =&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
This document defines the **Asset Chain model** used in this system and explains:&lt;br /&gt;
* how Assets are versioned&lt;br /&gt;
* how “top assets” are defined&lt;br /&gt;
* how Assets are selected easily by users&lt;br /&gt;
* how the full Asset chain can be displayed consistently&lt;br /&gt;
&lt;br /&gt;
The model is designed to:&lt;br /&gt;
* remain simple for historian users&lt;br /&gt;
* avoid complex custom code&lt;br /&gt;
* scale to large collections (1000+ Assets)&lt;br /&gt;
* keep editorial intent explicit and stable over time&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Core Principles ==&lt;br /&gt;
&lt;br /&gt;
* One Asset represents **one file**&lt;br /&gt;
* Assets may have multiple **versions**&lt;br /&gt;
* Versions are grouped into **Asset chains**&lt;br /&gt;
* Other entities (Object, Person, Place, etc.) link to **one Asset only**&lt;br /&gt;
* That Asset acts as the **entry point to the full chain**&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Asset Chain Definition ==&lt;br /&gt;
&lt;br /&gt;
=== Top Asset (Chain Root) ===&lt;br /&gt;
* The **first Asset created** for a subject&lt;br /&gt;
* Has **no parent Asset**&lt;br /&gt;
* Is the **top asset permanently**&lt;br /&gt;
* Represents:&lt;br /&gt;
  * recognition thumbnail&lt;br /&gt;
  * subject identification&lt;br /&gt;
  * entry point for the chain&lt;br /&gt;
&lt;br /&gt;
The definition of “top asset” is **structural**, not qualitative:&lt;br /&gt;
* it is NOT “best quality”&lt;br /&gt;
* it is NOT “highest resolution”&lt;br /&gt;
* it does NOT change later&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Child Assets (Versions) ===&lt;br /&gt;
* Represent alternative versions of the same Asset&lt;br /&gt;
  (e.g. better scan, restoration, derivative)&lt;br /&gt;
* Always have a **parent Asset**&lt;br /&gt;
* The parent **must be the top asset**&lt;br /&gt;
* Children of children are **not allowed**&lt;br /&gt;
&lt;br /&gt;
This creates a constrained structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Top asset (no parent)&lt;br /&gt;
├── Version A&lt;br /&gt;
├── Version B&lt;br /&gt;
├── Version C&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
No deep or random trees are permitted for Assets.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Editorial Workflow ==&lt;br /&gt;
&lt;br /&gt;
=== Creating an Asset Chain ===&lt;br /&gt;
1. User creates the first Asset&lt;br /&gt;
   * Parent field left empty&lt;br /&gt;
   * Asset becomes the **top asset**&lt;br /&gt;
2. User creates a new version&lt;br /&gt;
   * Parent selected = top asset&lt;br /&gt;
3. Repeat for additional versions&lt;br /&gt;
&lt;br /&gt;
At no point does the user need to redefine or move the top asset.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Linking Assets to Other Entities ===&lt;br /&gt;
* Object / Person / Place entities link to:&lt;br /&gt;
  * **exactly one Asset**&lt;br /&gt;
  * always a **top asset**&lt;br /&gt;
* This link never changes when new versions are added&lt;br /&gt;
&lt;br /&gt;
Benefits:&lt;br /&gt;
* stable references&lt;br /&gt;
* no cascading updates&lt;br /&gt;
* no hidden automation&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Asset Selection (Entity Browser) ==&lt;br /&gt;
&lt;br /&gt;
=== User Interface Principle ===&lt;br /&gt;
Users should:&lt;br /&gt;
* see **only top assets**&lt;br /&gt;
* search using **one simple search box**&lt;br /&gt;
* never need to understand Asset chains&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Entity Browser View Rules ===&lt;br /&gt;
&lt;br /&gt;
The Asset selection View applies:&lt;br /&gt;
&lt;br /&gt;
1. **One exposed filter (visible)**&lt;br /&gt;
   * Combined fields filter&lt;br /&gt;
   * Searches across:&lt;br /&gt;
     * title&lt;br /&gt;
     * identifiers&lt;br /&gt;
     * keywords&lt;br /&gt;
     * other metadata fields&lt;br /&gt;
&lt;br /&gt;
2. **Additional filters (not visible)**&lt;br /&gt;
   * Asset has no parent (top assets only)&lt;br /&gt;
   * Content type = Asset&lt;br /&gt;
&lt;br /&gt;
Important:&lt;br /&gt;
* Only the first exposed filter is shown in the modal&lt;br /&gt;
* All other filters are still applied silently&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
* clean UI&lt;br /&gt;
* strong constraints&lt;br /&gt;
* no risk of selecting a child asset&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Presenting the Asset Chain ==&lt;br /&gt;
&lt;br /&gt;
=== Asset Chain View ===&lt;br /&gt;
A dedicated View is used to show the full chain.&lt;br /&gt;
&lt;br /&gt;
Definition:&lt;br /&gt;
* Contextual argument = top asset ID&lt;br /&gt;
* Filters:&lt;br /&gt;
  * Asset ID = argument&lt;br /&gt;
  * OR parent Asset = argument&lt;br /&gt;
* Sorting:&lt;br /&gt;
  * creation date&lt;br /&gt;
  * version label&lt;br /&gt;
  * or other editorial order&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
* shows the top asset and all its versions together&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Usage ===&lt;br /&gt;
The Asset chain View can be:&lt;br /&gt;
* linked from Object / Person / Place&lt;br /&gt;
* linked from the Asset itself&lt;br /&gt;
* used to choose the appropriate version for publication&lt;br /&gt;
&lt;br /&gt;
This keeps:&lt;br /&gt;
* selection simple&lt;br /&gt;
* presentation rich&lt;br /&gt;
* version choice contextual&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Why This Model Was Chosen ==&lt;br /&gt;
&lt;br /&gt;
* Avoids redefining “top asset” over time&lt;br /&gt;
* Avoids automatic propagation or inference&lt;br /&gt;
* Avoids recursive Views logic&lt;br /&gt;
* Keeps historian workflow simple&lt;br /&gt;
* Makes system behavior predictable&lt;br /&gt;
* Allows future extensions if needed&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Aspect !! Decision&lt;br /&gt;
|-&lt;br /&gt;
| Asset versioning || Flat chain under permanent top asset&lt;br /&gt;
|-&lt;br /&gt;
| Top asset || First created, no parent, never changes&lt;br /&gt;
|-&lt;br /&gt;
| Selection UI || Only top assets visible&lt;br /&gt;
|-&lt;br /&gt;
| Filtering || One combined search + silent constraints&lt;br /&gt;
|-&lt;br /&gt;
| Linking from other entities || Always to top asset&lt;br /&gt;
|-&lt;br /&gt;
| Chain presentation || Dedicated Asset chain View&lt;br /&gt;
|-&lt;br /&gt;
| Custom code || Minimal, UI-level only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== End ==&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:D_Taxonomy_-_Vocabulary_-_Terms_use&amp;diff=1576</id>
		<title>ICT:D Taxonomy - Vocabulary - Terms use</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:D_Taxonomy_-_Vocabulary_-_Terms_use&amp;diff=1576"/>
		<updated>2026-03-27T13:39:06Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Using Taxonomy for Keywords and Search in Drupal (Assets, Views, Entity Browser) =  == Purpose == This document describes how to use **taxonomy (vocabularies and terms)** in Drupal to: * assign multiple keywords to entities (Assets, Persons, Objects, etc.) * reuse keywords consistently * enable effective searching via **Combined fields filters** in Views and Entity Browser modals * scale to large datasets (1000+ entities)  No custom code is required.  ---  == Concepts...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Using Taxonomy for Keywords and Search in Drupal (Assets, Views, Entity Browser) =&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
This document describes how to use **taxonomy (vocabularies and terms)** in Drupal to:&lt;br /&gt;
* assign multiple keywords to entities (Assets, Persons, Objects, etc.)&lt;br /&gt;
* reuse keywords consistently&lt;br /&gt;
* enable effective searching via **Combined fields filters** in Views and Entity Browser modals&lt;br /&gt;
* scale to large datasets (1000+ entities)&lt;br /&gt;
&lt;br /&gt;
No custom code is required.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Concepts (short definitions) ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Term !! Meaning in Drupal&lt;br /&gt;
|-&lt;br /&gt;
| Taxonomy || The classification system&lt;br /&gt;
|-&lt;br /&gt;
| Vocabulary || A named set of related terms (e.g. &amp;quot;Keywords&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| Term || One keyword inside a vocabulary&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A **vocabulary is part of taxonomy**, not a separate concept.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Creating a Keyword Vocabulary ==&lt;br /&gt;
&lt;br /&gt;
1. Go to  &lt;br /&gt;
   &#039;&#039;&#039;Structure → Taxonomy → Add vocabulary&#039;&#039;&#039;&lt;br /&gt;
2. Example:&lt;br /&gt;
   * Name: &#039;&#039;&#039;Keywords&#039;&#039;&#039;&lt;br /&gt;
   * Description: Free or controlled keywords for Assets and related entities&lt;br /&gt;
3. Save&lt;br /&gt;
&lt;br /&gt;
You may create **multiple vocabularies** if needed, e.g.:&lt;br /&gt;
* Subject keywords&lt;br /&gt;
* Object type&lt;br /&gt;
* Period&lt;br /&gt;
* Location&lt;br /&gt;
* Technique / Material&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Adding Keywords to an Entity Type ==&lt;br /&gt;
&lt;br /&gt;
Repeat for each entity type that needs keywords (Asset, Person, Object, etc.).&lt;br /&gt;
&lt;br /&gt;
1. Go to  &lt;br /&gt;
   &#039;&#039;&#039;Structure → Content types → Asset → Manage fields&#039;&#039;&#039;&lt;br /&gt;
2. Add a new field:&lt;br /&gt;
   * Field type: &#039;&#039;&#039;Term reference&#039;&#039;&#039;&lt;br /&gt;
   * Reference type: &#039;&#039;&#039;Taxonomy term&#039;&#039;&#039;&lt;br /&gt;
3. Field settings:&lt;br /&gt;
   * Allowed vocabulary: &#039;&#039;&#039;Keywords&#039;&#039;&#039;&lt;br /&gt;
   * Cardinality: &#039;&#039;&#039;Unlimited&#039;&#039;&#039;&lt;br /&gt;
4. Widget:&lt;br /&gt;
   * &#039;&#039;&#039;Autocomplete (tags style)&#039;&#039;&#039;&lt;br /&gt;
5. Save&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Keyword Usage by Editors ==&lt;br /&gt;
&lt;br /&gt;
With autocomplete (tags style):&lt;br /&gt;
&lt;br /&gt;
* Editors can **select existing keywords**&lt;br /&gt;
* Editors can **create new keywords on the fly** by typing and pressing Enter&lt;br /&gt;
* New terms are automatically added to the vocabulary&lt;br /&gt;
* Existing entities can be edited later to add or remove keywords&lt;br /&gt;
&lt;br /&gt;
(Requires permission: &#039;&#039;Create terms in Keywords vocabulary&#039;&#039;.)&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Using Keywords in Views (Search) ==&lt;br /&gt;
&lt;br /&gt;
=== Basic rule ===&lt;br /&gt;
The **Combined fields filter** can search:&lt;br /&gt;
* any field that is present in the View’s &#039;&#039;&#039;Fields&#039;&#039;&#039; list&lt;br /&gt;
* even if the field is &#039;&#039;&#039;excluded from display&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Configuring a View for Keyword Search ==&lt;br /&gt;
&lt;br /&gt;
1. Edit the View (e.g. Entity Browser View)&lt;br /&gt;
2. Under &#039;&#039;&#039;Fields&#039;&#039;&#039;:&lt;br /&gt;
   * Add the keyword field (term reference)&lt;br /&gt;
   * Enable &#039;&#039;&#039;Exclude from display&#039;&#039;&#039;&lt;br /&gt;
3. Repeat for any other searchable fields (title, identifier, description, etc.)&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Adding the Combined Fields Filter ==&lt;br /&gt;
&lt;br /&gt;
1. Under &#039;&#039;&#039;Filter criteria → Add&#039;&#039;&#039;&lt;br /&gt;
2. Choose:&lt;br /&gt;
   * &#039;&#039;&#039;Global: Combine fields filter&#039;&#039;&#039;&lt;br /&gt;
3. Expose the filter&lt;br /&gt;
4. In filter settings:&lt;br /&gt;
   * Select all searchable fields (including hidden keyword fields)&lt;br /&gt;
5. Save&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== How the Combined Fields Filter Works ==&lt;br /&gt;
&lt;br /&gt;
* One search box&lt;br /&gt;
* OR logic across fields&lt;br /&gt;
* Example logic:&lt;br /&gt;
  &amp;lt;pre&amp;gt;&lt;br /&gt;
  Title OR Identifier OR Keywords OR Description&lt;br /&gt;
  &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is:&lt;br /&gt;
* no AND syntax&lt;br /&gt;
* no multi-filter UI in Entity Browser modals&lt;br /&gt;
* only one exposed filter is shown&lt;br /&gt;
&lt;br /&gt;
This is a known Entity Browser limitation.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Scaling to Large Datasets ==&lt;br /&gt;
&lt;br /&gt;
For large collections (1000+ Assets):&lt;br /&gt;
&lt;br /&gt;
* Rich keyword metadata is essential&lt;br /&gt;
* Multiple vocabularies are recommended&lt;br /&gt;
* Combined search enables progressive narrowing:&lt;br /&gt;
  &amp;lt;pre&amp;gt;&lt;br /&gt;
  map 1850 Antwerp manuscript&lt;br /&gt;
  &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach:&lt;br /&gt;
* avoids scrolling&lt;br /&gt;
* avoids complex UI&lt;br /&gt;
* remains performant&lt;br /&gt;
* requires no custom module&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Recommended Pattern (Summary) ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Requirement !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| Multiple keywords per entity || Term reference field (unlimited)&lt;br /&gt;
|-&lt;br /&gt;
| Reusable keywords || Taxonomy vocabulary&lt;br /&gt;
|-&lt;br /&gt;
| Free keyword creation || Autocomplete (tags)&lt;br /&gt;
|-&lt;br /&gt;
| Narrowing search || Combined fields filter&lt;br /&gt;
|-&lt;br /&gt;
| Entity Browser modal || Single exposed combined filter&lt;br /&gt;
|-&lt;br /&gt;
| No custom code || Configuration only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Notes for Maintainers ==&lt;br /&gt;
&lt;br /&gt;
* Taxonomy vocabularies can be reused across entities&lt;br /&gt;
* Keyword structure can evolve over time&lt;br /&gt;
* Existing entities are not locked to initial keywords&lt;br /&gt;
* This setup intentionally avoids custom modules for maintainability&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== End ==&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:m-m_with_IEF_and_Entity_Browser_-_no_custom_module&amp;diff=1575</id>
		<title>ICT:m-m with IEF and Entity Browser - no custom module</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:m-m_with_IEF_and_Entity_Browser_-_no_custom_module&amp;diff=1575"/>
		<updated>2026-03-26T18:30:44Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Drupal: Asset selection with Inline Entity Form + Entity Browser (Gin, no custom module) =  == Purpose == This page documents the final, working setup for selecting and managing Assets on entity edit forms (e.g. Person → Asset), using:  * Inline Entity Form (IEF) – Complex (table display) * Entity Browser (modal picker with search and thumbnails) * Gin admin theme * No custom module * No JavaScript logic * Hardened for AlmaLinux / SELinux  The goal was to provide a...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Drupal: Asset selection with Inline Entity Form + Entity Browser (Gin, no custom module) =&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
This page documents the final, working setup for selecting and managing Assets&lt;br /&gt;
on entity edit forms (e.g. Person → Asset), using:&lt;br /&gt;
&lt;br /&gt;
* Inline Entity Form (IEF) – Complex (table display)&lt;br /&gt;
* Entity Browser (modal picker with search and thumbnails)&lt;br /&gt;
* Gin admin theme&lt;br /&gt;
* No custom module&lt;br /&gt;
* No JavaScript logic&lt;br /&gt;
* Hardened for AlmaLinux / SELinux&lt;br /&gt;
&lt;br /&gt;
The goal was to provide a historian‑friendly UX while keeping the solution&lt;br /&gt;
maintainable and upgrade‑safe.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* Drupal 10/11&lt;br /&gt;
* Inline Entity Form (&amp;gt;= 3.x)&lt;br /&gt;
* Entity Browser (2.x)&lt;br /&gt;
* Gin admin theme&lt;br /&gt;
* Assets as a reusable entity (e.g. node or media)&lt;br /&gt;
* AlmaLinux with SELinux enforcing&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Functional Requirements ==&lt;br /&gt;
* Editors select existing Assets via a modal with search and thumbnails&lt;br /&gt;
* Selected Assets appear as a table on the edit form&lt;br /&gt;
* Editors can remove / reorder Assets&lt;br /&gt;
* Editors must NOT be confused by an &amp;quot;Add node&amp;quot; action&lt;br /&gt;
* No custom Drupal module may be introduced&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Architecture Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Why this architecture ===&lt;br /&gt;
Drupal provides two strong but complementary tools:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Tool !! Role&lt;br /&gt;
|-&lt;br /&gt;
| Inline Entity Form (Complex) || Table display + edit/remove/reorder&lt;br /&gt;
|-&lt;br /&gt;
| Entity Browser || Modal selection UI using Views&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The key is:&lt;br /&gt;
* IEF controls &#039;&#039;&#039;how selected items are displayed&#039;&#039;&#039;&lt;br /&gt;
* Entity Browser controls &#039;&#039;&#039;how items are selected&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
They must be combined correctly.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Correct Field Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== Entity reference field ===&lt;br /&gt;
On the parent entity (e.g. Person):&lt;br /&gt;
&lt;br /&gt;
* Field type: Entity reference&lt;br /&gt;
* Target: Asset&lt;br /&gt;
* Cardinality: Unlimited&lt;br /&gt;
&lt;br /&gt;
=== Form display ===&lt;br /&gt;
* Widget: &#039;&#039;&#039;Inline Entity Form – Complex&#039;&#039;&#039;&lt;br /&gt;
* Widget settings:&lt;br /&gt;
** ✅ Allow users to add existing entities&lt;br /&gt;
** ❌ Allow users to add new entities&lt;br /&gt;
** Entity Browser: select the Asset picker browser&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039;  &lt;br /&gt;
Do NOT set the field widget itself to &amp;quot;Entity Browser&amp;quot;.&lt;br /&gt;
That results in a pill/tabs UI instead of a table.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Known UX Issue ==&lt;br /&gt;
When clicking &#039;&#039;&#039;Add existing Asset&#039;&#039;&#039;, Inline Entity Form opens an intermediate&lt;br /&gt;
chooser dialog containing:&lt;br /&gt;
&lt;br /&gt;
* Select entities&lt;br /&gt;
* Add node&lt;br /&gt;
* Cancel&lt;br /&gt;
&lt;br /&gt;
Even when &amp;quot;Add new&amp;quot; is disabled in IEF settings, the &#039;&#039;&#039;Add node&#039;&#039;&#039; button&lt;br /&gt;
still appears. This is a known limitation of IEF.&lt;br /&gt;
&lt;br /&gt;
For historians, this button is misleading and must be hidden.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Final Solution: Hide the &amp;quot;Add node&amp;quot; Button (CSS only) ==&lt;br /&gt;
&lt;br /&gt;
=== Why CSS ===&lt;br /&gt;
* No custom module&lt;br /&gt;
* No JS behaviors&lt;br /&gt;
* Upgrade‑safe&lt;br /&gt;
* Gin explicitly supports admin CSS overrides&lt;br /&gt;
&lt;br /&gt;
=== Location ===&lt;br /&gt;
Create or edit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sites/default/files/gin-custom.css&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This file is automatically loaded by Gin for admin pages.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Working CSS (final) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* Hide misleading &amp;quot;Add node&amp;quot; button in Inline Entity Form chooser */&lt;br /&gt;
input.ief-entity-submit[data-drupal-selector*=&amp;quot;ief-reference-save&amp;quot;][value=&amp;quot;Add node&amp;quot;] {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Why this works ===&lt;br /&gt;
* Targets the exact submit button rendered by IEF&lt;br /&gt;
* Uses stable attributes:&lt;br /&gt;
** class: &amp;lt;code&amp;gt;ief-entity-submit&amp;lt;/code&amp;gt;&lt;br /&gt;
** attribute: &amp;lt;code&amp;gt;data-drupal-selector&amp;lt;/code&amp;gt;&lt;br /&gt;
* Does NOT depend on dialog wrappers (.ui-dialog, .gin-dialog)&lt;br /&gt;
* Survives AJAX re-rendering&lt;br /&gt;
* Gin does not override &amp;lt;code&amp;gt;display: none&amp;lt;/code&amp;gt; on this element&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Security Considerations (AlmaLinux / SELinux) ==&lt;br /&gt;
&lt;br /&gt;
Because &amp;lt;code&amp;gt;gin-custom.css&amp;lt;/code&amp;gt; and optional &amp;lt;code&amp;gt;gin-custom.js&amp;lt;/code&amp;gt;&lt;br /&gt;
live in &amp;lt;code&amp;gt;sites/default/files&amp;lt;/code&amp;gt;, permissions and SELinux labels matter.&lt;br /&gt;
&lt;br /&gt;
=== Correct permissions ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-rw-r--r-- root root gin-custom.css&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apache must NOT have write access.&lt;br /&gt;
&lt;br /&gt;
=== Correct SELinux label ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
httpd_sys_content_t&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verify with:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ls -Z sites/default/files/gin-custom.css&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apache write test (should fail):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;test&amp;quot; | sudo -u apache tee -a sites/default/files/gin-custom.css&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Permission denied&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures admin JS/CSS cannot be overwritten by the web server.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Performance Notes ==&lt;br /&gt;
&lt;br /&gt;
* 1000+ Assets is fine&lt;br /&gt;
* Entity Browser uses a View with pagination and filters&lt;br /&gt;
* Only referenced Assets are rendered in the IEF table&lt;br /&gt;
* Thumbnails should always use image styles&lt;br /&gt;
* This setup is comparable to Drupal Media Library performance&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Known Limits ==&lt;br /&gt;
* The intermediate IEF chooser dialog cannot be removed by configuration&lt;br /&gt;
* The &amp;quot;Add node&amp;quot; button cannot be removed server‑side without custom code&lt;br /&gt;
* CSS hiding is the cleanest possible solution under the constraints&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Future Improvements ==&lt;br /&gt;
* Add thumbnail column to IEF table&lt;br /&gt;
* Scope CSS to specific fields if desired&lt;br /&gt;
* Improve wording via UI translation if needed&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
This solution achieves:&lt;br /&gt;
&lt;br /&gt;
* ✅ Clean historian UX&lt;br /&gt;
* ✅ Table-based Asset management&lt;br /&gt;
* ✅ Modal picker with search&lt;br /&gt;
* ✅ No custom module&lt;br /&gt;
* ✅ Secure on SELinux&lt;br /&gt;
* ✅ Maintainable and upgrade-safe&lt;br /&gt;
&lt;br /&gt;
The key lesson is:&lt;br /&gt;
&#039;&#039;&#039;Once the exact HTML is known, target the element directly.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Remove_view_link_in_messages_-_augmented_version&amp;diff=1574</id>
		<title>ICT:Remove view link in messages - augmented version</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Remove_view_link_in_messages_-_augmented_version&amp;diff=1574"/>
		<updated>2026-03-26T11:02:06Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Drupal 11 – Removing the “View” link from node save messages =  == Context == In the Heritage Project (Drupal 11.3.x), historian users must **not** be redirected to or encouraged to view the node page after saving content.   However, Drupal core automatically shows a status message such as:  &amp;lt;pre&amp;gt; Article “My title” has been updated. View &amp;lt;/pre&amp;gt;  The “View” link is part of the default node save status message.  A custom module (`heritage_tweaks`) modifies...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Drupal 11 – Removing the “View” link from node save messages =&lt;br /&gt;
&lt;br /&gt;
== Context ==&lt;br /&gt;
In the Heritage Project (Drupal 11.3.x), historian users must **not** be redirected to or encouraged to view the node page after saving content.  &lt;br /&gt;
However, Drupal core automatically shows a status message such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Article “My title” has been updated. View&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The “View” link is part of the default node save status message.&lt;br /&gt;
&lt;br /&gt;
A custom module (`heritage_tweaks`) modifies this behavior:&lt;br /&gt;
* The link is removed for non-administrators&lt;br /&gt;
* Administrators retain the default behavior&lt;br /&gt;
* Existing date validation logic is untouched&lt;br /&gt;
&lt;br /&gt;
This page documents **why the initial implementation was unreliable** and **what exactly was changed** to make it work consistently in Drupal 11.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Initial implementation (problematic) ==&lt;br /&gt;
&lt;br /&gt;
The original code attached a custom submit handler like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$form[&#039;actions&#039;][&#039;submit&#039;][&#039;#submit&#039;][] = &#039;heritage_tweaks_clean_save_message&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This assumes that:&lt;br /&gt;
* The node form always submits via &amp;lt;code&amp;gt;actions[submit]&amp;lt;/code&amp;gt;&lt;br /&gt;
* The custom submit handler always runs after Drupal core adds its status message&lt;br /&gt;
&lt;br /&gt;
These assumptions were **sometimes false** in Drupal 11.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Why this failed intermittently ==&lt;br /&gt;
&lt;br /&gt;
=== 1. Node forms can have multiple submit buttons ===&lt;br /&gt;
Even if the UI appears to show only one “Save” button, internally Drupal may expose:&lt;br /&gt;
* Multiple submit actions&lt;br /&gt;
* Workflow or moderation-related submit buttons&lt;br /&gt;
* Theme-generated submit wrappers&lt;br /&gt;
&lt;br /&gt;
If the user clicks a submit button that is **not** &amp;lt;code&amp;gt;actions[submit]&amp;lt;/code&amp;gt;, then:&lt;br /&gt;
* The custom handler is never executed&lt;br /&gt;
* The core “Saved … View” message remains unchanged&lt;br /&gt;
&lt;br /&gt;
=== 2. Submit handlers are button-specific ===&lt;br /&gt;
In Drupal Form API:&lt;br /&gt;
* Submit handlers attached to one button are **not shared** with others&lt;br /&gt;
* Attaching to the wrong button means the code simply never runs&lt;br /&gt;
&lt;br /&gt;
This is a common cause of “my submit handler works sometimes” bugs in Drupal.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Final implementation (working) ==&lt;br /&gt;
&lt;br /&gt;
The fix was to attach the cleanup handler to **all submit buttons**, not just one.&lt;br /&gt;
&lt;br /&gt;
Simplified logic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
foreach ($form[&#039;actions&#039;] as $action) {&lt;br /&gt;
  if ($action[&#039;#type&#039;] === &#039;submit&#039;) {&lt;br /&gt;
    $action[&#039;#submit&#039;][] = &#039;heritage_tweaks_clean_save_message&#039;;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures:&lt;br /&gt;
* The handler always runs, regardless of which submit action is used&lt;br /&gt;
* The cleanup logic executes after core has added its status message&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== What did NOT change ==&lt;br /&gt;
&lt;br /&gt;
* The date validation logic (fields ending in &amp;lt;code&amp;gt;_day&amp;lt;/code&amp;gt;)&lt;br /&gt;
* The placeholder logic (&amp;lt;code&amp;gt;dd/mm/yyyy&amp;lt;/code&amp;gt;)&lt;br /&gt;
* The use of Drupal’s Messenger API&lt;br /&gt;
* The permission check for administrators&lt;br /&gt;
&lt;br /&gt;
Only the **submit-handler attachment strategy** changed.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== How the message cleanup works ==&lt;br /&gt;
&lt;br /&gt;
When the submit handler runs:&lt;br /&gt;
&lt;br /&gt;
# If the user has the &amp;lt;code&amp;gt;administer nodes&amp;lt;/code&amp;gt; permission, do nothing&lt;br /&gt;
# Otherwise:&lt;br /&gt;
## Remove all STATUS messages&lt;br /&gt;
## Add a new plain-text status message without links&lt;br /&gt;
&lt;br /&gt;
Example replacement message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Article “My title” has been updated.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warnings and errors are preserved because only STATUS messages are removed.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Why this is correct for Drupal 11 ==&lt;br /&gt;
&lt;br /&gt;
Drupal 8.5+ introduced the Messenger service, and Drupal 11 relies fully on it for user messages.&lt;br /&gt;
&lt;br /&gt;
Key points:&lt;br /&gt;
* Status messages are stored in a message queue&lt;br /&gt;
* Messages must be removed **after** they are added&lt;br /&gt;
* Entity hooks (e.g. &amp;lt;code&amp;gt;hook_node_update&amp;lt;/code&amp;gt;) run too early&lt;br /&gt;
* Form submit handlers run at the correct time&lt;br /&gt;
&lt;br /&gt;
Attaching to all submit buttons is the **recommended defensive pattern** for node forms in modern Drupal.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Summary (one sentence) ==&lt;br /&gt;
&lt;br /&gt;
The fix works because the cleanup logic is now attached to **every submit button**, guaranteeing that the “View” link is removed no matter how the node form is submitted.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Maintenance note ==&lt;br /&gt;
If future changes introduce new submit actions (custom workflows, contributed modules, admin themes), this implementation will continue to work without modification.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Reverse-Proxy_on_AlmaLinux_with_nginx_to_apache_backend&amp;diff=1573</id>
		<title>ICT:Reverse-Proxy on AlmaLinux with nginx to apache backend</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Reverse-Proxy_on_AlmaLinux_with_nginx_to_apache_backend&amp;diff=1573"/>
		<updated>2026-03-25T18:00:41Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Reverse‑Proxy Architecture (nginx → Apache) =&lt;br /&gt;
This page documents the complete reverse‑proxy setup for the AlmaLinux frontend server.&lt;br /&gt;
It replaces the former IIS reverse‑proxy and Windows‑based certificate renewal system.&lt;br /&gt;
The configuration is now fully Linux‑native, stable, and successor‑friendly.&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
* Frontend: AlmaLinux + nginx&lt;br /&gt;
* Backend: Apache (Drupal + MediaWiki)&lt;br /&gt;
* Certificates: Let’s Encrypt (certbot)&lt;br /&gt;
* Automatic renewal: systemd timer&lt;br /&gt;
* Windows/IIS: fully retired (no bindings, no renewals, no proxies)&lt;br /&gt;
&lt;br /&gt;
The nginx server terminates HTTPS and forwards traffic to the internal Apache instance.&lt;br /&gt;
&lt;br /&gt;
== Directory Layout ==&lt;br /&gt;
* &#039;&#039;&#039;/etc/nginx/nginx.conf&#039;&#039;&#039; – global nginx configuration  &lt;br /&gt;
* &#039;&#039;&#039;/etc/nginx/conf.d/&#039;&#039;&#039; – per‑site reverse‑proxy configs  &lt;br /&gt;
* &#039;&#039;&#039;/var/www/letsencrypt/&#039;&#039;&#039; – ACME challenge directory  &lt;br /&gt;
* &#039;&#039;&#039;/etc/letsencrypt/live/&amp;lt;domain&amp;gt;/&#039;&#039;&#039; – certificate files  &lt;br /&gt;
&lt;br /&gt;
== ACME Challenge Directory ==&lt;br /&gt;
Certbot uses a shared directory for HTTP‑01 validation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/var/www/letsencrypt/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
nginx exposes this path for both domains.&lt;br /&gt;
&lt;br /&gt;
== Site: research.costasano.club ==&lt;br /&gt;
=== HTTP (port 80) ===&lt;br /&gt;
Redirects all traffic to HTTPS, except ACME challenges.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name research.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /var/www/letsencrypt;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HTTPS (port 443) ===&lt;br /&gt;
Terminates TLS and proxies to Apache.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name research.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/research.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/research.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /var/www/letsencrypt;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host research.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Site: mwiki.costasano.club ==&lt;br /&gt;
=== HTTP (port 80) ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name mwiki.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /var/www/letsencrypt;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HTTPS (port 443) ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name mwiki.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/mwiki.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/mwiki.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /var/www/letsencrypt;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host mwiki.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Certificate Renewal ==&lt;br /&gt;
Certbot is installed natively on AlmaLinux.&lt;br /&gt;
&lt;br /&gt;
=== Renewal Timer ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl status certbot-renew.timer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The timer runs daily and triggers renewal when certificates approach expiry.&lt;br /&gt;
&lt;br /&gt;
=== Manual Dry‑Run Test ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot renew --dry-run&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Windows/IIS Retirement ==&lt;br /&gt;
The former IIS reverse‑proxy and win‑acme renewal system were removed:&lt;br /&gt;
* IIS sites deleted  &lt;br /&gt;
* win‑acme renewal entries cancelled  &lt;br /&gt;
* scheduled tasks removed  &lt;br /&gt;
* old certificates removed from certlm.msc  &lt;br /&gt;
* no bindings remain on ports 80/443  &lt;br /&gt;
&lt;br /&gt;
== Verification Checklist ==&lt;br /&gt;
* nginx configs validated: &amp;lt;code&amp;gt;nginx -t&amp;lt;/code&amp;gt;  &lt;br /&gt;
* nginx reloaded: &amp;lt;code&amp;gt;systemctl reload nginx&amp;lt;/code&amp;gt;  &lt;br /&gt;
* certbot timer active  &lt;br /&gt;
* ACME challenge reachable  &lt;br /&gt;
* backend Apache reachable  &lt;br /&gt;
* Windows silent on ports 80/443  &lt;br /&gt;
&lt;br /&gt;
== Architecture Diagram (ASCII) ==&lt;br /&gt;
&lt;br /&gt;
The following diagram shows the full request flow from the internet to the backend applications.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                 +----------------------+&lt;br /&gt;
                 |      Internet        |&lt;br /&gt;
                 +----------+-----------+&lt;br /&gt;
                            |&lt;br /&gt;
                            v&lt;br /&gt;
                 +----------------------+&lt;br /&gt;
                 |   AlmaLinux Server   |&lt;br /&gt;
                 |      (frontend)      |&lt;br /&gt;
                 +----------+-----------+&lt;br /&gt;
                            |&lt;br /&gt;
                HTTPS :443 / HTTP :80&lt;br /&gt;
                            |&lt;br /&gt;
                            v&lt;br /&gt;
                    +---------------+&lt;br /&gt;
                    |    nginx      |&lt;br /&gt;
                    |  reverse proxy|&lt;br /&gt;
                    +-------+-------+&lt;br /&gt;
                            |&lt;br /&gt;
        +-------------------+-------------------+&lt;br /&gt;
        |                                       |&lt;br /&gt;
        v                                       v&lt;br /&gt;
+---------------------+               +---------------------+&lt;br /&gt;
| research.costasano  |               | mwiki.costasano     |&lt;br /&gt;
| .club (vhost)       |               | .club (vhost)       |&lt;br /&gt;
+----------+----------+               +----------+----------+&lt;br /&gt;
           |                                     |&lt;br /&gt;
           | proxy_pass http://192.168.33.231    |&lt;br /&gt;
           +-------------------------+-----------+&lt;br /&gt;
                                     |&lt;br /&gt;
                                     v&lt;br /&gt;
                         +------------------------+&lt;br /&gt;
                         |   Apache (backend)     |&lt;br /&gt;
                         |  192.168.33.231        |&lt;br /&gt;
                         +-----------+------------+&lt;br /&gt;
                                     |&lt;br /&gt;
                 +-------------------+-------------------+&lt;br /&gt;
                 |                                       |&lt;br /&gt;
                 v                                       v&lt;br /&gt;
        +-------------------+                  +-------------------+&lt;br /&gt;
        |   Drupal site     |                  |  MediaWiki site   |&lt;br /&gt;
        | research.*        |                  | mwiki.*           |&lt;br /&gt;
        +-------------------+                  +-------------------+&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ACME / Let’s Encrypt HTTP-01 validation:&lt;br /&gt;
&lt;br /&gt;
    Let’s Encrypt  ---&amp;gt;  nginx  ---&amp;gt;  /var/www/letsencrypt/&lt;br /&gt;
                         (both vhosts expose&lt;br /&gt;
                          /.well-known/acme-challenge/)&lt;br /&gt;
&lt;br /&gt;
Certificates:&lt;br /&gt;
&lt;br /&gt;
    /etc/letsencrypt/live/research.costasano.club/&lt;br /&gt;
    /etc/letsencrypt/live/mwiki.costasano.club/&lt;br /&gt;
&lt;br /&gt;
Renewal:&lt;br /&gt;
&lt;br /&gt;
    systemd timer: certbot-renew.timer&lt;br /&gt;
    service:       certbot-renew.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Rationale for Replacing IIS with nginx ==&lt;br /&gt;
&lt;br /&gt;
This section explains why the former Windows/IIS reverse‑proxy was retired in favour of a native Linux‑based nginx setup. The decision was driven by stability, clarity, maintainability, and long‑term operational safety.&lt;br /&gt;
&lt;br /&gt;
=== 1. Architectural Simplicity ===&lt;br /&gt;
The previous setup required:&lt;br /&gt;
* Windows Server&lt;br /&gt;
* IIS with URL Rewrite + ARR&lt;br /&gt;
* win-acme for certificate renewal&lt;br /&gt;
* Manual bindings for each hostname&lt;br /&gt;
* Coordination between Windows and the Linux backend&lt;br /&gt;
&lt;br /&gt;
This created a multi‑platform dependency chain.  &lt;br /&gt;
The new setup collapses the entire frontend into a single, predictable layer:&lt;br /&gt;
* AlmaLinux&lt;br /&gt;
* nginx&lt;br /&gt;
* certbot (systemd‑managed)&lt;br /&gt;
&lt;br /&gt;
Fewer moving parts → fewer failure points.&lt;br /&gt;
&lt;br /&gt;
=== 2. Elimination of Cross‑Platform Coupling ===&lt;br /&gt;
The old architecture required Windows to proxy traffic to Linux.  &lt;br /&gt;
This meant:&lt;br /&gt;
* Two operating systems had to stay in sync  &lt;br /&gt;
* Two certificate systems had to be maintained  &lt;br /&gt;
* Two sets of logs had to be consulted during troubleshooting  &lt;br /&gt;
&lt;br /&gt;
By moving the reverse‑proxy to the same machine that hosts the backend, the system becomes self‑contained and easier to reason about.&lt;br /&gt;
&lt;br /&gt;
=== 3. Cleaner TLS and Certificate Management ===&lt;br /&gt;
The Windows setup used win‑acme, scheduled tasks, and IIS bindings.  &lt;br /&gt;
This caused:&lt;br /&gt;
* Duplicate certificate stores  &lt;br /&gt;
* Renewal tasks that were easy to forget  &lt;br /&gt;
* Certificates living on a machine that no longer served the content  &lt;br /&gt;
&lt;br /&gt;
The new setup uses:&lt;br /&gt;
* certbot  &lt;br /&gt;
* systemd timers  &lt;br /&gt;
* a shared ACME challenge directory  &lt;br /&gt;
* certificates stored exactly where they are used  &lt;br /&gt;
&lt;br /&gt;
This is simpler, safer, and fully automated.&lt;br /&gt;
&lt;br /&gt;
=== 4. nginx Is Purpose‑Built for Reverse‑Proxying ===&lt;br /&gt;
nginx excels at:&lt;br /&gt;
* high‑performance proxying  &lt;br /&gt;
* buffering  &lt;br /&gt;
* header management  &lt;br /&gt;
* HTTP/2  &lt;br /&gt;
* TLS termination  &lt;br /&gt;
&lt;br /&gt;
IIS can do these things, but only through multiple modules (ARR, URL Rewrite) and with more complexity. nginx provides them natively and predictably.&lt;br /&gt;
&lt;br /&gt;
=== 5. Reduced Operational Overhead ===&lt;br /&gt;
The Windows/IIS layer required:&lt;br /&gt;
* OS patching  &lt;br /&gt;
* IIS updates  &lt;br /&gt;
* win‑acme maintenance  &lt;br /&gt;
* Monitoring of scheduled tasks  &lt;br /&gt;
* Manual cleanup of old bindings and certificates  &lt;br /&gt;
&lt;br /&gt;
The nginx layer requires:&lt;br /&gt;
* a single systemd timer  &lt;br /&gt;
* a single config directory  &lt;br /&gt;
* one place to check logs  &lt;br /&gt;
&lt;br /&gt;
This reduces cognitive load for both you and any future maintainer.&lt;br /&gt;
&lt;br /&gt;
=== 6. Successor‑Friendly Documentation and Maintenance ===&lt;br /&gt;
The new architecture is:&lt;br /&gt;
* linear  &lt;br /&gt;
* transparent  &lt;br /&gt;
* fully documented  &lt;br /&gt;
* reproducible  &lt;br /&gt;
* platform‑consistent  &lt;br /&gt;
&lt;br /&gt;
A successor only needs to understand:&lt;br /&gt;
* nginx vhost files  &lt;br /&gt;
* certbot  &lt;br /&gt;
* Apache backend routing  &lt;br /&gt;
&lt;br /&gt;
No Windows knowledge is required.&lt;br /&gt;
&lt;br /&gt;
=== 7. Removal of Legacy Dependencies ===&lt;br /&gt;
The IIS reverse‑proxy was originally introduced as a workaround during earlier infrastructure phases.  &lt;br /&gt;
Now that the backend is stable on AlmaLinux, the Windows layer no longer serves a purpose and only adds risk.&lt;br /&gt;
&lt;br /&gt;
Removing it:&lt;br /&gt;
* reduces attack surface  &lt;br /&gt;
* removes unused services  &lt;br /&gt;
* avoids silent port conflicts  &lt;br /&gt;
* simplifies backups and disaster recovery  &lt;br /&gt;
&lt;br /&gt;
=== Summary ===&lt;br /&gt;
The migration from IIS to nginx was not just a technical change but an architectural improvement.  &lt;br /&gt;
It aligns the system with:&lt;br /&gt;
* clarity  &lt;br /&gt;
* maintainability  &lt;br /&gt;
* long‑term stability  &lt;br /&gt;
* reduced operational burden  &lt;br /&gt;
* successor‑friendly design  &lt;br /&gt;
&lt;br /&gt;
The result is a cleaner, more predictable, and more robust infrastructure.&lt;br /&gt;
&lt;br /&gt;
== Before vs After: Reverse‑Proxy Architecture ==&lt;br /&gt;
&lt;br /&gt;
The table below summarizes the differences between the former IIS‑based setup and the current nginx‑based architecture.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Aspect&lt;br /&gt;
! &#039;&#039;&#039;Before: Windows + IIS&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;After: AlmaLinux + nginx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Reverse‑proxy layer&lt;br /&gt;
| IIS with ARR + URL Rewrite  &lt;br /&gt;
(added modules, multi‑step configuration)&lt;br /&gt;
| nginx (native reverse‑proxy, single config file per site)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Operating system&lt;br /&gt;
| Windows Server  &lt;br /&gt;
(separate from backend)&lt;br /&gt;
| AlmaLinux  &lt;br /&gt;
(same platform as backend)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Certificate management&lt;br /&gt;
| win‑acme (scheduled task, IIS bindings)  &lt;br /&gt;
Certificates stored in Windows certificate store&lt;br /&gt;
| certbot (systemd timer)  &lt;br /&gt;
Certificates stored directly under /etc/letsencrypt/&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ACME challenge handling&lt;br /&gt;
| IIS rewrite rules + filesystem mapping  &lt;br /&gt;
More fragile and harder to debug&lt;br /&gt;
| Direct nginx location block  &lt;br /&gt;
Shared ACME directory for all vhosts&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Number of moving parts&lt;br /&gt;
| High: Windows + IIS + ARR + Rewrite + win‑acme  &lt;br /&gt;
| Low: nginx + certbot&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Logging&lt;br /&gt;
| Split across Windows Event Viewer, IIS logs, win‑acme logs&lt;br /&gt;
| Unified: nginx logs + certbot logs&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Maintenance burden&lt;br /&gt;
| Requires Windows patching, IIS updates, scheduled task monitoring&lt;br /&gt;
| Minimal: systemd‑managed services, simple config reloads&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Failure modes&lt;br /&gt;
| Certificate renewal failures often silent  &lt;br /&gt;
Bindings could break after updates  &lt;br /&gt;
Proxy rules spread across GUI modules&lt;br /&gt;
| Transparent and predictable  &lt;br /&gt;
Config validated with nginx -t  &lt;br /&gt;
Renewal handled by systemd&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Security surface&lt;br /&gt;
| Larger (Windows + IIS + ARR + Rewrite)&lt;br /&gt;
| Smaller (nginx only)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Successor‑friendliness&lt;br /&gt;
| Requires Windows + IIS knowledge  &lt;br /&gt;
Proxy logic hidden behind GUI layers&lt;br /&gt;
| Fully text‑based, documented, reproducible  &lt;br /&gt;
Easy for any Linux‑familiar maintainer&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Architectural clarity&lt;br /&gt;
| Cross‑platform coupling (Windows → Linux)&lt;br /&gt;
| Single‑platform, self‑contained frontend&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Status ==&lt;br /&gt;
The reverse‑proxy setup is stable, maintainable, and fully Linux‑native.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting Appendix ==&lt;br /&gt;
&lt;br /&gt;
This appendix lists common issues that may occur in the nginx → Apache reverse‑proxy setup, along with clear diagnostic steps and expected outcomes. It is designed to help future maintainers quickly identify where a failure originates.&lt;br /&gt;
&lt;br /&gt;
=== 1. Site Not Loading (General) ===&lt;br /&gt;
If a site does not load at all:&lt;br /&gt;
&lt;br /&gt;
# Check nginx status:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl status nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Validate configuration:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nginx -t&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Reload nginx:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If nginx fails to start or reload, the error message will point to the exact file and line.&lt;br /&gt;
&lt;br /&gt;
=== 2. HTTPS Works but HTTP Does Not Redirect ===&lt;br /&gt;
Symptoms:&lt;br /&gt;
* Visiting http://domain shows an error  &lt;br /&gt;
* Visiting https://domain works normally  &lt;br /&gt;
&lt;br /&gt;
Check the port 80 server block for the domain:&lt;br /&gt;
* It must include the ACME location block  &lt;br /&gt;
* It must end with: &amp;lt;code&amp;gt;return 301 https://$host$request_uri;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart nginx after correcting:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3. Certificate Renewal Fails ===&lt;br /&gt;
Symptoms:&lt;br /&gt;
* Certbot emails warnings  &lt;br /&gt;
* Browser shows certificate expiring soon  &lt;br /&gt;
* Dry‑run renewal fails  &lt;br /&gt;
&lt;br /&gt;
Diagnostics:&lt;br /&gt;
&lt;br /&gt;
# Test ACME challenge manually:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl http://domain/.well-known/acme-challenge/test&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected:&lt;br /&gt;
* 404 Not Found (normal)&lt;br /&gt;
* NOT a 403 or 500&lt;br /&gt;
&lt;br /&gt;
# Check permissions:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ls -ld /var/www/letsencrypt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Run a dry‑run:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot renew --dry-run&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If renewal fails, the error message will indicate whether nginx routing or filesystem access is the cause.&lt;br /&gt;
&lt;br /&gt;
=== 4. Backend (Apache) Not Responding ===&lt;br /&gt;
Symptoms:&lt;br /&gt;
* nginx returns 502 Bad Gateway  &lt;br /&gt;
* nginx returns 504 Gateway Timeout  &lt;br /&gt;
&lt;br /&gt;
Diagnostics:&lt;br /&gt;
&lt;br /&gt;
# Check Apache:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl status httpd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Test backend directly:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl http://192.168.33.231&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If Apache is down, restart it:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl restart httpd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If Apache is up but unreachable, check firewall rules.&lt;br /&gt;
&lt;br /&gt;
=== 5. Wrong Site Appears (Host Header Issue) ===&lt;br /&gt;
Symptoms:&lt;br /&gt;
* Visiting research.* shows the MediaWiki site  &lt;br /&gt;
* Visiting mwiki.* shows the Drupal site  &lt;br /&gt;
&lt;br /&gt;
Cause:&lt;br /&gt;
* Missing or incorrect &amp;lt;code&amp;gt;proxy_set_header Host ...&amp;lt;/code&amp;gt; in nginx&lt;br /&gt;
&lt;br /&gt;
Fix:&lt;br /&gt;
Ensure each vhost sets the correct Host header:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
proxy_set_header Host research.costasano.club;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload nginx afterwards.&lt;br /&gt;
&lt;br /&gt;
=== 6. ACME Challenge Returns 404 ===&lt;br /&gt;
Symptoms:&lt;br /&gt;
* Certbot renewal fails  &lt;br /&gt;
* Browser shows 404 for ACME path  &lt;br /&gt;
&lt;br /&gt;
Check that both vhosts contain:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
location /.well-known/acme-challenge/ {&lt;br /&gt;
    root /var/www/letsencrypt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ensure the directory exists:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ls /var/www/letsencrypt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 7. Windows Still Interfering (Legacy Setup) ===&lt;br /&gt;
Symptoms:&lt;br /&gt;
* Port conflicts  &lt;br /&gt;
* Unexpected certificates  &lt;br /&gt;
* Old bindings still active  &lt;br /&gt;
&lt;br /&gt;
Diagnostics on Windows:&lt;br /&gt;
&lt;br /&gt;
# Check ports:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
netstat -ano | findstr :80&lt;br /&gt;
netstat -ano | findstr :443&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Check IIS:&lt;br /&gt;
Ensure no sites remain in IIS Manager.&lt;br /&gt;
&lt;br /&gt;
# Check scheduled tasks:&lt;br /&gt;
Remove any leftover win‑acme tasks.&lt;br /&gt;
&lt;br /&gt;
If Windows is still listening on 80/443, disable the Web Server (IIS) role entirely.&lt;br /&gt;
&lt;br /&gt;
=== 8. Browser Shows Old Certificate ===&lt;br /&gt;
Symptoms:&lt;br /&gt;
* Browser still shows the old expiry date  &lt;br /&gt;
* Renewal succeeded on the server  &lt;br /&gt;
&lt;br /&gt;
Possible causes:&lt;br /&gt;
* Browser caching  &lt;br /&gt;
* Intermediate certificate caching  &lt;br /&gt;
* CDN or proxy caching (if applicable)&lt;br /&gt;
&lt;br /&gt;
Fix:&lt;br /&gt;
* Hard refresh (Ctrl+F5)  &lt;br /&gt;
* Test with curl:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -v https://domain&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The certificate shown by curl is authoritative.&lt;br /&gt;
&lt;br /&gt;
=== 9. nginx Reload Fails After Editing Config ===&lt;br /&gt;
Symptoms:&lt;br /&gt;
* &amp;lt;code&amp;gt;systemctl reload nginx&amp;lt;/code&amp;gt; fails  &lt;br /&gt;
* nginx refuses to start  &lt;br /&gt;
&lt;br /&gt;
Run:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nginx -t&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will show:&lt;br /&gt;
* the file  &lt;br /&gt;
* the line  &lt;br /&gt;
* the exact syntax error  &lt;br /&gt;
&lt;br /&gt;
Fix the issue and reload nginx.&lt;br /&gt;
&lt;br /&gt;
=== 10. Slow Responses or Timeouts ===&lt;br /&gt;
Symptoms:&lt;br /&gt;
* Pages load slowly  &lt;br /&gt;
* MediaWiki or Drupal feels sluggish  &lt;br /&gt;
&lt;br /&gt;
Check proxy timeouts:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
proxy_read_timeout 60s;&lt;br /&gt;
proxy_send_timeout 60s;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check backend load:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
top&lt;br /&gt;
systemctl status httpd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If Apache is overloaded, investigate modules, PHP-FPM, or database performance.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
This appendix provides a structured approach to diagnosing issues in the nginx → Apache reverse‑proxy setup. Most problems fall into one of three categories:&lt;br /&gt;
* nginx configuration  &lt;br /&gt;
* certificate renewal  &lt;br /&gt;
* backend availability  &lt;br /&gt;
&lt;br /&gt;
Following the steps above should isolate the cause quickly and safely.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notes for AlmaLinux 10.1 Security Changes ==&lt;br /&gt;
&lt;br /&gt;
AlmaLinux 10.1 introduces several security‑related changes that affect nginx, certbot, and&lt;br /&gt;
backend communication. The following points must be checked whenever the system is updated&lt;br /&gt;
or rebuilt.&lt;br /&gt;
&lt;br /&gt;
=== 1. SELinux: Stricter Defaults ===&lt;br /&gt;
AlmaLinux 10.1 enforces tighter SELinux rules, especially for:&lt;br /&gt;
* nginx reading files outside /usr/share/nginx/html&lt;br /&gt;
* ACME challenge directories&lt;br /&gt;
* proxy connections to backend servers&lt;br /&gt;
&lt;br /&gt;
Ensure:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
semanage fcontext -a -t httpd_sys_content_t &amp;quot;/var/www/letsencrypt(/.*)?&amp;quot;&lt;br /&gt;
restorecon -Rv /var/www/letsencrypt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If nginx cannot reach Apache:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
setsebool -P httpd_can_network_connect 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. FirewallD: More Restrictive Defaults ===&lt;br /&gt;
After updates, FirewallD may re‑apply stricter profiles.&lt;br /&gt;
&lt;br /&gt;
Verify:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
firewall-cmd --list-services&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ensure:&lt;br /&gt;
* http&lt;br /&gt;
* https&lt;br /&gt;
&lt;br /&gt;
are enabled permanently.&lt;br /&gt;
&lt;br /&gt;
=== 3. OpenSSL 3.x Enforcement ===&lt;br /&gt;
AlmaLinux 10.1 uses OpenSSL 3.x with stricter cipher policies.&lt;br /&gt;
&lt;br /&gt;
Ensure nginx uses:&lt;br /&gt;
* TLSv1.2&lt;br /&gt;
* TLSv1.3&lt;br /&gt;
&lt;br /&gt;
and avoid deprecated ciphers.&lt;br /&gt;
&lt;br /&gt;
=== 4. Systemd Hardening of Services ===&lt;br /&gt;
Some systemd units (nginx, certbot) may receive tightened sandboxing.&lt;br /&gt;
&lt;br /&gt;
If certbot renewal fails after an update:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
journalctl -u certbot-renew.service&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check for:&lt;br /&gt;
* permission denials&lt;br /&gt;
* sandbox restrictions&lt;br /&gt;
* missing ACME directory access&lt;br /&gt;
&lt;br /&gt;
=== 5. nginx: HTTP/2 and TLS Requirements ===&lt;br /&gt;
AlmaLinux 10.1 enforces modern TLS for HTTP/2.&lt;br /&gt;
&lt;br /&gt;
Ensure:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
http2 on;&lt;br /&gt;
ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
HTTP/2 will silently disable itself if insecure ciphers are used.&lt;br /&gt;
&lt;br /&gt;
=== 6. Apache: Private IP Binding Verification ===&lt;br /&gt;
After updates, Apache may rebind or change Listen directives.&lt;br /&gt;
&lt;br /&gt;
Verify:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
grep -R &amp;quot;Listen&amp;quot; /etc/httpd/conf*&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected:&lt;br /&gt;
* Apache listens on 192.168.33.231:80&lt;br /&gt;
* NOT on 0.0.0.0:80 (to avoid conflicts)&lt;br /&gt;
&lt;br /&gt;
=== 7. Certbot: Renewal Timer Integrity ===&lt;br /&gt;
System updates may reset or disable timers.&lt;br /&gt;
&lt;br /&gt;
Verify:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl status certbot-renew.timer&lt;br /&gt;
systemctl list-timers | grep certbot&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Expected:&lt;br /&gt;
* Timer active&lt;br /&gt;
* Next run scheduled within 24 hours&lt;br /&gt;
&lt;br /&gt;
=== 8. Kernel-Level Networking Changes ===&lt;br /&gt;
AlmaLinux 10.1 includes updated nftables and TCP stack behaviour.&lt;br /&gt;
&lt;br /&gt;
If proxying becomes slow or unstable:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sysctl net.ipv4.ip_local_port_range&lt;br /&gt;
sysctl net.core.somaxconn&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ensure defaults are not overly restrictive.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
AlmaLinux 10.1 is more secure by default, but this can affect nginx, certbot, and Apache&lt;br /&gt;
interactions. After any major update, verify:&lt;br /&gt;
* SELinux permissions  &lt;br /&gt;
* FirewallD rules  &lt;br /&gt;
* nginx TLS configuration  &lt;br /&gt;
* Apache binding  &lt;br /&gt;
* certbot timers  &lt;br /&gt;
&lt;br /&gt;
This ensures the reverse‑proxy remains stable and predictable.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Reverse-Proxy_on_AlmaLinux_with_nginx_to_apache_backend&amp;diff=1572</id>
		<title>ICT:Reverse-Proxy on AlmaLinux with nginx to apache backend</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Reverse-Proxy_on_AlmaLinux_with_nginx_to_apache_backend&amp;diff=1572"/>
		<updated>2026-03-25T17:56:51Z</updated>

		<summary type="html">&lt;p&gt;Mngr: Created page with &amp;quot;= Reverse‑Proxy Architecture (nginx → Apache) = This page documents the complete reverse‑proxy setup for the AlmaLinux frontend server. It replaces the former IIS reverse‑proxy and Windows‑based certificate renewal system. The configuration is now fully Linux‑native, stable, and successor‑friendly.  == Overview == * Frontend: AlmaLinux + nginx * Backend: Apache (Drupal + MediaWiki) * Certificates: Let’s Encrypt (certbot) * Automatic renewal: systemd timer...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Reverse‑Proxy Architecture (nginx → Apache) =&lt;br /&gt;
This page documents the complete reverse‑proxy setup for the AlmaLinux frontend server.&lt;br /&gt;
It replaces the former IIS reverse‑proxy and Windows‑based certificate renewal system.&lt;br /&gt;
The configuration is now fully Linux‑native, stable, and successor‑friendly.&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
* Frontend: AlmaLinux + nginx&lt;br /&gt;
* Backend: Apache (Drupal + MediaWiki)&lt;br /&gt;
* Certificates: Let’s Encrypt (certbot)&lt;br /&gt;
* Automatic renewal: systemd timer&lt;br /&gt;
* Windows/IIS: fully retired (no bindings, no renewals, no proxies)&lt;br /&gt;
&lt;br /&gt;
The nginx server terminates HTTPS and forwards traffic to the internal Apache instance.&lt;br /&gt;
&lt;br /&gt;
== Directory Layout ==&lt;br /&gt;
* &#039;&#039;&#039;/etc/nginx/nginx.conf&#039;&#039;&#039; – global nginx configuration  &lt;br /&gt;
* &#039;&#039;&#039;/etc/nginx/conf.d/&#039;&#039;&#039; – per‑site reverse‑proxy configs  &lt;br /&gt;
* &#039;&#039;&#039;/var/www/letsencrypt/&#039;&#039;&#039; – ACME challenge directory  &lt;br /&gt;
* &#039;&#039;&#039;/etc/letsencrypt/live/&amp;lt;domain&amp;gt;/&#039;&#039;&#039; – certificate files  &lt;br /&gt;
&lt;br /&gt;
== ACME Challenge Directory ==&lt;br /&gt;
Certbot uses a shared directory for HTTP‑01 validation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/var/www/letsencrypt/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
nginx exposes this path for both domains.&lt;br /&gt;
&lt;br /&gt;
== Site: research.costasano.club ==&lt;br /&gt;
=== HTTP (port 80) ===&lt;br /&gt;
Redirects all traffic to HTTPS, except ACME challenges.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name research.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /var/www/letsencrypt;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HTTPS (port 443) ===&lt;br /&gt;
Terminates TLS and proxies to Apache.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name research.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/research.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/research.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /var/www/letsencrypt;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host research.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Site: mwiki.costasano.club ==&lt;br /&gt;
=== HTTP (port 80) ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    listen [::]:80;&lt;br /&gt;
    server_name mwiki.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /var/www/letsencrypt;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HTTPS (port 443) ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    listen [::]:443 ssl;&lt;br /&gt;
    http2 on;&lt;br /&gt;
&lt;br /&gt;
    server_name mwiki.costasano.club;&lt;br /&gt;
&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/mwiki.costasano.club/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/mwiki.costasano.club/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    ssl_protocols TLSv1.2 TLSv1.3;&lt;br /&gt;
    ssl_prefer_server_ciphers on;&lt;br /&gt;
&lt;br /&gt;
    location /.well-known/acme-challenge/ {&lt;br /&gt;
        root /var/www/letsencrypt;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_pass http://192.168.33.231;&lt;br /&gt;
&lt;br /&gt;
        proxy_set_header Host mwiki.costasano.club;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto https;&lt;br /&gt;
&lt;br /&gt;
        proxy_buffering on;&lt;br /&gt;
        proxy_buffers 16 16k;&lt;br /&gt;
        proxy_buffer_size 16k;&lt;br /&gt;
&lt;br /&gt;
        proxy_connect_timeout 60s;&lt;br /&gt;
        proxy_send_timeout 60s;&lt;br /&gt;
        proxy_read_timeout 60s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Certificate Renewal ==&lt;br /&gt;
Certbot is installed natively on AlmaLinux.&lt;br /&gt;
&lt;br /&gt;
=== Renewal Timer ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl status certbot-renew.timer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The timer runs daily and triggers renewal when certificates approach expiry.&lt;br /&gt;
&lt;br /&gt;
=== Manual Dry‑Run Test ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot renew --dry-run&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Windows/IIS Retirement ==&lt;br /&gt;
The former IIS reverse‑proxy and win‑acme renewal system were removed:&lt;br /&gt;
* IIS sites deleted  &lt;br /&gt;
* win‑acme renewal entries cancelled  &lt;br /&gt;
* scheduled tasks removed  &lt;br /&gt;
* old certificates removed from certlm.msc  &lt;br /&gt;
* no bindings remain on ports 80/443  &lt;br /&gt;
&lt;br /&gt;
== Verification Checklist ==&lt;br /&gt;
* nginx configs validated: &amp;lt;code&amp;gt;nginx -t&amp;lt;/code&amp;gt;  &lt;br /&gt;
* nginx reloaded: &amp;lt;code&amp;gt;systemctl reload nginx&amp;lt;/code&amp;gt;  &lt;br /&gt;
* certbot timer active  &lt;br /&gt;
* ACME challenge reachable  &lt;br /&gt;
* backend Apache reachable  &lt;br /&gt;
* Windows silent on ports 80/443  &lt;br /&gt;
&lt;br /&gt;
== Architecture Diagram (ASCII) ==&lt;br /&gt;
&lt;br /&gt;
The following diagram shows the full request flow from the internet to the backend applications.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                 +----------------------+&lt;br /&gt;
                 |      Internet        |&lt;br /&gt;
                 +----------+-----------+&lt;br /&gt;
                            |&lt;br /&gt;
                            v&lt;br /&gt;
                 +----------------------+&lt;br /&gt;
                 |   AlmaLinux Server   |&lt;br /&gt;
                 |      (frontend)      |&lt;br /&gt;
                 +----------+-----------+&lt;br /&gt;
                            |&lt;br /&gt;
                HTTPS :443 / HTTP :80&lt;br /&gt;
                            |&lt;br /&gt;
                            v&lt;br /&gt;
                    +---------------+&lt;br /&gt;
                    |    nginx      |&lt;br /&gt;
                    |  reverse proxy|&lt;br /&gt;
                    +-------+-------+&lt;br /&gt;
                            |&lt;br /&gt;
        +-------------------+-------------------+&lt;br /&gt;
        |                                       |&lt;br /&gt;
        v                                       v&lt;br /&gt;
+---------------------+               +---------------------+&lt;br /&gt;
| research.costasano  |               | mwiki.costasano     |&lt;br /&gt;
| .club (vhost)       |               | .club (vhost)       |&lt;br /&gt;
+----------+----------+               +----------+----------+&lt;br /&gt;
           |                                     |&lt;br /&gt;
           | proxy_pass http://192.168.33.231    |&lt;br /&gt;
           +-------------------------+-----------+&lt;br /&gt;
                                     |&lt;br /&gt;
                                     v&lt;br /&gt;
                         +------------------------+&lt;br /&gt;
                         |   Apache (backend)     |&lt;br /&gt;
                         |  192.168.33.231        |&lt;br /&gt;
                         +-----------+------------+&lt;br /&gt;
                                     |&lt;br /&gt;
                 +-------------------+-------------------+&lt;br /&gt;
                 |                                       |&lt;br /&gt;
                 v                                       v&lt;br /&gt;
        +-------------------+                  +-------------------+&lt;br /&gt;
        |   Drupal site     |                  |  MediaWiki site   |&lt;br /&gt;
        | research.*        |                  | mwiki.*           |&lt;br /&gt;
        +-------------------+                  +-------------------+&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ACME / Let’s Encrypt HTTP-01 validation:&lt;br /&gt;
&lt;br /&gt;
    Let’s Encrypt  ---&amp;gt;  nginx  ---&amp;gt;  /var/www/letsencrypt/&lt;br /&gt;
                         (both vhosts expose&lt;br /&gt;
                          /.well-known/acme-challenge/)&lt;br /&gt;
&lt;br /&gt;
Certificates:&lt;br /&gt;
&lt;br /&gt;
    /etc/letsencrypt/live/research.costasano.club/&lt;br /&gt;
    /etc/letsencrypt/live/mwiki.costasano.club/&lt;br /&gt;
&lt;br /&gt;
Renewal:&lt;br /&gt;
&lt;br /&gt;
    systemd timer: certbot-renew.timer&lt;br /&gt;
    service:       certbot-renew.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Rationale for Replacing IIS with nginx ==&lt;br /&gt;
&lt;br /&gt;
This section explains why the former Windows/IIS reverse‑proxy was retired in favour of a native Linux‑based nginx setup. The decision was driven by stability, clarity, maintainability, and long‑term operational safety.&lt;br /&gt;
&lt;br /&gt;
=== 1. Architectural Simplicity ===&lt;br /&gt;
The previous setup required:&lt;br /&gt;
* Windows Server&lt;br /&gt;
* IIS with URL Rewrite + ARR&lt;br /&gt;
* win-acme for certificate renewal&lt;br /&gt;
* Manual bindings for each hostname&lt;br /&gt;
* Coordination between Windows and the Linux backend&lt;br /&gt;
&lt;br /&gt;
This created a multi‑platform dependency chain.  &lt;br /&gt;
The new setup collapses the entire frontend into a single, predictable layer:&lt;br /&gt;
* AlmaLinux&lt;br /&gt;
* nginx&lt;br /&gt;
* certbot (systemd‑managed)&lt;br /&gt;
&lt;br /&gt;
Fewer moving parts → fewer failure points.&lt;br /&gt;
&lt;br /&gt;
=== 2. Elimination of Cross‑Platform Coupling ===&lt;br /&gt;
The old architecture required Windows to proxy traffic to Linux.  &lt;br /&gt;
This meant:&lt;br /&gt;
* Two operating systems had to stay in sync  &lt;br /&gt;
* Two certificate systems had to be maintained  &lt;br /&gt;
* Two sets of logs had to be consulted during troubleshooting  &lt;br /&gt;
&lt;br /&gt;
By moving the reverse‑proxy to the same machine that hosts the backend, the system becomes self‑contained and easier to reason about.&lt;br /&gt;
&lt;br /&gt;
=== 3. Cleaner TLS and Certificate Management ===&lt;br /&gt;
The Windows setup used win‑acme, scheduled tasks, and IIS bindings.  &lt;br /&gt;
This caused:&lt;br /&gt;
* Duplicate certificate stores  &lt;br /&gt;
* Renewal tasks that were easy to forget  &lt;br /&gt;
* Certificates living on a machine that no longer served the content  &lt;br /&gt;
&lt;br /&gt;
The new setup uses:&lt;br /&gt;
* certbot  &lt;br /&gt;
* systemd timers  &lt;br /&gt;
* a shared ACME challenge directory  &lt;br /&gt;
* certificates stored exactly where they are used  &lt;br /&gt;
&lt;br /&gt;
This is simpler, safer, and fully automated.&lt;br /&gt;
&lt;br /&gt;
=== 4. nginx Is Purpose‑Built for Reverse‑Proxying ===&lt;br /&gt;
nginx excels at:&lt;br /&gt;
* high‑performance proxying  &lt;br /&gt;
* buffering  &lt;br /&gt;
* header management  &lt;br /&gt;
* HTTP/2  &lt;br /&gt;
* TLS termination  &lt;br /&gt;
&lt;br /&gt;
IIS can do these things, but only through multiple modules (ARR, URL Rewrite) and with more complexity. nginx provides them natively and predictably.&lt;br /&gt;
&lt;br /&gt;
=== 5. Reduced Operational Overhead ===&lt;br /&gt;
The Windows/IIS layer required:&lt;br /&gt;
* OS patching  &lt;br /&gt;
* IIS updates  &lt;br /&gt;
* win‑acme maintenance  &lt;br /&gt;
* Monitoring of scheduled tasks  &lt;br /&gt;
* Manual cleanup of old bindings and certificates  &lt;br /&gt;
&lt;br /&gt;
The nginx layer requires:&lt;br /&gt;
* a single systemd timer  &lt;br /&gt;
* a single config directory  &lt;br /&gt;
* one place to check logs  &lt;br /&gt;
&lt;br /&gt;
This reduces cognitive load for both you and any future maintainer.&lt;br /&gt;
&lt;br /&gt;
=== 6. Successor‑Friendly Documentation and Maintenance ===&lt;br /&gt;
The new architecture is:&lt;br /&gt;
* linear  &lt;br /&gt;
* transparent  &lt;br /&gt;
* fully documented  &lt;br /&gt;
* reproducible  &lt;br /&gt;
* platform‑consistent  &lt;br /&gt;
&lt;br /&gt;
A successor only needs to understand:&lt;br /&gt;
* nginx vhost files  &lt;br /&gt;
* certbot  &lt;br /&gt;
* Apache backend routing  &lt;br /&gt;
&lt;br /&gt;
No Windows knowledge is required.&lt;br /&gt;
&lt;br /&gt;
=== 7. Removal of Legacy Dependencies ===&lt;br /&gt;
The IIS reverse‑proxy was originally introduced as a workaround during earlier infrastructure phases.  &lt;br /&gt;
Now that the backend is stable on AlmaLinux, the Windows layer no longer serves a purpose and only adds risk.&lt;br /&gt;
&lt;br /&gt;
Removing it:&lt;br /&gt;
* reduces attack surface  &lt;br /&gt;
* removes unused services  &lt;br /&gt;
* avoids silent port conflicts  &lt;br /&gt;
* simplifies backups and disaster recovery  &lt;br /&gt;
&lt;br /&gt;
=== Summary ===&lt;br /&gt;
The migration from IIS to nginx was not just a technical change but an architectural improvement.  &lt;br /&gt;
It aligns the system with:&lt;br /&gt;
* clarity  &lt;br /&gt;
* maintainability  &lt;br /&gt;
* long‑term stability  &lt;br /&gt;
* reduced operational burden  &lt;br /&gt;
* successor‑friendly design  &lt;br /&gt;
&lt;br /&gt;
The result is a cleaner, more predictable, and more robust infrastructure.&lt;br /&gt;
&lt;br /&gt;
== Before vs After: Reverse‑Proxy Architecture ==&lt;br /&gt;
&lt;br /&gt;
The table below summarizes the differences between the former IIS‑based setup and the current nginx‑based architecture.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Aspect&lt;br /&gt;
! &#039;&#039;&#039;Before: Windows + IIS&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;After: AlmaLinux + nginx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Reverse‑proxy layer&lt;br /&gt;
| IIS with ARR + URL Rewrite  &lt;br /&gt;
(added modules, multi‑step configuration)&lt;br /&gt;
| nginx (native reverse‑proxy, single config file per site)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Operating system&lt;br /&gt;
| Windows Server  &lt;br /&gt;
(separate from backend)&lt;br /&gt;
| AlmaLinux  &lt;br /&gt;
(same platform as backend)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Certificate management&lt;br /&gt;
| win‑acme (scheduled task, IIS bindings)  &lt;br /&gt;
Certificates stored in Windows certificate store&lt;br /&gt;
| certbot (systemd timer)  &lt;br /&gt;
Certificates stored directly under /etc/letsencrypt/&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ACME challenge handling&lt;br /&gt;
| IIS rewrite rules + filesystem mapping  &lt;br /&gt;
More fragile and harder to debug&lt;br /&gt;
| Direct nginx location block  &lt;br /&gt;
Shared ACME directory for all vhosts&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Number of moving parts&lt;br /&gt;
| High: Windows + IIS + ARR + Rewrite + win‑acme  &lt;br /&gt;
| Low: nginx + certbot&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Logging&lt;br /&gt;
| Split across Windows Event Viewer, IIS logs, win‑acme logs&lt;br /&gt;
| Unified: nginx logs + certbot logs&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Maintenance burden&lt;br /&gt;
| Requires Windows patching, IIS updates, scheduled task monitoring&lt;br /&gt;
| Minimal: systemd‑managed services, simple config reloads&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Failure modes&lt;br /&gt;
| Certificate renewal failures often silent  &lt;br /&gt;
Bindings could break after updates  &lt;br /&gt;
Proxy rules spread across GUI modules&lt;br /&gt;
| Transparent and predictable  &lt;br /&gt;
Config validated with nginx -t  &lt;br /&gt;
Renewal handled by systemd&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Security surface&lt;br /&gt;
| Larger (Windows + IIS + ARR + Rewrite)&lt;br /&gt;
| Smaller (nginx only)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Successor‑friendliness&lt;br /&gt;
| Requires Windows + IIS knowledge  &lt;br /&gt;
Proxy logic hidden behind GUI layers&lt;br /&gt;
| Fully text‑based, documented, reproducible  &lt;br /&gt;
Easy for any Linux‑familiar maintainer&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Architectural clarity&lt;br /&gt;
| Cross‑platform coupling (Windows → Linux)&lt;br /&gt;
| Single‑platform, self‑contained frontend&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Status ==&lt;br /&gt;
The reverse‑proxy setup is stable, maintainable, and fully Linux‑native.&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Drupal_Blueprint_-_Version_6.5&amp;diff=1566</id>
		<title>ICT:Drupal Blueprint - Version 6.5</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Drupal_Blueprint_-_Version_6.5&amp;diff=1566"/>
		<updated>2026-03-23T10:51:15Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Costasano Heritage Project — Drupal 11 Architecture &amp;amp; Workflow Blueprint (v6.5) =&lt;br /&gt;
&lt;br /&gt;
== Purpose of this Document ==&lt;br /&gt;
This page defines the architectural, conceptual, and workflow foundations of the Costasano Heritage Project.&lt;br /&gt;
&lt;br /&gt;
It serves as:&lt;br /&gt;
* a stable reference for human contributors&lt;br /&gt;
* a briefing document for AI-assisted work sessions&lt;br /&gt;
* a safeguard against context loss over long-term development&lt;br /&gt;
&lt;br /&gt;
All future design, implementation, and automation decisions must be compatible with the principles defined here.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
The Costasano Heritage Project is a long-term digital heritage initiative operated by members of the Costa Sano Club (70+ years old, amateur historians, non-ICT specialists, except for Martin, realising the IT-project, also amateur historian).&lt;br /&gt;
&lt;br /&gt;
The project manages:&lt;br /&gt;
* hundreds of gigabytes of documents, images, and maps&lt;br /&gt;
* collaborative historical research&lt;br /&gt;
* multi-channel dissemination (wiki, public website, exhibitions, video)&lt;br /&gt;
&lt;br /&gt;
The system is designed for &#039;&#039;&#039;decades-long durability&#039;&#039;&#039;, not short-term publication.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Core Philosophy ==&lt;br /&gt;
&lt;br /&gt;
=== Authoritative Source Principle ===&lt;br /&gt;
There is exactly ONE authoritative system for data and assets:&lt;br /&gt;
* &#039;&#039;&#039;Drupal 11.3&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
All other outputs (MediaWiki, public website, video, exhibitions) are derivatives.&lt;br /&gt;
&lt;br /&gt;
No downstream system is allowed to correct, invent, or override facts.  &lt;br /&gt;
All corrections must return to Drupal.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Asset-Centric Design ===&lt;br /&gt;
The central object of the entire system is:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Asset + File (ONE asset = ONE file)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Everything else (chapter, place, organisation) exists to:&lt;br /&gt;
* contextualize assets&lt;br /&gt;
* classify assets&lt;br /&gt;
* generate stable identifiers&lt;br /&gt;
* reuse assets across outputs&lt;br /&gt;
* chapters provide a sort of timeline&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Ingest First, Enrich Later ===&lt;br /&gt;
The system must allow:&lt;br /&gt;
* rapid bulk ingestion of files with minimal metadata&lt;br /&gt;
* gradual collaborative enrichment over time&lt;br /&gt;
* clear lifecycle states without pressure for completeness&lt;br /&gt;
&lt;br /&gt;
Completeness is a process, not a prerequisite.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Target Users &amp;amp; UX Constraints ==&lt;br /&gt;
&lt;br /&gt;
=== User Profile ===&lt;br /&gt;
* Age: 70+&lt;br /&gt;
* Background: amateur historians&lt;br /&gt;
* ICT skills: minimal&lt;br /&gt;
* Tolerance for complexity: low&lt;br /&gt;
&lt;br /&gt;
=== UX Non-Negotiables ===&lt;br /&gt;
* No technical terminology exposed (no “entity”, “taxonomy”, “foreign key”)&lt;br /&gt;
* No cluttered forms&lt;br /&gt;
* Dashboard-first and only navigation&lt;br /&gt;
* Large click targets&lt;br /&gt;
* Predictable layouts&lt;br /&gt;
* Dark mode mandatory (accessibility)&lt;br /&gt;
&lt;br /&gt;
Users must never feel they are “using Drupal”.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== System Architecture ==&lt;br /&gt;
&lt;br /&gt;
=== Infrastructure ===&lt;br /&gt;
* Reverse Proxy: Windows Server 2019 (IIS + ARR, SSL offloading)&lt;br /&gt;
* Web Server: AlmaLinux 10.1 (Apache 2.4.63, PHP 8.3.29, PHP-FPM)&lt;br /&gt;
* Database Server: AlmaLinux 10.1 (MariaDB 10.11.15)&lt;br /&gt;
* Drupal 11.3.5&lt;br /&gt;
* SELinux: Enforcing&lt;br /&gt;
* PHP-FPM: Per-site pools&lt;br /&gt;
* Filesystem: Hardened&lt;br /&gt;
&lt;br /&gt;
=== Operational Constants ===&lt;br /&gt;
* &#039;&#039;&#039;Site URL&#039;&#039;&#039;: https://research.costasano.club&lt;br /&gt;
* &#039;&#039;&#039;Drupal project root&#039;&#039;&#039;: /var/www/drupal&lt;br /&gt;
* &#039;&#039;&#039;Drupal web root&#039;&#039;&#039;: /var/www/drupal/web&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Output Channels ==&lt;br /&gt;
&lt;br /&gt;
=== 1. MediaWiki (Research &amp;amp; Writing) ===&lt;br /&gt;
Role:&lt;br /&gt;
* illustrated research pages&lt;br /&gt;
* collaborative narrative writing&lt;br /&gt;
* preparation for Wikipedia publication&lt;br /&gt;
&lt;br /&gt;
MediaWiki does NOT:&lt;br /&gt;
* store authoritative metadata&lt;br /&gt;
* manage files&lt;br /&gt;
* generate identifiers&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== 2. Public Drupal Website ===&lt;br /&gt;
Role:&lt;br /&gt;
* curated presentation of selected research&lt;br /&gt;
* timelines, maps, galleries&lt;br /&gt;
&lt;br /&gt;
Only “complete” or approved assets are shown publicly.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== 3. Offline Outputs ===&lt;br /&gt;
* video documentaries&lt;br /&gt;
* lectures&lt;br /&gt;
* exhibitions&lt;br /&gt;
&lt;br /&gt;
These reuse:&lt;br /&gt;
* the same assets&lt;br /&gt;
* the same identifiers&lt;br /&gt;
* the same metadata&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Authority Entity Implementation Strategy ==&lt;br /&gt;
&lt;br /&gt;
Authority entities are implemented progressively, from simplest to most complex.  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Step 1: Chapter (Temporal Authority) ===&lt;br /&gt;
Type:&lt;br /&gt;
* &#039;&#039;&#039;Drupal Custom Content Entity (authority)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Meaning:&lt;br /&gt;
* historical periods / timeline segmentation&lt;br /&gt;
* not editorial or narrative content&lt;br /&gt;
&lt;br /&gt;
Characteristics:&lt;br /&gt;
* stable Code (max 6 characters) like CHxx.ab CH01, CH01a, CH01aa, CH01ab&lt;br /&gt;
* recursive (parent/child) two levels using small letters after the first four&lt;br /&gt;
* stable Code (max 6 characters)&lt;br /&gt;
* used as first mandatory component in asset identifiers&lt;br /&gt;
* no sequence field&lt;br /&gt;
&lt;br /&gt;
Dashboard pattern:&lt;br /&gt;
* table of existing records&lt;br /&gt;
* add new record&lt;br /&gt;
* click pencil in front of row to edit&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and Dashboard page operational&lt;br /&gt;
Changed decision about chapter code layout&lt;br /&gt;
ToDo (max 6 characters)&lt;br /&gt;
* Address field for geocoding&lt;br /&gt;
* coordinates auto-derived when possible&lt;br /&gt;
* coordinates never required for ingestion&lt;br /&gt;
&lt;br /&gt;
Purpose:&lt;br /&gt;
* spatial context&lt;br /&gt;
* optionally reused across assets for numbering reasoons&lt;br /&gt;
* reused for organisations&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and Dashboard page operational&lt;br /&gt;
Recently added: type list, geo link in view towards openstreetmap&lt;br /&gt;
Added view adapted to type.&lt;br /&gt;
Address to coordinates implemented using Openstreatmap providers. &lt;br /&gt;
ToDo:Completing helpfunctions&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Step 3: Organisation (Institutional Authority) ===&lt;br /&gt;
Type:&lt;br /&gt;
* &#039;&#039;&#039;Drupal Custom Content Entity (authority)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Characteristics:&lt;br /&gt;
* stable Code (max 6 characters)&lt;br /&gt;
* linked to a Place&lt;br /&gt;
* optionally reused across assets for numbering&lt;br /&gt;
&lt;br /&gt;
Purpose:&lt;br /&gt;
* institutional context&lt;br /&gt;
* archival provenance&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and Dashboard page operational&lt;br /&gt;
ToDo: adding map view based on Place coordinnates now that provider is available&lt;br /&gt;
Completing help functions&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Step 4: Asset (CORE ENTITY) ===&lt;br /&gt;
Type:&lt;br /&gt;
* &#039;&#039;&#039;Drupal Custom Content Entity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Rules:&lt;br /&gt;
* ONE asset = ONE file&lt;br /&gt;
* file is immutable once uploaded&lt;br /&gt;
* identifier is immutable once generated&lt;br /&gt;
* physical filenames are managed by Drupal&lt;br /&gt;
* original filename is preserved for user reference&lt;br /&gt;
&lt;br /&gt;
Identifier format:&lt;br /&gt;
* Chapter.Code&lt;br /&gt;
* Context.Code = Place.Code OR Organisation.Code&lt;br /&gt;
* Scoped 5-digit counter (asset sequence only)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CH03ab-OOSTEN-00007&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Asset creation is impossible before Chapter, Place, and Organisation exist.&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and View complete&lt;br /&gt;
Use of special configuration of classic Media-Library for media upload.&lt;br /&gt;
Upload of images, documents, video and audio foreseen&lt;br /&gt;
Automatic numbering for asset via a custom module&lt;br /&gt;
Creation in 2 steps: first essential fields to generate autonumbering.&lt;br /&gt;
After that, use of editing via table to fill un further fields like description and note.&lt;br /&gt;
Optional change of other fields except autocode.&lt;br /&gt;
View enhanced with search filter.&lt;br /&gt;
ToDo: complete help functions&lt;br /&gt;
Remark: Inital configuration with Code as PK (Drupal Title) has been abandoned.&lt;br /&gt;
Now Code is a classic field and the Title field is a Caption field. This is a short description of the file.&lt;br /&gt;
&lt;br /&gt;
== Next Major step: Person and HeritageObject and the m-m relationship with Asset ==&lt;br /&gt;
&lt;br /&gt;
=== Step 5: Person implementation ===&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;Person&#039;&#039;&#039; represents a historical individual with agency.&lt;br /&gt;
&lt;br /&gt;
It answers the question:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;“Who was involved historically?”&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A person is:&lt;br /&gt;
&lt;br /&gt;
* religious sisters&lt;br /&gt;
* directors&lt;br /&gt;
* architects&lt;br /&gt;
* patients&lt;br /&gt;
* shareholders&lt;br /&gt;
* board members&lt;br /&gt;
&lt;br /&gt;
A Person may:&lt;br /&gt;
&lt;br /&gt;
* play roles in relation to HeritageObjects&lt;br /&gt;
* play roles within Organizations&lt;br /&gt;
* act as a holder of HeritageObjects&lt;br /&gt;
* be documented by DigitalAssets (portraits, letters, biographies, articles)&lt;br /&gt;
&lt;br /&gt;
Roles belong to relationships, not to the Person entity itself.&lt;br /&gt;
&lt;br /&gt;
Purpose: Persons model historical agency, responsibility, and participation.&lt;br /&gt;
&lt;br /&gt;
Implementation: &lt;br /&gt;
- automatic numbering of Person entities realized with custom module: PE-00123&lt;br /&gt;
- use for plain text for birthday and deathday due to drupal limitation in Date &amp;amp; Time widget&lt;br /&gt;
- use of custom module to verify the day format DD/MM/YYYY &lt;br /&gt;
- both modules are implemented in heritage-tweaks&lt;br /&gt;
- first view operational&lt;br /&gt;
ToDo: refinement&lt;br /&gt;
&lt;br /&gt;
=== Step 6: HeritageObject implementation ===&lt;br /&gt;
The implementation of HeritageObject abbreviated Object in Drupal&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;HeritageObject (HO)&#039;&#039;&#039; represents a historical, conceptual, or material entity that is the subject of study.&lt;br /&gt;
&lt;br /&gt;
It answers the question:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;“What is the thing we are studying?”&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A HeritageObject may represent:&lt;br /&gt;
&lt;br /&gt;
* a sanatorium&lt;br /&gt;
* a building&lt;br /&gt;
* a document or register&lt;br /&gt;
* a historically meaningful place&lt;br /&gt;
* a room, component, or architectural element&lt;br /&gt;
* a conceptual or functional unit (e.g. “medical practice”)&lt;br /&gt;
&lt;br /&gt;
HeritageObjects are &#039;&#039;&#039;recursive&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Each HeritageObject may:&lt;br /&gt;
&lt;br /&gt;
* have zero or one parent HeritageObject&lt;br /&gt;
* have zero or more child HeritageObjects&lt;br /&gt;
&lt;br /&gt;
A HeritageObject may:&lt;br /&gt;
&lt;br /&gt;
* be documented by multiple DigitalAssets&lt;br /&gt;
* designate one DigitalAsset as preferred representation&lt;br /&gt;
* be linked to Persons with roles&lt;br /&gt;
* be linked to Organizations with roles&lt;br /&gt;
* have Persons or Organizations as holders&lt;br /&gt;
* belong to multiple ResearchChapters&lt;br /&gt;
* be tagged with Keywords&lt;br /&gt;
* be associated with one or more Places&lt;br /&gt;
&lt;br /&gt;
Purpose: HeritageObjects are the primary conceptual anchors of the research.&lt;br /&gt;
&lt;br /&gt;
Implementation: &lt;br /&gt;
- automatic numbering of object entity: OB-00123&lt;br /&gt;
- Onject is recursive&lt;br /&gt;
&lt;br /&gt;
ToDo&lt;br /&gt;
- implementation of a many to many relationship between Object and Asset&lt;br /&gt;
- One digital Asset is a prefered Asset only for illustration purposes of the Object in a view&lt;br /&gt;
- Notion of top&amp;quot; Asset in the asset structure is the Asset without parent to by used in the many-to-many relationship. This top asset gives also access to the full tree of assets linked to that top. That top may change in the lifetime of the asset-tree (decided by the user).&lt;br /&gt;
&lt;br /&gt;
=== Step 7: Object &amp;amp; Advanced Relationships ===&lt;br /&gt;
Type:&lt;br /&gt;
* Custom Content Entities&lt;br /&gt;
* Role-based relationships&lt;br /&gt;
* other many-to-many relationships&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Asset Lifecycle &amp;amp; Collaboration Workflow ==&lt;br /&gt;
&lt;br /&gt;
=== Roles (conceptual) ===&lt;br /&gt;
* &#039;&#039;&#039;Ingestor&#039;&#039;&#039;&lt;br /&gt;
** uploads files&lt;br /&gt;
** assigns minimal context&lt;br /&gt;
* &#039;&#039;&#039;Enricher&#039;&#039;&#039;&lt;br /&gt;
** adds metadata&lt;br /&gt;
** validates data&lt;br /&gt;
&lt;br /&gt;
One person may have multiple roles.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Lifecycle States ===&lt;br /&gt;
* Newly added&lt;br /&gt;
* In progress&lt;br /&gt;
* Reviewed&lt;br /&gt;
* Complete&lt;br /&gt;
&lt;br /&gt;
States describe maturity, not publication.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Dashboard Design Pattern ==&lt;br /&gt;
&lt;br /&gt;
For Chapter, Place, Organisation:&lt;br /&gt;
* single dashboard page&lt;br /&gt;
* table of existing records&lt;br /&gt;
* add/edit via simple forms&lt;br /&gt;
&lt;br /&gt;
Status: done&lt;br /&gt;
&lt;br /&gt;
For Assets:&lt;br /&gt;
* add asset always visible&lt;br /&gt;
* recent assets list&lt;br /&gt;
* guided asset discovery&lt;br /&gt;
* no massive tables&lt;br /&gt;
&lt;br /&gt;
Status: Done, &lt;br /&gt;
Refining realised dealing with massive amounts of assets: using a combined field filter.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Help &amp;amp; Guidance Strategy ==&lt;br /&gt;
&lt;br /&gt;
Help must be:&lt;br /&gt;
* contextual&lt;br /&gt;
* non-intrusive&lt;br /&gt;
* always available&lt;br /&gt;
&lt;br /&gt;
Mechanisms:&lt;br /&gt;
* field descriptions&lt;br /&gt;
* ℹ️ icons  (difficult in Drupal - placeholders may help here)&lt;br /&gt;
* collapsible help sections&lt;br /&gt;
* TBD?: link to MediaWiki Reasearch namespace for extended documentation and system philosophy desccription&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Automation &amp;amp; Logic ==&lt;br /&gt;
&lt;br /&gt;
Automation is handled via:&lt;br /&gt;
* Drupal configuration and extra available modules&lt;br /&gt;
* Custom modules if really necessary, and simplifying a lot the implementation&lt;br /&gt;
* take care: using drupal 11.3 - based on new (OO) concepts - needs special attention in building custom modules&lt;br /&gt;
&lt;br /&gt;
Automation is used to:&lt;br /&gt;
* generate identifiers&lt;br /&gt;
* manage asset counters&lt;br /&gt;
* enforce immutability rules&lt;br /&gt;
* reduce cognitive load&lt;br /&gt;
&lt;br /&gt;
Automation must never obscure what happens.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Design Discipline Rules ==&lt;br /&gt;
&lt;br /&gt;
* No authority entity is implemented as editorial content&lt;br /&gt;
* No field is required unless it enables a mechanism&lt;br /&gt;
* No output system becomes authoritative&lt;br /&gt;
* No UX decision is optimized for power users&lt;br /&gt;
* Stability is more important than elegance&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Usage in AI Sessions ==&lt;br /&gt;
&lt;br /&gt;
This document must be:&lt;br /&gt;
* pasted or referenced at the start of new AI-assisted sessions&lt;br /&gt;
* treated as authoritative context&lt;br /&gt;
* updated only when architectural decisions change and progress is made&lt;br /&gt;
&lt;br /&gt;
AI output must conform to this blueprint.&lt;br /&gt;
&lt;br /&gt;
== AI Session protocol ==&lt;br /&gt;
* no hurry - simple step by simple step: AI proposes, sysop executes finishing with ACK&lt;br /&gt;
* no long explanations and justifications - sysop will ask for if needed.&lt;br /&gt;
* making the requirement work is the most important step, sysop will documenent once a milestone is achieved.&lt;br /&gt;
* AI will suggest a synthesis in mediawikitext format on sysop request to feed the documentation&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Supplement — UI Theming, Accessibility &amp;amp; Visual Discipline ==&lt;br /&gt;
&lt;br /&gt;
This supplement records explicit requirements related to visual theming, accessibility, and user comfort that were clarified after initial project restart.  &lt;br /&gt;
These requirements are binding and must be respected in all future design and implementation decisions.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Separation of UI Concerns ===&lt;br /&gt;
&lt;br /&gt;
The system UI is explicitly divided into two independent domains:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Administrative / Dashboard UI&#039;&#039;&#039; (authenticated users)&lt;br /&gt;
* &#039;&#039;&#039;Public Presentation UI&#039;&#039;&#039; (anonymous visitors)&lt;br /&gt;
&lt;br /&gt;
No design, theme, or UX decision in one domain is allowed to negatively affect the other.&lt;br /&gt;
&lt;br /&gt;
Decisions: &lt;br /&gt;
* GIN skin is used for administration and user input in mandatory DARK mode.&lt;br /&gt;
* SOLO skin is used for public pages.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Administrative &amp;amp; Dashboard UI (Authenticated Users) ===&lt;br /&gt;
&lt;br /&gt;
==== Target Users ====&lt;br /&gt;
* System administrator (username: &#039;&#039;&#039;mngr&#039;&#039;&#039;)&lt;br /&gt;
* Club members and contributors (e.g. &#039;&#039;&#039;testuser&#039;&#039;&#039;)&lt;br /&gt;
* Age profile: 70+&lt;br /&gt;
* Long working sessions expected&lt;br /&gt;
&lt;br /&gt;
==== Mandatory Requirements ====&lt;br /&gt;
* &#039;&#039;&#039;Permanent dark mode&#039;&#039;&#039; is used via the GIN skin/template&lt;br /&gt;
* Preferred no custom CSS or hacks &lt;br /&gt;
&lt;br /&gt;
==== Scope ====&lt;br /&gt;
* Applies to:&lt;br /&gt;
** content ingestion&lt;br /&gt;
** asset enrichment&lt;br /&gt;
** review workflows&lt;br /&gt;
** dashboards&lt;br /&gt;
* Applies equally to administrators and non-technical contributors&lt;br /&gt;
&lt;br /&gt;
==== Design Discipline ====&lt;br /&gt;
* Accessibility and eye comfort take precedence over aesthetics&lt;br /&gt;
* The administrative UI must remain:&lt;br /&gt;
** predictable&lt;br /&gt;
** low-contrast-stress&lt;br /&gt;
** uncluttered&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Public Presentation UI (Anonymous Users) ===&lt;br /&gt;
&lt;br /&gt;
==== Audience ====&lt;br /&gt;
* General public&lt;br /&gt;
* Researchers&lt;br /&gt;
* Visitors with unknown accessibility needs&lt;br /&gt;
&lt;br /&gt;
==== Mandatory Requirements ====&lt;br /&gt;
* Public-facing theme must be:&lt;br /&gt;
** modern&lt;br /&gt;
** responsive / adaptive&lt;br /&gt;
** suitable for long-term institutional use&lt;br /&gt;
&lt;br /&gt;
DECISION: SOLO skin/template has been choosen for dealing with these requirements&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Timing &amp;amp; Implementation Rules ====&lt;br /&gt;
&lt;br /&gt;
* Public-facing theming is explicitly deferred until:&lt;br /&gt;
** asset logic is validated&lt;br /&gt;
** authoritative entities are stable&lt;br /&gt;
* Early decisions must not constrain future design exploration, but must enforce:&lt;br /&gt;
** accessibility&lt;br /&gt;
** adaptability&lt;br /&gt;
** long-term maintainability&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Stability Principle (UI) ====&lt;br /&gt;
&lt;br /&gt;
* UI comfort is a prerequisite for correct decision-making&lt;br /&gt;
* Visual strain increases error rates and architectural drift&lt;br /&gt;
* Therefore:&lt;br /&gt;
** administrative UI comfort is non-negotiable&lt;br /&gt;
** public UI adaptability is mandatory&lt;br /&gt;
&lt;br /&gt;
=== Naming conventions - Design discipline ===&lt;br /&gt;
All attributes in an entity will have the first 2 letters of the entity as prefix in the Drupal machine name.&lt;br /&gt;
Example: Chapter &lt;br /&gt;
field &amp;quot;Start Year&amp;quot; - machine name: ch_startyear  internal storage name: field_ch_startyear&lt;br /&gt;
field &amp;quot;Description&amp;quot; - ch_description -- field_ch_description&lt;br /&gt;
&lt;br /&gt;
Due to limitation in Drupal concerning the Data and Time widget, a plain text field is used for &amp;quot;day&amp;quot;..&lt;br /&gt;
A custom module checks the format DD/MM/YYY and the field is limited to 10 chars, explanation about the format including also a placemarker with the format. The machine field name should end on &amp;quot;_day&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Implementation Status - March 23th 2026) ==&lt;br /&gt;
&lt;br /&gt;
Entities implemented:&lt;br /&gt;
&lt;br /&gt;
* Chapter&lt;br /&gt;
* Place&lt;br /&gt;
* Organisation&lt;br /&gt;
* Asset (basic structure)&lt;br /&gt;
* Person&lt;br /&gt;
* Object&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All entities include:&lt;br /&gt;
* dashboards via Views&lt;br /&gt;
* edit workflow via dashboard&lt;br /&gt;
* recursive relationships where required&lt;br /&gt;
* FK implemented &lt;br /&gt;
* entity references verified&lt;br /&gt;
&lt;br /&gt;
== End of Blueprint ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Appendix - 1:  Data Model - &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// =====================================================&lt;br /&gt;
// Costasano Heritage Database Model — v5.0 &lt;br /&gt;
// =====================================================&lt;br /&gt;
&lt;br /&gt;
// ==== HERITAGE OBJECTS ====&lt;br /&gt;
&lt;br /&gt;
Table Object {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Parent string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Name string&lt;br /&gt;
  Type string&lt;br /&gt;
  Subtype string&lt;br /&gt;
  Place string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  DateFrom date&lt;br /&gt;
  DateTo date&lt;br /&gt;
  Description text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== DIGITAL ASSETS ====&lt;br /&gt;
&lt;br /&gt;
Table Asset {&lt;br /&gt;
  Caption string&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Chapter string [ref: &amp;gt; Chapter.Code]&lt;br /&gt;
  Place string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Sequence int&lt;br /&gt;
  MediaFile string [ref: &amp;gt; Files.id]&lt;br /&gt;
  Parent string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
  AssetSourceType string [ref: &amp;gt; AssetSourceType.Code]&lt;br /&gt;
  AssetType string [ref: &amp;gt; AssetType.Code]&lt;br /&gt;
  SourceReference string&lt;br /&gt;
  Description text&lt;br /&gt;
  Notes text&lt;br /&gt;
  AiProcessed boolean&lt;br /&gt;
  Citation text&lt;br /&gt;
  Permalink string&lt;br /&gt;
  Repository string&lt;br /&gt;
  Rights string&lt;br /&gt;
  IsPublishable boolean&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== EXTERNAL DRUPAL FILES ====&lt;br /&gt;
&lt;br /&gt;
Table Files {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Label string&lt;br /&gt;
  Timestamp datetime&lt;br /&gt;
  User string&lt;br /&gt;
  Comment text&lt;br /&gt;
  Size int&lt;br /&gt;
  Sha1 string&lt;br /&gt;
  Mime string&lt;br /&gt;
  Url string&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== RESEARCH ENTITIES ====&lt;br /&gt;
&lt;br /&gt;
Table Person {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  FirstName string&lt;br /&gt;
  LastName string&lt;br /&gt;
  BirthDate date&lt;br /&gt;
  DeathDate date&lt;br /&gt;
  Biography text&lt;br /&gt;
  Contact text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Organisation {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Place string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  Description text&lt;br /&gt;
  Contact text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Place {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Parent string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  Label string&lt;br /&gt;
  Type string&lt;br /&gt;
  Latitude float&lt;br /&gt;
  Longitude float&lt;br /&gt;
  Description text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Chapter {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Description string&lt;br /&gt;
  Parent string [ref: &amp;gt; Chapter.Code]&lt;br /&gt;
  StartYear integer&lt;br /&gt;
  EndYear integer&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== LOOKUP TABLES ====&lt;br /&gt;
&lt;br /&gt;
Table ObjectType {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table AssetSourceType {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Label string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table AssetType {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table AssetRole {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table PersonRole {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table OrganisationRole {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Keywords {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== RELATIONSHIP TABLES (Junctions) ====&lt;br /&gt;
&lt;br /&gt;
Table ObjectAsset {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Asset string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; AssetRole.Code]&lt;br /&gt;
  IsPreferred boolean&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table PersonAsset {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Asset string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table OrganisationAsset {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Asset string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table OjectChapter {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Chapter string [ref: &amp;gt; Chapter.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectPerson {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; PersonRole.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectOrganisation {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; OrganisationRole.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table PersonOrganisationRole {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; OrganisationRole.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectKeyword {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Keyword string [ref: &amp;gt; Keywords.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectHolder {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Appendix - 2: Modules already enabled in our Drupal 11 configuration - version 20260319 ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mngr@localhost:/var/www/drupal$ drush pm:list --status=enabled&lt;br /&gt;
  ---------------- ----------------------------------------------------------------------- --------- ---------- &lt;br /&gt;
  Package          Name                                                                    Status    Version   &lt;br /&gt;
 ---------------- ----------------------------------------------------------------------- --------- ---------- &lt;br /&gt;
  Core             Announcements (announcements_feed)                                      Enabled   11.3.5    &lt;br /&gt;
  Core             Automated Cron (automated_cron)                                         Enabled   11.3.5    &lt;br /&gt;
  Core             BigPipe (big_pipe)                                                      Enabled   11.3.5    &lt;br /&gt;
  Core             Block (block)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Block Content (block_content)                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Breakpoint (breakpoint)                                                 Enabled   11.3.5    &lt;br /&gt;
  Core             CKEditor 5 (ckeditor5)                                                  Enabled   11.3.5    &lt;br /&gt;
  Core             Comment (comment)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Configuration Manager (config)                                          Enabled   11.3.5    &lt;br /&gt;
  Core             Contact (contact)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Contextual Links (contextual)                                           Enabled   11.3.5    &lt;br /&gt;
  Field types      Datetime (datetime)                                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      Datetime Range (datetime_range)                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Database Logging (dblog)                                                Enabled   11.3.5    &lt;br /&gt;
  Core             Internal Dynamic Page Cache (dynamic_page_cache)                        Enabled   11.3.5    &lt;br /&gt;
  Core             Text Editor (editor)                                                    Enabled   11.3.5    &lt;br /&gt;
  Core             Field (field)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Field UI (field_ui)                                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      File (file)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Filter (filter)                                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Help (help)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             History (history)                                                       Enabled   11.3.5    &lt;br /&gt;
  Field types      Image (image)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Inline Form Errors (inline_form_errors)                                 Enabled   11.3.5    &lt;br /&gt;
  Core             Layout Builder (layout_builder)                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Layout Discovery (layout_discovery)                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      Link (link)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Media (media)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Media Library (media_library)                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Custom Menu Links (menu_link_content)                                   Enabled   11.3.5    &lt;br /&gt;
  Core             Menu UI (menu_ui)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             MySQL (mysql)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Node (node)                                                             Enabled   11.3.5    &lt;br /&gt;
  Field types      Options (options)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Internal Page Cache (page_cache)                                        Enabled   11.3.5    &lt;br /&gt;
  Core             Path (path)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Path alias (path_alias)                                                 Enabled   11.3.5    &lt;br /&gt;
  Core             Responsive Image (responsive_image)                                     Enabled   11.3.5    &lt;br /&gt;
  Core             Search (search)                                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Shortcut (shortcut)                                                     Enabled   11.3.5    &lt;br /&gt;
  Core             System (system)                                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Taxonomy (taxonomy)                                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      Text (text)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Toolbar (toolbar)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Update Status (update)                                                  Enabled   11.3.5    &lt;br /&gt;
  Core             User (user)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Views (views)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Views UI (views_ui)                                                     Enabled   11.3.5    &lt;br /&gt;
  Core             Workspaces (workspaces)                                                 Enabled   11.3.5    &lt;br /&gt;
  Core             Workspaces UI (workspaces_ui)                                           Enabled   11.3.5    &lt;br /&gt;
  Field types      Address (address)                                                       Enabled   2.0.4     &lt;br /&gt;
  Development      Asset Injector (asset_injector)                                         Enabled   8.x-2.21  &lt;br /&gt;
  Field types      Color Field (color_field)                                               Enabled   3.0.2     &lt;br /&gt;
  Other            Computed Field (computed_field)                                         Enabled   4.0.0     &lt;br /&gt;
  Other            Contact Formatter (contact_formatter)                                   Enabled   2.0.4     &lt;br /&gt;
  Core             Dashboard (dashboard)                                                   Enabled   2.2.0     &lt;br /&gt;
  Field types      Entity Reference Revisions (entity_reference_revisions)                 Enabled   8.x-1.14  &lt;br /&gt;
  Fields           Field Group (field_group)                                               Enabled   4.0.0     &lt;br /&gt;
  File metadata    File metadata manager (file_mdm)                                        Enabled   3.2.0     &lt;br /&gt;
  File metadata    File metadata - EXIF (file_mdm_exif)                                    Enabled   3.2.0     &lt;br /&gt;
  File metadata    File metadata - Font (file_mdm_font)                                    Enabled   3.2.0     &lt;br /&gt;
  Geocoding        Geocoder (geocoder)                                                     Enabled   8.x-4.31  &lt;br /&gt;
  Geocoding        Geocoder Address (geocoder_address)                                     Enabled   8.x-4.31  &lt;br /&gt;
  Geocoding        Geocoder Field (geocoder_field)                                         Enabled   8.x-4.31  &lt;br /&gt;
  Geocoding        Geocoder Geofield (geocoder_geofield)                                   Enabled   8.x-4.31  &lt;br /&gt;
  Geofield         Geofield (geofield)                                                     Enabled   10.3.4    &lt;br /&gt;
  Geolocation      Geolocation (geolocation)                                               Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Address (geolocation_address)                             Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Baidu Maps (geolocation_baidu)                            Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Here Maps (geolocation_here)                              Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Leaflet (geolocation_leaflet)                             Enabled   8.x-3.14  &lt;br /&gt;
  Other            Gin Toolbar (gin_toolbar)                                               Enabled   3.0.3     &lt;br /&gt;
  Media            Image Effects (image_effects)                                           Enabled   5.0.0     &lt;br /&gt;
  Media            ImageMagick (imagemagick)                                               Enabled   5.0.1     &lt;br /&gt;
  jQuery UI        jQuery UI (jquery_ui)                                                   Enabled   8.x-1.8   &lt;br /&gt;
  jQuery UI        jQuery UI Autocomplete (jquery_ui_autocomplete)                         Enabled   2.1.0     &lt;br /&gt;
  jQuery UI        jQuery UI Menu (jquery_ui_menu)                                         Enabled   2.1.0     &lt;br /&gt;
  User interface   Klaro Cookie &amp;amp; Consent Manager (klaro)                                  Enabled   3.0.8     &lt;br /&gt;
  Geofield         Leaflet (leaflet)                                                       Enabled   10.4.4    &lt;br /&gt;
  Geofield         Leaflet Markercluster (leaflet_markercluster)                           Enabled   10.4.4    &lt;br /&gt;
  Geofield         Leaflet Views (leaflet_views)                                           Enabled   10.4.4    &lt;br /&gt;
  User interface   Linkit (linkit)                                                         Enabled   7.0.13    &lt;br /&gt;
  Other            Media Library Form Element (media_library_form_element)                 Enabled   2.1.4     &lt;br /&gt;
  Paragraphs       Paragraphs Type Permissions (paragraphs_type_permissions)               Enabled   8.x-1.20  &lt;br /&gt;
  Paragraphs       Paragraphs (paragraphs)                                                 Enabled   8.x-1.20  &lt;br /&gt;
  Paragraphs       Paragraph Bundle 3D Carousel (paragraph_bundle_3d_carousel)             Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle 3D Flip Box (paragraph_bundle_3d_flip_box)             Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Accordion (paragraph_bundle_accordion)                 Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Alert (paragraph_bundle_alert)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Block (paragraph_bundle_block)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Block Content (paragraph_bundle_block_content)         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Card (paragraph_bundle_card)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Carousel (paragraph_bundle_carousel)                   Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Contact Form (paragraph_bundle_contact_form)           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Content (paragraph_bundle_content)                     Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Grid (paragraph_bundle_grid)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Hero (paragraph_bundle_hero)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Icon (paragraph_bundle_icon)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image (paragraph_bundle_image)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image Background (paragraph_bundle_image_background)   Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image Grid (Image Gallery - Lightbox)                  Enabled   1.0.16    &lt;br /&gt;
                   (paragraph_bundle_image_grid)                                                               &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image Overlay (paragraph_bundle_image_overlay)         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Layout (paragraph_bundle_layout)                       Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Modal (paragraph_bundle_modal)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Node Reference (paragraph_bundle_node_reference)       Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Parallax (paragraph_bundle_parallax)                   Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Slideshow (paragraph_bundle_slideshow)                 Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Tabs (paragraph_bundle_tabs)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraphs Bundles (paragraphs_bundles)                                 Enabled   1.0.16    &lt;br /&gt;
  Mail             SMTP Authentication Support (smtp)                                      Enabled   8.x-1.4   &lt;br /&gt;
  Solo Suite       Solo Utilities (solo_utilities)                                         Enabled   1.0.6     &lt;br /&gt;
  Other            Sophron (sophron)                                                       Enabled   3.1.0     &lt;br /&gt;
  Media            SVG Image Responsive (svg_image_responsive)                             Enabled   3.2.2     &lt;br /&gt;
  Media            SVG image (svg_image)                                                   Enabled   3.2.2     &lt;br /&gt;
  Costasano        Costasano Asset (costasano_asset)                                       Enabled             &lt;br /&gt;
  Heritage         Heritage Codes (heritage_codes)                                         Enabled   1.0.0     &lt;br /&gt;
  Heritage         Heritage Tweaks (heritage_tweaks)                                       Enabled             &lt;br /&gt;
  Core             Claro (claro)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Olivero (olivero)                                                       Enabled   11.3.5    &lt;br /&gt;
                   Gin (gin)                                                               Enabled   5.0.12    &lt;br /&gt;
  Solo             Solo (solo)                                                             Enabled   1.0.31    &lt;br /&gt;
 ---------------- ----------------------------------------------------------------------- --------- ---------- &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Drupal_Blueprint_-_Version_6.5&amp;diff=1565</id>
		<title>ICT:Drupal Blueprint - Version 6.5</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Drupal_Blueprint_-_Version_6.5&amp;diff=1565"/>
		<updated>2026-03-23T10:46:28Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Costasano Heritage Project — Drupal 11 Architecture &amp;amp; Workflow Blueprint (v6.5) =&lt;br /&gt;
&lt;br /&gt;
== Purpose of this Document ==&lt;br /&gt;
This page defines the architectural, conceptual, and workflow foundations of the Costasano Heritage Project.&lt;br /&gt;
&lt;br /&gt;
It serves as:&lt;br /&gt;
* a stable reference for human contributors&lt;br /&gt;
* a briefing document for AI-assisted work sessions&lt;br /&gt;
* a safeguard against context loss over long-term development&lt;br /&gt;
&lt;br /&gt;
All future design, implementation, and automation decisions must be compatible with the principles defined here.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
The Costasano Heritage Project is a long-term digital heritage initiative operated by members of the Costa Sano Club (70+ years old, amateur historians, non-ICT specialists, except for Martin, realising the IT-project, also amateur historian).&lt;br /&gt;
&lt;br /&gt;
The project manages:&lt;br /&gt;
* hundreds of gigabytes of documents, images, and maps&lt;br /&gt;
* collaborative historical research&lt;br /&gt;
* multi-channel dissemination (wiki, public website, exhibitions, video)&lt;br /&gt;
&lt;br /&gt;
The system is designed for &#039;&#039;&#039;decades-long durability&#039;&#039;&#039;, not short-term publication.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Core Philosophy ==&lt;br /&gt;
&lt;br /&gt;
=== Authoritative Source Principle ===&lt;br /&gt;
There is exactly ONE authoritative system for data and assets:&lt;br /&gt;
* &#039;&#039;&#039;Drupal 11.3&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
All other outputs (MediaWiki, public website, video, exhibitions) are derivatives.&lt;br /&gt;
&lt;br /&gt;
No downstream system is allowed to correct, invent, or override facts.  &lt;br /&gt;
All corrections must return to Drupal.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Asset-Centric Design ===&lt;br /&gt;
The central object of the entire system is:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Asset + File (ONE asset = ONE file)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Everything else (chapter, place, organisation) exists to:&lt;br /&gt;
* contextualize assets&lt;br /&gt;
* classify assets&lt;br /&gt;
* generate stable identifiers&lt;br /&gt;
* reuse assets across outputs&lt;br /&gt;
* chapters provide a sort of timeline&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Ingest First, Enrich Later ===&lt;br /&gt;
The system must allow:&lt;br /&gt;
* rapid bulk ingestion of files with minimal metadata&lt;br /&gt;
* gradual collaborative enrichment over time&lt;br /&gt;
* clear lifecycle states without pressure for completeness&lt;br /&gt;
&lt;br /&gt;
Completeness is a process, not a prerequisite.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Target Users &amp;amp; UX Constraints ==&lt;br /&gt;
&lt;br /&gt;
=== User Profile ===&lt;br /&gt;
* Age: 70+&lt;br /&gt;
* Background: amateur historians&lt;br /&gt;
* ICT skills: minimal&lt;br /&gt;
* Tolerance for complexity: low&lt;br /&gt;
&lt;br /&gt;
=== UX Non-Negotiables ===&lt;br /&gt;
* No technical terminology exposed (no “entity”, “taxonomy”, “foreign key”)&lt;br /&gt;
* No cluttered forms&lt;br /&gt;
* Dashboard-first and only navigation&lt;br /&gt;
* Large click targets&lt;br /&gt;
* Predictable layouts&lt;br /&gt;
* Dark mode mandatory (accessibility)&lt;br /&gt;
&lt;br /&gt;
Users must never feel they are “using Drupal”.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== System Architecture ==&lt;br /&gt;
&lt;br /&gt;
=== Infrastructure ===&lt;br /&gt;
* Reverse Proxy: Windows Server 2019 (IIS + ARR, SSL offloading)&lt;br /&gt;
* Web Server: AlmaLinux 10.1 (Apache 2.4.63, PHP 8.3.29, PHP-FPM)&lt;br /&gt;
* Database Server: AlmaLinux 10.1 (MariaDB 10.11.15)&lt;br /&gt;
* Drupal 11.3.5&lt;br /&gt;
* SELinux: Enforcing&lt;br /&gt;
* PHP-FPM: Per-site pools&lt;br /&gt;
* Filesystem: Hardened&lt;br /&gt;
&lt;br /&gt;
=== Operational Constants ===&lt;br /&gt;
* &#039;&#039;&#039;Site URL&#039;&#039;&#039;: https://research.costasano.club&lt;br /&gt;
* &#039;&#039;&#039;Drupal project root&#039;&#039;&#039;: /var/www/drupal&lt;br /&gt;
* &#039;&#039;&#039;Drupal web root&#039;&#039;&#039;: /var/www/drupal/web&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Output Channels ==&lt;br /&gt;
&lt;br /&gt;
=== 1. MediaWiki (Research &amp;amp; Writing) ===&lt;br /&gt;
Role:&lt;br /&gt;
* illustrated research pages&lt;br /&gt;
* collaborative narrative writing&lt;br /&gt;
* preparation for Wikipedia publication&lt;br /&gt;
&lt;br /&gt;
MediaWiki does NOT:&lt;br /&gt;
* store authoritative metadata&lt;br /&gt;
* manage files&lt;br /&gt;
* generate identifiers&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== 2. Public Drupal Website ===&lt;br /&gt;
Role:&lt;br /&gt;
* curated presentation of selected research&lt;br /&gt;
* timelines, maps, galleries&lt;br /&gt;
&lt;br /&gt;
Only “complete” or approved assets are shown publicly.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== 3. Offline Outputs ===&lt;br /&gt;
* video documentaries&lt;br /&gt;
* lectures&lt;br /&gt;
* exhibitions&lt;br /&gt;
&lt;br /&gt;
These reuse:&lt;br /&gt;
* the same assets&lt;br /&gt;
* the same identifiers&lt;br /&gt;
* the same metadata&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Authority Entity Implementation Strategy ==&lt;br /&gt;
&lt;br /&gt;
Authority entities are implemented progressively, from simplest to most complex.  &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Step 1: Chapter (Temporal Authority) ===&lt;br /&gt;
Type:&lt;br /&gt;
* &#039;&#039;&#039;Drupal Custom Content Entity (authority)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Meaning:&lt;br /&gt;
* historical periods / timeline segmentation&lt;br /&gt;
* not editorial or narrative content&lt;br /&gt;
&lt;br /&gt;
Characteristics:&lt;br /&gt;
* stable Code (max 6 characters) like CHxx.ab CH01, CH01a, CH01aa, CH01ab&lt;br /&gt;
* recursive (parent/child) two levels using small letters after the first four&lt;br /&gt;
* stable Code (max 6 characters)&lt;br /&gt;
* used as first mandatory component in asset identifiers&lt;br /&gt;
* no sequence field&lt;br /&gt;
&lt;br /&gt;
Dashboard pattern:&lt;br /&gt;
* table of existing records&lt;br /&gt;
* add new record&lt;br /&gt;
* click pencil in front of row to edit&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and Dashboard page operational&lt;br /&gt;
Changed decision about chapter code layout&lt;br /&gt;
ToDo (max 6 characters)&lt;br /&gt;
* Address field for geocoding&lt;br /&gt;
* coordinates auto-derived when possible&lt;br /&gt;
* coordinates never required for ingestion&lt;br /&gt;
&lt;br /&gt;
Purpose:&lt;br /&gt;
* spatial context&lt;br /&gt;
* optionally reused across assets for numbering reasoons&lt;br /&gt;
* reused for organisations&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and Dashboard page operational&lt;br /&gt;
Recently added: type list, geo link in view towards openstreetmap&lt;br /&gt;
Added view adapted to type.&lt;br /&gt;
Address to coordinates implemented using Openstreatmap providers. &lt;br /&gt;
ToDo:Completing helpfunctions&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Step 3: Organisation (Institutional Authority) ===&lt;br /&gt;
Type:&lt;br /&gt;
* &#039;&#039;&#039;Drupal Custom Content Entity (authority)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Characteristics:&lt;br /&gt;
* stable Code (max 6 characters)&lt;br /&gt;
* linked to a Place&lt;br /&gt;
* optionally reused across assets for numbering&lt;br /&gt;
&lt;br /&gt;
Purpose:&lt;br /&gt;
* institutional context&lt;br /&gt;
* archival provenance&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and Dashboard page operational&lt;br /&gt;
ToDo: adding map view based on Place coordinnates now that provider is available&lt;br /&gt;
Completing help functions&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Step 4: Asset (CORE ENTITY) ===&lt;br /&gt;
Type:&lt;br /&gt;
* &#039;&#039;&#039;Drupal Custom Content Entity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Rules:&lt;br /&gt;
* ONE asset = ONE file&lt;br /&gt;
* file is immutable once uploaded&lt;br /&gt;
* identifier is immutable once generated&lt;br /&gt;
* physical filenames are managed by Drupal&lt;br /&gt;
* original filename is preserved for user reference&lt;br /&gt;
&lt;br /&gt;
Identifier format:&lt;br /&gt;
* Chapter.Code&lt;br /&gt;
* Context.Code = Place.Code OR Organisation.Code&lt;br /&gt;
* Scoped 5-digit counter (asset sequence only)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CH03ab-OOSTEN-00007&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Asset creation is impossible before Chapter, Place, and Organisation exist.&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and View complete&lt;br /&gt;
Use of special configuration of classic Media-Library for media upload.&lt;br /&gt;
Upload of images, documents, video and audio foreseen&lt;br /&gt;
Automatic numbering for asset via a custom module&lt;br /&gt;
Creation in 2 steps: first essential fields to generate autonumbering.&lt;br /&gt;
After that, use of editing via table to fill un further fields like description and note.&lt;br /&gt;
Optional change of other fields except autocode.&lt;br /&gt;
View enhanced with search filter.&lt;br /&gt;
ToDo: complete help functions&lt;br /&gt;
Remark: Inital configuration with Code as PK (Drupal Title) has been abandoned.&lt;br /&gt;
Now Code is a classic field and the Title field is a Caption field. This is a short description of the file.&lt;br /&gt;
&lt;br /&gt;
== Next Major step: HeritageObject and Person ==&lt;br /&gt;
&lt;br /&gt;
Now that Chapter, Place, Organisation and Asset are stable it is time for a next major step.&lt;br /&gt;
HeritageObject, Drupal called Object, is the next challenge due to the fact it has a many  to many relationship with Asset,&lt;br /&gt;
is recursive and also linked to a serious number of other entities. One of these is Person, not yet prepared for. This will be the first next step.&lt;br /&gt;
&lt;br /&gt;
=== Step 5: Person implementation ===&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;Person&#039;&#039;&#039; represents a historical individual with agency.&lt;br /&gt;
&lt;br /&gt;
It answers the question:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;“Who was involved historically?”&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A person is:&lt;br /&gt;
&lt;br /&gt;
* religious sisters&lt;br /&gt;
* directors&lt;br /&gt;
* architects&lt;br /&gt;
* patients&lt;br /&gt;
* shareholders&lt;br /&gt;
* board members&lt;br /&gt;
&lt;br /&gt;
A Person may:&lt;br /&gt;
&lt;br /&gt;
* play roles in relation to HeritageObjects&lt;br /&gt;
* play roles within Organizations&lt;br /&gt;
* act as a holder of HeritageObjects&lt;br /&gt;
* be documented by DigitalAssets (portraits, letters, biographies, articles)&lt;br /&gt;
&lt;br /&gt;
Roles belong to relationships, not to the Person entity itself.&lt;br /&gt;
&lt;br /&gt;
Purpose: Persons model historical agency, responsibility, and participation.&lt;br /&gt;
&lt;br /&gt;
Implementation: &lt;br /&gt;
- automatic numbering of Person entities realized with custom module: PE-00123&lt;br /&gt;
- use for plain text for birthday and deathday due to drupal limitation in Date &amp;amp; Time widget&lt;br /&gt;
- use of custom module to verify the day format DD/MM/YYYY &lt;br /&gt;
- both modules are implemented in heritage-tweaks&lt;br /&gt;
- first view operational&lt;br /&gt;
ToDo: Nihil for time being&lt;br /&gt;
&lt;br /&gt;
=== Step 6: HeritageObject implementation ===&lt;br /&gt;
The implementation of HeritageObject abbreviated Object in Drupal&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;HeritageObject (HO)&#039;&#039;&#039; represents a historical, conceptual, or material entity that is the subject of study.&lt;br /&gt;
&lt;br /&gt;
It answers the question:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;“What is the thing we are studying?”&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A HeritageObject may represent:&lt;br /&gt;
&lt;br /&gt;
* a sanatorium&lt;br /&gt;
* a building&lt;br /&gt;
* a document or register&lt;br /&gt;
* a historically meaningful place&lt;br /&gt;
* a room, component, or architectural element&lt;br /&gt;
* a conceptual or functional unit (e.g. “medical practice”)&lt;br /&gt;
&lt;br /&gt;
HeritageObjects are &#039;&#039;&#039;recursive&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Each HeritageObject may:&lt;br /&gt;
&lt;br /&gt;
* have zero or one parent HeritageObject&lt;br /&gt;
* have zero or more child HeritageObjects&lt;br /&gt;
&lt;br /&gt;
A HeritageObject may:&lt;br /&gt;
&lt;br /&gt;
* be documented by multiple DigitalAssets&lt;br /&gt;
* designate one DigitalAsset as preferred representation&lt;br /&gt;
* be linked to Persons with roles&lt;br /&gt;
* be linked to Organizations with roles&lt;br /&gt;
* have Persons or Organizations as holders&lt;br /&gt;
* belong to multiple ResearchChapters&lt;br /&gt;
* be tagged with Keywords&lt;br /&gt;
* be associated with one or more Places&lt;br /&gt;
&lt;br /&gt;
Purpose: HeritageObjects are the primary conceptual anchors of the research.&lt;br /&gt;
&lt;br /&gt;
Implementation: &lt;br /&gt;
- automatic numbering of object entity: OB-00123&lt;br /&gt;
- Onject is recursive&lt;br /&gt;
&lt;br /&gt;
ToDO&lt;br /&gt;
- implementation of a many to many relationship between Object and Asset&lt;br /&gt;
- One digital Asset is a prefered Asset only for illustration purposes of the Object in a view&lt;br /&gt;
- Notion of top&amp;quot; Asset in the asset structure is the Asset without parent to by used in the many-to-many relationship. This top asset gives also access to the full tree of assets linked to that top. That top may change in the lifetime of the asset-tree (decided by the user).&lt;br /&gt;
&lt;br /&gt;
=== Step 7: Object &amp;amp; Advanced Relationships ===&lt;br /&gt;
Type:&lt;br /&gt;
* Custom Content Entities&lt;br /&gt;
* Role-based relationships&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Asset Lifecycle &amp;amp; Collaboration Workflow ==&lt;br /&gt;
&lt;br /&gt;
=== Roles (conceptual) ===&lt;br /&gt;
* &#039;&#039;&#039;Ingestor&#039;&#039;&#039;&lt;br /&gt;
** uploads files&lt;br /&gt;
** assigns minimal context&lt;br /&gt;
* &#039;&#039;&#039;Enricher&#039;&#039;&#039;&lt;br /&gt;
** adds metadata&lt;br /&gt;
** validates data&lt;br /&gt;
&lt;br /&gt;
One person may have multiple roles.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Lifecycle States ===&lt;br /&gt;
* Newly added&lt;br /&gt;
* In progress&lt;br /&gt;
* Reviewed&lt;br /&gt;
* Complete&lt;br /&gt;
&lt;br /&gt;
States describe maturity, not publication.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Dashboard Design Pattern ==&lt;br /&gt;
&lt;br /&gt;
For Chapter, Place, Organisation:&lt;br /&gt;
* single dashboard page&lt;br /&gt;
* table of existing records&lt;br /&gt;
* add/edit via simple forms&lt;br /&gt;
&lt;br /&gt;
Status: done&lt;br /&gt;
&lt;br /&gt;
For Assets:&lt;br /&gt;
* add asset always visible&lt;br /&gt;
* recent assets list&lt;br /&gt;
* guided asset discovery&lt;br /&gt;
* no massive tables&lt;br /&gt;
&lt;br /&gt;
Status: Done, &lt;br /&gt;
Refining realised dealing with massive amounts of assets: using a combined field filter.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Help &amp;amp; Guidance Strategy ==&lt;br /&gt;
&lt;br /&gt;
Help must be:&lt;br /&gt;
* contextual&lt;br /&gt;
* non-intrusive&lt;br /&gt;
* always available&lt;br /&gt;
&lt;br /&gt;
Mechanisms:&lt;br /&gt;
* field descriptions&lt;br /&gt;
* ℹ️ icons  (difficult in Drupal - placeholders may help here)&lt;br /&gt;
* collapsible help sections&lt;br /&gt;
* TBD?: link to MediaWiki Reasearch namespace for extended documentation and system philosophy desccription&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Automation &amp;amp; Logic ==&lt;br /&gt;
&lt;br /&gt;
Automation is handled via:&lt;br /&gt;
* Drupal configuration and extra available modules&lt;br /&gt;
* Custom modules if really necessary, and simplifying a lot the implementation&lt;br /&gt;
* take care: using drupal 11.3 - based on new (OO) concepts - needs special attention in building custom modules&lt;br /&gt;
&lt;br /&gt;
Automation is used to:&lt;br /&gt;
* generate identifiers&lt;br /&gt;
* manage asset counters&lt;br /&gt;
* enforce immutability rules&lt;br /&gt;
* reduce cognitive load&lt;br /&gt;
&lt;br /&gt;
Automation must never obscure what happens.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Design Discipline Rules ==&lt;br /&gt;
&lt;br /&gt;
* No authority entity is implemented as editorial content&lt;br /&gt;
* No field is required unless it enables a mechanism&lt;br /&gt;
* No output system becomes authoritative&lt;br /&gt;
* No UX decision is optimized for power users&lt;br /&gt;
* Stability is more important than elegance&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Usage in AI Sessions ==&lt;br /&gt;
&lt;br /&gt;
This document must be:&lt;br /&gt;
* pasted or referenced at the start of new AI-assisted sessions&lt;br /&gt;
* treated as authoritative context&lt;br /&gt;
* updated only when architectural decisions change and progress is made&lt;br /&gt;
&lt;br /&gt;
AI output must conform to this blueprint.&lt;br /&gt;
&lt;br /&gt;
== AI Session protocol ==&lt;br /&gt;
* no hurry - simple step by simple step: AI proposes, sysop executes finishing with ACK&lt;br /&gt;
* no long explanations and justifications - sysop will ask for if needed.&lt;br /&gt;
* making the requirement work is the most important step, sysop will documenent once a milestone is achieved.&lt;br /&gt;
* AI will suggest a synthesis in mediawikitext format on sysop request to feed the documentation&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Supplement — UI Theming, Accessibility &amp;amp; Visual Discipline ==&lt;br /&gt;
&lt;br /&gt;
This supplement records explicit requirements related to visual theming, accessibility, and user comfort that were clarified after initial project restart.  &lt;br /&gt;
These requirements are binding and must be respected in all future design and implementation decisions.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Separation of UI Concerns ===&lt;br /&gt;
&lt;br /&gt;
The system UI is explicitly divided into two independent domains:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Administrative / Dashboard UI&#039;&#039;&#039; (authenticated users)&lt;br /&gt;
* &#039;&#039;&#039;Public Presentation UI&#039;&#039;&#039; (anonymous visitors)&lt;br /&gt;
&lt;br /&gt;
No design, theme, or UX decision in one domain is allowed to negatively affect the other.&lt;br /&gt;
&lt;br /&gt;
Decisions: &lt;br /&gt;
* GIN skin is used for administration and user input in mandatory DARK mode.&lt;br /&gt;
* SOLO skin is used for public pages.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Administrative &amp;amp; Dashboard UI (Authenticated Users) ===&lt;br /&gt;
&lt;br /&gt;
==== Target Users ====&lt;br /&gt;
* System administrator (username: &#039;&#039;&#039;mngr&#039;&#039;&#039;)&lt;br /&gt;
* Club members and contributors (e.g. &#039;&#039;&#039;testuser&#039;&#039;&#039;)&lt;br /&gt;
* Age profile: 70+&lt;br /&gt;
* Long working sessions expected&lt;br /&gt;
&lt;br /&gt;
==== Mandatory Requirements ====&lt;br /&gt;
* &#039;&#039;&#039;Permanent dark mode&#039;&#039;&#039; is used via the GIN skin/template&lt;br /&gt;
* Preferred no custom CSS or hacks &lt;br /&gt;
&lt;br /&gt;
==== Scope ====&lt;br /&gt;
* Applies to:&lt;br /&gt;
** content ingestion&lt;br /&gt;
** asset enrichment&lt;br /&gt;
** review workflows&lt;br /&gt;
** dashboards&lt;br /&gt;
* Applies equally to administrators and non-technical contributors&lt;br /&gt;
&lt;br /&gt;
==== Design Discipline ====&lt;br /&gt;
* Accessibility and eye comfort take precedence over aesthetics&lt;br /&gt;
* The administrative UI must remain:&lt;br /&gt;
** predictable&lt;br /&gt;
** low-contrast-stress&lt;br /&gt;
** uncluttered&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Public Presentation UI (Anonymous Users) ===&lt;br /&gt;
&lt;br /&gt;
==== Audience ====&lt;br /&gt;
* General public&lt;br /&gt;
* Researchers&lt;br /&gt;
* Visitors with unknown accessibility needs&lt;br /&gt;
&lt;br /&gt;
==== Mandatory Requirements ====&lt;br /&gt;
* Public-facing theme must be:&lt;br /&gt;
** modern&lt;br /&gt;
** responsive / adaptive&lt;br /&gt;
** suitable for long-term institutional use&lt;br /&gt;
&lt;br /&gt;
DECISION: SOLO skin/template has been choosen for dealing with these requirements&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Timing &amp;amp; Implementation Rules ====&lt;br /&gt;
&lt;br /&gt;
* Public-facing theming is explicitly deferred until:&lt;br /&gt;
** asset logic is validated&lt;br /&gt;
** authoritative entities are stable&lt;br /&gt;
* Early decisions must not constrain future design exploration, but must enforce:&lt;br /&gt;
** accessibility&lt;br /&gt;
** adaptability&lt;br /&gt;
** long-term maintainability&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Stability Principle (UI) ====&lt;br /&gt;
&lt;br /&gt;
* UI comfort is a prerequisite for correct decision-making&lt;br /&gt;
* Visual strain increases error rates and architectural drift&lt;br /&gt;
* Therefore:&lt;br /&gt;
** administrative UI comfort is non-negotiable&lt;br /&gt;
** public UI adaptability is mandatory&lt;br /&gt;
&lt;br /&gt;
=== Naming conventions - Design discipline ===&lt;br /&gt;
All attributes in an entity will have the first 2 letters of the entity as prefix in the Drupal machine name.&lt;br /&gt;
Example: Chapter &lt;br /&gt;
field &amp;quot;Start Year&amp;quot; - machine name: ch_startyear  internal storage name: field_ch_startyear&lt;br /&gt;
field &amp;quot;Description&amp;quot; - ch_description -- field_ch_description&lt;br /&gt;
&lt;br /&gt;
Due to limitation in Drupal concerning the Data and Time widget, a plain text field is used for &amp;quot;day&amp;quot;..&lt;br /&gt;
A custom module checks the format DD/MM/YYY and the field is limited to 10 chars, explanation about the format including also a placemarker with the format. The machine field name should end on &amp;quot;_day&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Implementation Status - March 23th 2026) ==&lt;br /&gt;
&lt;br /&gt;
Entities implemented:&lt;br /&gt;
&lt;br /&gt;
* Chapter&lt;br /&gt;
* Place&lt;br /&gt;
* Organisation&lt;br /&gt;
* Asset (basic structure)&lt;br /&gt;
* Person&lt;br /&gt;
* Object&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All entities include:&lt;br /&gt;
* dashboards via Views&lt;br /&gt;
* edit workflow via dashboard&lt;br /&gt;
* recursive relationships where required&lt;br /&gt;
* FK implemented &lt;br /&gt;
* entity references verified&lt;br /&gt;
&lt;br /&gt;
== End of Blueprint ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Appendix - 1:  Data Model - &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// =====================================================&lt;br /&gt;
// Costasano Heritage Database Model — v5.0 &lt;br /&gt;
// =====================================================&lt;br /&gt;
&lt;br /&gt;
// ==== HERITAGE OBJECTS ====&lt;br /&gt;
&lt;br /&gt;
Table Object {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Parent string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Name string&lt;br /&gt;
  Type string&lt;br /&gt;
  Subtype string&lt;br /&gt;
  Place string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  DateFrom date&lt;br /&gt;
  DateTo date&lt;br /&gt;
  Description text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== DIGITAL ASSETS ====&lt;br /&gt;
&lt;br /&gt;
Table Asset {&lt;br /&gt;
  Caption string&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Chapter string [ref: &amp;gt; Chapter.Code]&lt;br /&gt;
  Place string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Sequence int&lt;br /&gt;
  MediaFile string [ref: &amp;gt; Files.id]&lt;br /&gt;
  Parent string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
  AssetSourceType string [ref: &amp;gt; AssetSourceType.Code]&lt;br /&gt;
  AssetType string [ref: &amp;gt; AssetType.Code]&lt;br /&gt;
  SourceReference string&lt;br /&gt;
  Description text&lt;br /&gt;
  Notes text&lt;br /&gt;
  AiProcessed boolean&lt;br /&gt;
  Citation text&lt;br /&gt;
  Permalink string&lt;br /&gt;
  Repository string&lt;br /&gt;
  Rights string&lt;br /&gt;
  IsPublishable boolean&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== EXTERNAL DRUPAL FILES ====&lt;br /&gt;
&lt;br /&gt;
Table Files {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Label string&lt;br /&gt;
  Timestamp datetime&lt;br /&gt;
  User string&lt;br /&gt;
  Comment text&lt;br /&gt;
  Size int&lt;br /&gt;
  Sha1 string&lt;br /&gt;
  Mime string&lt;br /&gt;
  Url string&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== RESEARCH ENTITIES ====&lt;br /&gt;
&lt;br /&gt;
Table Person {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  FirstName string&lt;br /&gt;
  LastName string&lt;br /&gt;
  BirthDate date&lt;br /&gt;
  DeathDate date&lt;br /&gt;
  Biography text&lt;br /&gt;
  Contact text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Organisation {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Place string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  Description text&lt;br /&gt;
  Contact text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Place {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Parent string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  Label string&lt;br /&gt;
  Type string&lt;br /&gt;
  Latitude float&lt;br /&gt;
  Longitude float&lt;br /&gt;
  Description text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Chapter {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Description string&lt;br /&gt;
  Parent string [ref: &amp;gt; Chapter.Code]&lt;br /&gt;
  StartYear integer&lt;br /&gt;
  EndYear integer&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== LOOKUP TABLES ====&lt;br /&gt;
&lt;br /&gt;
Table ObjectType {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table AssetSourceType {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Label string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table AssetType {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table AssetRole {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table PersonRole {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table OrganisationRole {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Keywords {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== RELATIONSHIP TABLES (Junctions) ====&lt;br /&gt;
&lt;br /&gt;
Table ObjectAsset {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Asset string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; AssetRole.Code]&lt;br /&gt;
  IsPreferred boolean&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table PersonAsset {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Asset string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table OrganisationAsset {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Asset string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table OjectChapter {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Chapter string [ref: &amp;gt; Chapter.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectPerson {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; PersonRole.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectOrganisation {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; OrganisationRole.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table PersonOrganisationRole {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; OrganisationRole.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectKeyword {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Keyword string [ref: &amp;gt; Keywords.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectHolder {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Appendix - 2: Modules already enabled in our Drupal 11 configuration - version 20260319 ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mngr@localhost:/var/www/drupal$ drush pm:list --status=enabled&lt;br /&gt;
  ---------------- ----------------------------------------------------------------------- --------- ---------- &lt;br /&gt;
  Package          Name                                                                    Status    Version   &lt;br /&gt;
 ---------------- ----------------------------------------------------------------------- --------- ---------- &lt;br /&gt;
  Core             Announcements (announcements_feed)                                      Enabled   11.3.5    &lt;br /&gt;
  Core             Automated Cron (automated_cron)                                         Enabled   11.3.5    &lt;br /&gt;
  Core             BigPipe (big_pipe)                                                      Enabled   11.3.5    &lt;br /&gt;
  Core             Block (block)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Block Content (block_content)                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Breakpoint (breakpoint)                                                 Enabled   11.3.5    &lt;br /&gt;
  Core             CKEditor 5 (ckeditor5)                                                  Enabled   11.3.5    &lt;br /&gt;
  Core             Comment (comment)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Configuration Manager (config)                                          Enabled   11.3.5    &lt;br /&gt;
  Core             Contact (contact)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Contextual Links (contextual)                                           Enabled   11.3.5    &lt;br /&gt;
  Field types      Datetime (datetime)                                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      Datetime Range (datetime_range)                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Database Logging (dblog)                                                Enabled   11.3.5    &lt;br /&gt;
  Core             Internal Dynamic Page Cache (dynamic_page_cache)                        Enabled   11.3.5    &lt;br /&gt;
  Core             Text Editor (editor)                                                    Enabled   11.3.5    &lt;br /&gt;
  Core             Field (field)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Field UI (field_ui)                                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      File (file)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Filter (filter)                                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Help (help)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             History (history)                                                       Enabled   11.3.5    &lt;br /&gt;
  Field types      Image (image)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Inline Form Errors (inline_form_errors)                                 Enabled   11.3.5    &lt;br /&gt;
  Core             Layout Builder (layout_builder)                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Layout Discovery (layout_discovery)                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      Link (link)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Media (media)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Media Library (media_library)                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Custom Menu Links (menu_link_content)                                   Enabled   11.3.5    &lt;br /&gt;
  Core             Menu UI (menu_ui)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             MySQL (mysql)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Node (node)                                                             Enabled   11.3.5    &lt;br /&gt;
  Field types      Options (options)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Internal Page Cache (page_cache)                                        Enabled   11.3.5    &lt;br /&gt;
  Core             Path (path)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Path alias (path_alias)                                                 Enabled   11.3.5    &lt;br /&gt;
  Core             Responsive Image (responsive_image)                                     Enabled   11.3.5    &lt;br /&gt;
  Core             Search (search)                                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Shortcut (shortcut)                                                     Enabled   11.3.5    &lt;br /&gt;
  Core             System (system)                                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Taxonomy (taxonomy)                                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      Text (text)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Toolbar (toolbar)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Update Status (update)                                                  Enabled   11.3.5    &lt;br /&gt;
  Core             User (user)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Views (views)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Views UI (views_ui)                                                     Enabled   11.3.5    &lt;br /&gt;
  Core             Workspaces (workspaces)                                                 Enabled   11.3.5    &lt;br /&gt;
  Core             Workspaces UI (workspaces_ui)                                           Enabled   11.3.5    &lt;br /&gt;
  Field types      Address (address)                                                       Enabled   2.0.4     &lt;br /&gt;
  Development      Asset Injector (asset_injector)                                         Enabled   8.x-2.21  &lt;br /&gt;
  Field types      Color Field (color_field)                                               Enabled   3.0.2     &lt;br /&gt;
  Other            Computed Field (computed_field)                                         Enabled   4.0.0     &lt;br /&gt;
  Other            Contact Formatter (contact_formatter)                                   Enabled   2.0.4     &lt;br /&gt;
  Core             Dashboard (dashboard)                                                   Enabled   2.2.0     &lt;br /&gt;
  Field types      Entity Reference Revisions (entity_reference_revisions)                 Enabled   8.x-1.14  &lt;br /&gt;
  Fields           Field Group (field_group)                                               Enabled   4.0.0     &lt;br /&gt;
  File metadata    File metadata manager (file_mdm)                                        Enabled   3.2.0     &lt;br /&gt;
  File metadata    File metadata - EXIF (file_mdm_exif)                                    Enabled   3.2.0     &lt;br /&gt;
  File metadata    File metadata - Font (file_mdm_font)                                    Enabled   3.2.0     &lt;br /&gt;
  Geocoding        Geocoder (geocoder)                                                     Enabled   8.x-4.31  &lt;br /&gt;
  Geocoding        Geocoder Address (geocoder_address)                                     Enabled   8.x-4.31  &lt;br /&gt;
  Geocoding        Geocoder Field (geocoder_field)                                         Enabled   8.x-4.31  &lt;br /&gt;
  Geocoding        Geocoder Geofield (geocoder_geofield)                                   Enabled   8.x-4.31  &lt;br /&gt;
  Geofield         Geofield (geofield)                                                     Enabled   10.3.4    &lt;br /&gt;
  Geolocation      Geolocation (geolocation)                                               Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Address (geolocation_address)                             Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Baidu Maps (geolocation_baidu)                            Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Here Maps (geolocation_here)                              Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Leaflet (geolocation_leaflet)                             Enabled   8.x-3.14  &lt;br /&gt;
  Other            Gin Toolbar (gin_toolbar)                                               Enabled   3.0.3     &lt;br /&gt;
  Media            Image Effects (image_effects)                                           Enabled   5.0.0     &lt;br /&gt;
  Media            ImageMagick (imagemagick)                                               Enabled   5.0.1     &lt;br /&gt;
  jQuery UI        jQuery UI (jquery_ui)                                                   Enabled   8.x-1.8   &lt;br /&gt;
  jQuery UI        jQuery UI Autocomplete (jquery_ui_autocomplete)                         Enabled   2.1.0     &lt;br /&gt;
  jQuery UI        jQuery UI Menu (jquery_ui_menu)                                         Enabled   2.1.0     &lt;br /&gt;
  User interface   Klaro Cookie &amp;amp; Consent Manager (klaro)                                  Enabled   3.0.8     &lt;br /&gt;
  Geofield         Leaflet (leaflet)                                                       Enabled   10.4.4    &lt;br /&gt;
  Geofield         Leaflet Markercluster (leaflet_markercluster)                           Enabled   10.4.4    &lt;br /&gt;
  Geofield         Leaflet Views (leaflet_views)                                           Enabled   10.4.4    &lt;br /&gt;
  User interface   Linkit (linkit)                                                         Enabled   7.0.13    &lt;br /&gt;
  Other            Media Library Form Element (media_library_form_element)                 Enabled   2.1.4     &lt;br /&gt;
  Paragraphs       Paragraphs Type Permissions (paragraphs_type_permissions)               Enabled   8.x-1.20  &lt;br /&gt;
  Paragraphs       Paragraphs (paragraphs)                                                 Enabled   8.x-1.20  &lt;br /&gt;
  Paragraphs       Paragraph Bundle 3D Carousel (paragraph_bundle_3d_carousel)             Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle 3D Flip Box (paragraph_bundle_3d_flip_box)             Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Accordion (paragraph_bundle_accordion)                 Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Alert (paragraph_bundle_alert)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Block (paragraph_bundle_block)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Block Content (paragraph_bundle_block_content)         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Card (paragraph_bundle_card)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Carousel (paragraph_bundle_carousel)                   Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Contact Form (paragraph_bundle_contact_form)           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Content (paragraph_bundle_content)                     Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Grid (paragraph_bundle_grid)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Hero (paragraph_bundle_hero)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Icon (paragraph_bundle_icon)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image (paragraph_bundle_image)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image Background (paragraph_bundle_image_background)   Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image Grid (Image Gallery - Lightbox)                  Enabled   1.0.16    &lt;br /&gt;
                   (paragraph_bundle_image_grid)                                                               &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image Overlay (paragraph_bundle_image_overlay)         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Layout (paragraph_bundle_layout)                       Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Modal (paragraph_bundle_modal)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Node Reference (paragraph_bundle_node_reference)       Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Parallax (paragraph_bundle_parallax)                   Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Slideshow (paragraph_bundle_slideshow)                 Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Tabs (paragraph_bundle_tabs)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraphs Bundles (paragraphs_bundles)                                 Enabled   1.0.16    &lt;br /&gt;
  Mail             SMTP Authentication Support (smtp)                                      Enabled   8.x-1.4   &lt;br /&gt;
  Solo Suite       Solo Utilities (solo_utilities)                                         Enabled   1.0.6     &lt;br /&gt;
  Other            Sophron (sophron)                                                       Enabled   3.1.0     &lt;br /&gt;
  Media            SVG Image Responsive (svg_image_responsive)                             Enabled   3.2.2     &lt;br /&gt;
  Media            SVG image (svg_image)                                                   Enabled   3.2.2     &lt;br /&gt;
  Costasano        Costasano Asset (costasano_asset)                                       Enabled             &lt;br /&gt;
  Heritage         Heritage Codes (heritage_codes)                                         Enabled   1.0.0     &lt;br /&gt;
  Heritage         Heritage Tweaks (heritage_tweaks)                                       Enabled             &lt;br /&gt;
  Core             Claro (claro)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Olivero (olivero)                                                       Enabled   11.3.5    &lt;br /&gt;
                   Gin (gin)                                                               Enabled   5.0.12    &lt;br /&gt;
  Solo             Solo (solo)                                                             Enabled   1.0.31    &lt;br /&gt;
 ---------------- ----------------------------------------------------------------------- --------- ---------- &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
	<entry>
		<id>https://mwiki.costasano.club/index.php?title=ICT:Drupal_Blueprint_-_Version_6.5&amp;diff=1564</id>
		<title>ICT:Drupal Blueprint - Version 6.5</title>
		<link rel="alternate" type="text/html" href="https://mwiki.costasano.club/index.php?title=ICT:Drupal_Blueprint_-_Version_6.5&amp;diff=1564"/>
		<updated>2026-03-23T10:25:23Z</updated>

		<summary type="html">&lt;p&gt;Mngr: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Costasano Heritage Project — Drupal 11 Architecture &amp;amp; Workflow Blueprint (v6.5) =&lt;br /&gt;
&lt;br /&gt;
== Purpose of this Document ==&lt;br /&gt;
This page defines the architectural, conceptual, and workflow foundations of the Costasano Heritage Project.&lt;br /&gt;
&lt;br /&gt;
It serves as:&lt;br /&gt;
* a stable reference for human contributors&lt;br /&gt;
* a briefing document for AI-assisted work sessions&lt;br /&gt;
* a safeguard against context loss over long-term development&lt;br /&gt;
&lt;br /&gt;
All future design, implementation, and automation decisions must be compatible with the principles defined here.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Project Overview ==&lt;br /&gt;
&lt;br /&gt;
The Costasano Heritage Project is a long-term digital heritage initiative operated by members of the Costa Sano Club (70+ years old, amateur historians, non-ICT specialists, except for Martin, realising the IT-project, also amateur historian).&lt;br /&gt;
&lt;br /&gt;
The project manages:&lt;br /&gt;
* hundreds of gigabytes of documents, images, and maps&lt;br /&gt;
* collaborative historical research&lt;br /&gt;
* multi-channel dissemination (wiki, public website, exhibitions, video)&lt;br /&gt;
&lt;br /&gt;
The system is designed for &#039;&#039;&#039;decades-long durability&#039;&#039;&#039;, not short-term publication.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Core Philosophy ==&lt;br /&gt;
&lt;br /&gt;
=== Authoritative Source Principle ===&lt;br /&gt;
There is exactly ONE authoritative system for data and assets:&lt;br /&gt;
* &#039;&#039;&#039;Drupal 11.3&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
All other outputs (MediaWiki, public website, video, exhibitions) are derivatives.&lt;br /&gt;
&lt;br /&gt;
No downstream system is allowed to correct, invent, or override facts.  &lt;br /&gt;
All corrections must return to Drupal.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Asset-Centric Design ===&lt;br /&gt;
The central object of the entire system is:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Asset + File (ONE asset = ONE file)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Everything else (chapter, place, organisation) exists to:&lt;br /&gt;
* contextualize assets&lt;br /&gt;
* classify assets&lt;br /&gt;
* generate stable identifiers&lt;br /&gt;
* reuse assets across outputs&lt;br /&gt;
* chapters provide a sort of timeline&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Ingest First, Enrich Later ===&lt;br /&gt;
The system must allow:&lt;br /&gt;
* rapid bulk ingestion of files with minimal metadata&lt;br /&gt;
* gradual collaborative enrichment over time&lt;br /&gt;
* clear lifecycle states without pressure for completeness&lt;br /&gt;
&lt;br /&gt;
Completeness is a process, not a prerequisite.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Target Users &amp;amp; UX Constraints ==&lt;br /&gt;
&lt;br /&gt;
=== User Profile ===&lt;br /&gt;
* Age: 70+&lt;br /&gt;
* Background: amateur historians&lt;br /&gt;
* ICT skills: minimal&lt;br /&gt;
* Tolerance for complexity: low&lt;br /&gt;
&lt;br /&gt;
=== UX Non-Negotiables ===&lt;br /&gt;
* No technical terminology exposed (no “entity”, “taxonomy”, “foreign key”)&lt;br /&gt;
* No cluttered forms&lt;br /&gt;
* Dashboard-first and only navigation&lt;br /&gt;
* Large click targets&lt;br /&gt;
* Predictable layouts&lt;br /&gt;
* Dark mode mandatory (accessibility)&lt;br /&gt;
&lt;br /&gt;
Users must never feel they are “using Drupal”.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== System Architecture ==&lt;br /&gt;
&lt;br /&gt;
=== Infrastructure ===&lt;br /&gt;
* Reverse Proxy: Windows Server (IIS + ARR, SSL offloading)&lt;br /&gt;
* Web Server: AlmaLinux 10.1 (Apache 2.4.63, PHP 8.3.29, PHP-FPM)&lt;br /&gt;
* Database Server: AlmaLinux 10.1 (MariaDB 10.11.15)&lt;br /&gt;
* Drupal 11.3.5&lt;br /&gt;
* SELinux: Enforcing&lt;br /&gt;
* PHP-FPM: Per-site pools&lt;br /&gt;
* Filesystem: Hardened&lt;br /&gt;
&lt;br /&gt;
=== Operational Constants ===&lt;br /&gt;
* &#039;&#039;&#039;Site URL&#039;&#039;&#039;: https://research.costasano.club&lt;br /&gt;
* &#039;&#039;&#039;Drupal project root&#039;&#039;&#039;: /var/www/drupal&lt;br /&gt;
* &#039;&#039;&#039;Drupal web root&#039;&#039;&#039;: /var/www/drupal/web&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Output Channels ==&lt;br /&gt;
&lt;br /&gt;
=== 1. MediaWiki (Research &amp;amp; Writing) ===&lt;br /&gt;
Role:&lt;br /&gt;
* illustrated research pages&lt;br /&gt;
* collaborative narrative writing&lt;br /&gt;
* preparation for Wikipedia publication&lt;br /&gt;
&lt;br /&gt;
MediaWiki does NOT:&lt;br /&gt;
* store authoritative metadata&lt;br /&gt;
* manage files&lt;br /&gt;
* generate identifiers&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== 2. Public Drupal Website ===&lt;br /&gt;
Role:&lt;br /&gt;
* curated presentation of selected research&lt;br /&gt;
* timelines, maps, galleries&lt;br /&gt;
&lt;br /&gt;
Only “complete” or approved assets are shown publicly.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== 3. Offline Outputs ===&lt;br /&gt;
* video documentaries&lt;br /&gt;
* lectures&lt;br /&gt;
* exhibitions&lt;br /&gt;
&lt;br /&gt;
These reuse:&lt;br /&gt;
* the same assets&lt;br /&gt;
* the same identifiers&lt;br /&gt;
* the same metadata&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Authority Entity Implementation Strategy ==&lt;br /&gt;
&lt;br /&gt;
Authority entities are implemented progressively, from simplest to most complex.  &lt;br /&gt;
This order is mandatory.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Step 1: Chapter (Temporal Authority) ===&lt;br /&gt;
Type:&lt;br /&gt;
* &#039;&#039;&#039;Drupal Custom Content Entity (authority)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Meaning:&lt;br /&gt;
* historical periods / timeline segmentation&lt;br /&gt;
* not editorial or narrative content&lt;br /&gt;
&lt;br /&gt;
Characteristics:&lt;br /&gt;
* stable Code (max 6 characters) like CHxx.ab CH01, CH01a, CH01aa, CH01ab&lt;br /&gt;
* recursive (parent/child) two levels using small letters after the first four&lt;br /&gt;
* stable Code (max 6 characters)&lt;br /&gt;
* used as first mandatory component in asset identifiers&lt;br /&gt;
* no sequence field&lt;br /&gt;
&lt;br /&gt;
Dashboard pattern:&lt;br /&gt;
* table of existing records&lt;br /&gt;
* add new record&lt;br /&gt;
* click pencil in front of row to edit&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and Dashboard page operational&lt;br /&gt;
Changed decision about chapter code layout&lt;br /&gt;
ToDo (max 6 characters)&lt;br /&gt;
* Address field for geocoding&lt;br /&gt;
* coordinates auto-derived when possible&lt;br /&gt;
* coordinates never required for ingestion&lt;br /&gt;
&lt;br /&gt;
Purpose:&lt;br /&gt;
* spatial context&lt;br /&gt;
* optionally reused across assets for numbering reasoons&lt;br /&gt;
* reused for organisations&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and Dashboard page operational&lt;br /&gt;
Recently added: type list, geo link in view towards openstreetmap&lt;br /&gt;
Added view adapted to type.&lt;br /&gt;
Address to coordinates implemented using Openstreatmap providers. &lt;br /&gt;
ToDo:Completing helpfunctions&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Step 3: Organisation (Institutional Authority) ===&lt;br /&gt;
Type:&lt;br /&gt;
* &#039;&#039;&#039;Drupal Custom Content Entity (authority)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Characteristics:&lt;br /&gt;
* stable Code (max 6 characters)&lt;br /&gt;
* linked to a Place&lt;br /&gt;
* optionally reused across assets for numbering&lt;br /&gt;
&lt;br /&gt;
Purpose:&lt;br /&gt;
* institutional context&lt;br /&gt;
* archival provenance&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and Dashboard page operational&lt;br /&gt;
ToDo: adding map view based on Place coordinnates now that provider is available&lt;br /&gt;
Completing help functions&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Step 4: Asset (CORE ENTITY) ===&lt;br /&gt;
Type:&lt;br /&gt;
* &#039;&#039;&#039;Drupal Custom Content Entity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Rules:&lt;br /&gt;
* ONE asset = ONE file&lt;br /&gt;
* file is immutable once uploaded&lt;br /&gt;
* identifier is immutable once generated&lt;br /&gt;
* physical filenames are managed by Drupal&lt;br /&gt;
* original filename is preserved for user reference&lt;br /&gt;
&lt;br /&gt;
Identifier format:&lt;br /&gt;
* Chapter.Code&lt;br /&gt;
* Context.Code = Place.Code OR Organisation.Code&lt;br /&gt;
* Scoped 5-digit counter (asset sequence only)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CH03ab-OOSTEN-00007&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Asset creation is impossible before Chapter, Place, and Organisation exist.&lt;br /&gt;
&lt;br /&gt;
Status: Drupal Context Type and View complete&lt;br /&gt;
Use of special configuration of classic Media-Library for media upload.&lt;br /&gt;
Upload of images, documents, video and audio foreseen&lt;br /&gt;
Automatic numbering for asset via a custom module&lt;br /&gt;
Creation in 2 steps: first essential fields to generate autonumbering.&lt;br /&gt;
After that, use of editing via table to fill un further fields like description and note.&lt;br /&gt;
Optional change of other fields except autocode.&lt;br /&gt;
View enhanced with search filter.&lt;br /&gt;
ToDo: complete help functions&lt;br /&gt;
Remark: Inital configuration with Code as PK (Drupal Title) has been abandoned.&lt;br /&gt;
Now Code is a classic field and the Title field is a Caption field. This is a short description of the file.&lt;br /&gt;
&lt;br /&gt;
== Next Major step: HeritageObject and Person ==&lt;br /&gt;
&lt;br /&gt;
Now that Chapter, Place, Organisation and Asset are stable it is time for a next major step.&lt;br /&gt;
HeritageObject, Drupal called Object, is the next challenge due to the fact it has a many  to many relationship with Asset,&lt;br /&gt;
is recursive and also linked to a serious number of other entities. One of these is Person, not yet prepared for. This will be the first next step.&lt;br /&gt;
&lt;br /&gt;
=== Step 5: Person implementation ===&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;Person&#039;&#039;&#039; represents a historical individual with agency.&lt;br /&gt;
&lt;br /&gt;
It answers the question:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;“Who was involved historically?”&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A person is:&lt;br /&gt;
&lt;br /&gt;
* religious sisters&lt;br /&gt;
* directors&lt;br /&gt;
* architects&lt;br /&gt;
* patients&lt;br /&gt;
* shareholders&lt;br /&gt;
* board members&lt;br /&gt;
&lt;br /&gt;
A Person may:&lt;br /&gt;
&lt;br /&gt;
* play roles in relation to HeritageObjects&lt;br /&gt;
* play roles within Organizations&lt;br /&gt;
* act as a holder of HeritageObjects&lt;br /&gt;
* be documented by DigitalAssets (portraits, letters, biographies, articles)&lt;br /&gt;
&lt;br /&gt;
Roles belong to relationships, not to the Person entity itself.&lt;br /&gt;
&lt;br /&gt;
Purpose: Persons model historical agency, responsibility, and participation.&lt;br /&gt;
&lt;br /&gt;
Implementation: &lt;br /&gt;
- automatic numbering of Person entities realized with custom module: PE-00123&lt;br /&gt;
- use for plain text for birthday and deathday due to drupal limitation in Date &amp;amp; Time widget&lt;br /&gt;
- use of custom module to verify the day format DD/MM/YYYY &lt;br /&gt;
- both modules are implemented in heritage-tweaks&lt;br /&gt;
- first view operational&lt;br /&gt;
ToDo: Nihil for time being&lt;br /&gt;
&lt;br /&gt;
=== Step 6: HeritageObject implementation ===&lt;br /&gt;
The implementation of HeritageObject abbreviated Object in Drupal&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;HeritageObject (HO)&#039;&#039;&#039; represents a historical, conceptual, or material entity that is the subject of study.&lt;br /&gt;
&lt;br /&gt;
It answers the question:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;“What is the thing we are studying?”&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A HeritageObject may represent:&lt;br /&gt;
&lt;br /&gt;
* a sanatorium&lt;br /&gt;
* a building&lt;br /&gt;
* a document or register&lt;br /&gt;
* a historically meaningful place&lt;br /&gt;
* a room, component, or architectural element&lt;br /&gt;
* a conceptual or functional unit (e.g. “medical practice”)&lt;br /&gt;
&lt;br /&gt;
HeritageObjects are &#039;&#039;&#039;recursive&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Each HeritageObject may:&lt;br /&gt;
&lt;br /&gt;
* have zero or one parent HeritageObject&lt;br /&gt;
* have zero or more child HeritageObjects&lt;br /&gt;
&lt;br /&gt;
A HeritageObject may:&lt;br /&gt;
&lt;br /&gt;
* be documented by multiple DigitalAssets&lt;br /&gt;
* designate one DigitalAsset as preferred representation&lt;br /&gt;
* be linked to Persons with roles&lt;br /&gt;
* be linked to Organizations with roles&lt;br /&gt;
* have Persons or Organizations as holders&lt;br /&gt;
* belong to multiple ResearchChapters&lt;br /&gt;
* be tagged with Keywords&lt;br /&gt;
* be associated with one or more Places&lt;br /&gt;
&lt;br /&gt;
Purpose: HeritageObjects are the primary conceptual anchors of the research.&lt;br /&gt;
&lt;br /&gt;
Implementation: &lt;br /&gt;
- automatic numbering of object entity: OB-00123&lt;br /&gt;
- Onject is recursive&lt;br /&gt;
&lt;br /&gt;
ToDO&lt;br /&gt;
- implementation of a many to many relationship between Object and Asset&lt;br /&gt;
- One digital Asset is a prefered Asset only for illustration purposes of the Object in a view&lt;br /&gt;
- Notion of top&amp;quot; Asset in the asset structure is the Asset without parent to by used in the many-to-many relationship. This top asset gives also access to the full tree of assets linked to that top. That top may change in the lifetime of the asset-tree (decided by the user).&lt;br /&gt;
&lt;br /&gt;
=== Step 7: Object &amp;amp; Advanced Relationships ===&lt;br /&gt;
Type:&lt;br /&gt;
* Custom Content Entities&lt;br /&gt;
* Role-based relationships&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Asset Lifecycle &amp;amp; Collaboration Workflow ==&lt;br /&gt;
&lt;br /&gt;
=== Roles (conceptual) ===&lt;br /&gt;
* &#039;&#039;&#039;Ingestor&#039;&#039;&#039;&lt;br /&gt;
** uploads files&lt;br /&gt;
** assigns minimal context&lt;br /&gt;
* &#039;&#039;&#039;Enricher&#039;&#039;&#039;&lt;br /&gt;
** adds metadata&lt;br /&gt;
** validates data&lt;br /&gt;
&lt;br /&gt;
One person may have multiple roles.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Lifecycle States ===&lt;br /&gt;
* Newly added&lt;br /&gt;
* In progress&lt;br /&gt;
* Reviewed&lt;br /&gt;
* Complete&lt;br /&gt;
&lt;br /&gt;
States describe maturity, not publication.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Dashboard Design Pattern ==&lt;br /&gt;
&lt;br /&gt;
For Chapter, Place, Organisation:&lt;br /&gt;
* single dashboard page&lt;br /&gt;
* table of existing records&lt;br /&gt;
* add/edit via simple forms&lt;br /&gt;
&lt;br /&gt;
Status: done&lt;br /&gt;
&lt;br /&gt;
For Assets:&lt;br /&gt;
* add asset always visible&lt;br /&gt;
* recent assets list&lt;br /&gt;
* guided asset discovery&lt;br /&gt;
* no massive tables&lt;br /&gt;
&lt;br /&gt;
Status: Done, but some refining necessary to deal with massive amounts of assets.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Help &amp;amp; Guidance Strategy ==&lt;br /&gt;
&lt;br /&gt;
Help must be:&lt;br /&gt;
* contextual&lt;br /&gt;
* non-intrusive&lt;br /&gt;
* always available&lt;br /&gt;
&lt;br /&gt;
Mechanisms:&lt;br /&gt;
* field descriptions&lt;br /&gt;
* ℹ️ icons&lt;br /&gt;
* collapsible help sections&lt;br /&gt;
&lt;br /&gt;
No separate manuals.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Automation &amp;amp; Logic ==&lt;br /&gt;
&lt;br /&gt;
Automation is handled via:&lt;br /&gt;
* Drupal configuration&lt;br /&gt;
* Custom module&lt;br /&gt;
&lt;br /&gt;
Status: DONE but some refinements might be needed&lt;br /&gt;
&lt;br /&gt;
Automation is used to:&lt;br /&gt;
* generate identifiers&lt;br /&gt;
* manage asset counters&lt;br /&gt;
* enforce immutability rules&lt;br /&gt;
* reduce cognitive load&lt;br /&gt;
&lt;br /&gt;
Automation must never obscure what happens.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Design Discipline Rules ==&lt;br /&gt;
&lt;br /&gt;
* No authority entity is implemented as editorial content&lt;br /&gt;
* No field is required unless it enables a mechanism&lt;br /&gt;
* No output system becomes authoritative&lt;br /&gt;
* No UX decision is optimized for power users&lt;br /&gt;
* Stability is more important than elegance&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Usage in AI Sessions ==&lt;br /&gt;
&lt;br /&gt;
This document must be:&lt;br /&gt;
* pasted or referenced at the start of new AI-assisted sessions&lt;br /&gt;
* treated as authoritative context&lt;br /&gt;
* updated only when architectural decisions change and progress is made&lt;br /&gt;
&lt;br /&gt;
AI output must conform to this blueprint.&lt;br /&gt;
&lt;br /&gt;
== AI Session protocol ==&lt;br /&gt;
* no hurry - simple step by simple step: AI proposes, sysop executes finishing with ACK&lt;br /&gt;
* no long explanations and justifications - sysop will ask for if needed.&lt;br /&gt;
* making the requirement work is the most important step, sysop will documenent once a milestone is achieved.&lt;br /&gt;
* AI will suggest a synthesis in mediawikitext format on sysop request to feed the documentation&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Supplement — UI Theming, Accessibility &amp;amp; Visual Discipline ==&lt;br /&gt;
&lt;br /&gt;
This supplement records explicit requirements related to visual theming, accessibility, and user comfort that were clarified after initial project restart.  &lt;br /&gt;
These requirements are binding and must be respected in all future design and implementation decisions.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Separation of UI Concerns ===&lt;br /&gt;
&lt;br /&gt;
The system UI is explicitly divided into two independent domains:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Administrative / Dashboard UI&#039;&#039;&#039; (authenticated users)&lt;br /&gt;
* &#039;&#039;&#039;Public Presentation UI&#039;&#039;&#039; (anonymous visitors)&lt;br /&gt;
&lt;br /&gt;
No design, theme, or UX decision in one domain is allowed to negatively affect the other.&lt;br /&gt;
&lt;br /&gt;
Decisions: &lt;br /&gt;
* GIN skin is used for administration and user input in mandatory DARK mode.&lt;br /&gt;
] SOLO skin is used for public pages.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Administrative &amp;amp; Dashboard UI (Authenticated Users) ===&lt;br /&gt;
&lt;br /&gt;
==== Target Users ====&lt;br /&gt;
* System administrator (username: &#039;&#039;&#039;mngr&#039;&#039;&#039;)&lt;br /&gt;
* Club members and contributors (e.g. &#039;&#039;&#039;testuser&#039;&#039;&#039;)&lt;br /&gt;
* Age profile: 70+&lt;br /&gt;
* Long working sessions expected&lt;br /&gt;
&lt;br /&gt;
==== Mandatory Requirements ====&lt;br /&gt;
* &#039;&#039;&#039;Permanent dark mode&#039;&#039;&#039; is used via the GIN skin/template&lt;br /&gt;
* No custom CSS or hacks&lt;br /&gt;
&lt;br /&gt;
==== Scope ====&lt;br /&gt;
* Applies to:&lt;br /&gt;
** content ingestion&lt;br /&gt;
** asset enrichment&lt;br /&gt;
** review workflows&lt;br /&gt;
** dashboards&lt;br /&gt;
* Applies equally to administrators and non-technical contributors&lt;br /&gt;
&lt;br /&gt;
==== Design Discipline ====&lt;br /&gt;
* Accessibility and eye comfort take precedence over aesthetics&lt;br /&gt;
* The administrative UI must remain:&lt;br /&gt;
** predictable&lt;br /&gt;
** low-contrast-stress&lt;br /&gt;
** uncluttered&lt;br /&gt;
* No experimental or unstable admin themes are permitted&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Public Presentation UI (Anonymous Users) ===&lt;br /&gt;
&lt;br /&gt;
==== Audience ====&lt;br /&gt;
* General public&lt;br /&gt;
* Researchers&lt;br /&gt;
* Visitors with unknown accessibility needs&lt;br /&gt;
&lt;br /&gt;
==== Mandatory Requirements ====&lt;br /&gt;
* Public-facing theme must be:&lt;br /&gt;
** modern&lt;br /&gt;
** responsive / adaptive&lt;br /&gt;
** suitable for long-term institutional use&lt;br /&gt;
&lt;br /&gt;
DECISION: SOLO skin/template has been choosen for dealing with these requirements&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Timing &amp;amp; Implementation Rules ====&lt;br /&gt;
&lt;br /&gt;
* Public-facing theming is explicitly deferred until:&lt;br /&gt;
** asset logic is validated&lt;br /&gt;
** authoritative entities are stable&lt;br /&gt;
* Early decisions must not constrain future design exploration, but must enforce:&lt;br /&gt;
** accessibility&lt;br /&gt;
** adaptability&lt;br /&gt;
** long-term maintainability&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Stability Principle (UI) ====&lt;br /&gt;
&lt;br /&gt;
* UI comfort is a prerequisite for correct decision-making&lt;br /&gt;
* Visual strain increases error rates and architectural drift&lt;br /&gt;
* Therefore:&lt;br /&gt;
** administrative UI comfort is non-negotiable&lt;br /&gt;
** public UI adaptability is mandatory&lt;br /&gt;
&lt;br /&gt;
=== Naming conventions - Design discipline ===&lt;br /&gt;
All attributes in an entity will have the first 2 letters of the entity as prefix in the Drupal machine name.&lt;br /&gt;
Example: Chapter &lt;br /&gt;
field &amp;quot;Start Year&amp;quot; - machine name: ch_startyear  internal storage name: field_ch_startyear&lt;br /&gt;
field &amp;quot;Description&amp;quot; - ch_description -- field_ch_description&lt;br /&gt;
&lt;br /&gt;
== Implementation Status - March 7th 2026) ==&lt;br /&gt;
&lt;br /&gt;
Entities implemented:&lt;br /&gt;
&lt;br /&gt;
* Chapter&lt;br /&gt;
* Place&lt;br /&gt;
* Organisation&lt;br /&gt;
* Asset (basic structure)&lt;br /&gt;
&lt;br /&gt;
All entities include:&lt;br /&gt;
* dashboards via Views&lt;br /&gt;
* edit workflow via dashboard&lt;br /&gt;
* recursive relationships where required&lt;br /&gt;
* FK implemented where required&lt;br /&gt;
* entity references verified&lt;br /&gt;
&lt;br /&gt;
Automation for DigitalAsset identifiers is not yet implemented.&lt;br /&gt;
&lt;br /&gt;
== End of Blueprint ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Appendix - 1:  Data Model - &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// =====================================================&lt;br /&gt;
// Costasano Heritage Database Model — v5.0 &lt;br /&gt;
// =====================================================&lt;br /&gt;
&lt;br /&gt;
// ==== HERITAGE OBJECTS ====&lt;br /&gt;
&lt;br /&gt;
Table Object {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Parent string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Name string&lt;br /&gt;
  Type string&lt;br /&gt;
  Subtype string&lt;br /&gt;
  Place string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  DateFrom date&lt;br /&gt;
  DateTo date&lt;br /&gt;
  Description text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== DIGITAL ASSETS ====&lt;br /&gt;
&lt;br /&gt;
Table Asset {&lt;br /&gt;
  Caption string&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Chapter string [ref: &amp;gt; Chapter.Code]&lt;br /&gt;
  Place string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Sequence int&lt;br /&gt;
  MediaFile string [ref: &amp;gt; Files.id]&lt;br /&gt;
  Parent string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
  AssetSourceType string [ref: &amp;gt; AssetSourceType.Code]&lt;br /&gt;
  AssetType string [ref: &amp;gt; AssetType.Code]&lt;br /&gt;
  SourceReference string&lt;br /&gt;
  Description text&lt;br /&gt;
  Notes text&lt;br /&gt;
  AiProcessed boolean&lt;br /&gt;
  Citation text&lt;br /&gt;
  Permalink string&lt;br /&gt;
  Repository string&lt;br /&gt;
  Rights string&lt;br /&gt;
  IsPublishable boolean&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== EXTERNAL DRUPAL FILES ====&lt;br /&gt;
&lt;br /&gt;
Table Files {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Label string&lt;br /&gt;
  Timestamp datetime&lt;br /&gt;
  User string&lt;br /&gt;
  Comment text&lt;br /&gt;
  Size int&lt;br /&gt;
  Sha1 string&lt;br /&gt;
  Mime string&lt;br /&gt;
  Url string&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== RESEARCH ENTITIES ====&lt;br /&gt;
&lt;br /&gt;
Table Person {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  FirstName string&lt;br /&gt;
  LastName string&lt;br /&gt;
  BirthDate date&lt;br /&gt;
  DeathDate date&lt;br /&gt;
  Biography text&lt;br /&gt;
  Contact text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Organisation {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Place string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  Description text&lt;br /&gt;
  Contact text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Place {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Parent string [ref: &amp;gt; Place.Code]&lt;br /&gt;
  Label string&lt;br /&gt;
  Type string&lt;br /&gt;
  Latitude float&lt;br /&gt;
  Longitude float&lt;br /&gt;
  Description text&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Chapter {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Description string&lt;br /&gt;
  Parent string [ref: &amp;gt; Chapter.Code]&lt;br /&gt;
  StartYear integer&lt;br /&gt;
  EndYear integer&lt;br /&gt;
  Notes text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== LOOKUP TABLES ====&lt;br /&gt;
&lt;br /&gt;
Table ObjectType {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table AssetSourceType {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Label string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table AssetType {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table AssetRole {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table PersonRole {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table OrganisationRole {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table Keywords {&lt;br /&gt;
  Code string [pk]&lt;br /&gt;
  Name string&lt;br /&gt;
  Description text&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ==== RELATIONSHIP TABLES (Junctions) ====&lt;br /&gt;
&lt;br /&gt;
Table ObjectAsset {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Asset string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; AssetRole.Code]&lt;br /&gt;
  IsPreferred boolean&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table PersonAsset {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Asset string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table OrganisationAsset {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Asset string [ref: &amp;gt; Asset.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table OjectChapter {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Chapter string [ref: &amp;gt; Chapter.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectPerson {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; PersonRole.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectOrganisation {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; OrganisationRole.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table PersonOrganisationRole {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
  Role string [ref: &amp;gt; OrganisationRole.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectKeyword {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Keyword string [ref: &amp;gt; Keywords.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Table ObjectHolder {&lt;br /&gt;
  id string [pk]&lt;br /&gt;
  Object string [ref: &amp;gt; Object.Code]&lt;br /&gt;
  Person string [ref: &amp;gt; Person.Code]&lt;br /&gt;
  Organisation string [ref: &amp;gt; Organisation.Code]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
 &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Appendix - 2: Lessons learned on feb 27 2026 =&lt;br /&gt;
&lt;br /&gt;
This session ended in with Drupal in an undetermined stated due to a misunderstanding about the requirements, and then finally a problem wrongly using a Drupal reserved word DASHBOARD.&lt;br /&gt;
&lt;br /&gt;
Restart from scratch after that day, restoring of the full virtual disks for both machines dated 26 feb.&lt;br /&gt;
&lt;br /&gt;
Conclusion of the day were chatgpt promises for restarting.&lt;br /&gt;
&lt;br /&gt;
Tomorrow I will:&lt;br /&gt;
&lt;br /&gt;
* avoid cleverness&lt;br /&gt;
* avoid custom routing early&lt;br /&gt;
* avoid reserved names&lt;br /&gt;
* rely on Drupal defaults first&lt;br /&gt;
* reduce bash steps to the minimum stop earlier when entropy appears&lt;br /&gt;
&lt;br /&gt;
Agreement: You bring architectural clarity. I bring Drupal-specific caution. That’s a workable division.&lt;br /&gt;
&lt;br /&gt;
== Important AI protocal ==&lt;br /&gt;
After the agreement an a workflow, we work step by step, no hurry. AI proposes a step, I execute and report back the result and then only we go for the next step. Too much explanation is NOT needed, as I am a novice in this field. After 50+ years in ICT, I understand mostly what the steps do and if not, I will ask for. This should permit for a slow but steady flow towards the endgoal of the session.&lt;br /&gt;
&lt;br /&gt;
= Appendix - 4: Modules already enabled in our Drupal 11 configuration - version 20260319 ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mngr@localhost:/var/www/drupal$ drush pm:list --status=enabled&lt;br /&gt;
  ---------------- ----------------------------------------------------------------------- --------- ---------- &lt;br /&gt;
  Package          Name                                                                    Status    Version   &lt;br /&gt;
 ---------------- ----------------------------------------------------------------------- --------- ---------- &lt;br /&gt;
  Core             Announcements (announcements_feed)                                      Enabled   11.3.5    &lt;br /&gt;
  Core             Automated Cron (automated_cron)                                         Enabled   11.3.5    &lt;br /&gt;
  Core             BigPipe (big_pipe)                                                      Enabled   11.3.5    &lt;br /&gt;
  Core             Block (block)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Block Content (block_content)                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Breakpoint (breakpoint)                                                 Enabled   11.3.5    &lt;br /&gt;
  Core             CKEditor 5 (ckeditor5)                                                  Enabled   11.3.5    &lt;br /&gt;
  Core             Comment (comment)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Configuration Manager (config)                                          Enabled   11.3.5    &lt;br /&gt;
  Core             Contact (contact)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Contextual Links (contextual)                                           Enabled   11.3.5    &lt;br /&gt;
  Field types      Datetime (datetime)                                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      Datetime Range (datetime_range)                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Database Logging (dblog)                                                Enabled   11.3.5    &lt;br /&gt;
  Core             Internal Dynamic Page Cache (dynamic_page_cache)                        Enabled   11.3.5    &lt;br /&gt;
  Core             Text Editor (editor)                                                    Enabled   11.3.5    &lt;br /&gt;
  Core             Field (field)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Field UI (field_ui)                                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      File (file)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Filter (filter)                                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Help (help)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             History (history)                                                       Enabled   11.3.5    &lt;br /&gt;
  Field types      Image (image)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Inline Form Errors (inline_form_errors)                                 Enabled   11.3.5    &lt;br /&gt;
  Core             Layout Builder (layout_builder)                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Layout Discovery (layout_discovery)                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      Link (link)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Media (media)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Media Library (media_library)                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Custom Menu Links (menu_link_content)                                   Enabled   11.3.5    &lt;br /&gt;
  Core             Menu UI (menu_ui)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             MySQL (mysql)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Node (node)                                                             Enabled   11.3.5    &lt;br /&gt;
  Field types      Options (options)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Internal Page Cache (page_cache)                                        Enabled   11.3.5    &lt;br /&gt;
  Core             Path (path)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Path alias (path_alias)                                                 Enabled   11.3.5    &lt;br /&gt;
  Core             Responsive Image (responsive_image)                                     Enabled   11.3.5    &lt;br /&gt;
  Core             Search (search)                                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Shortcut (shortcut)                                                     Enabled   11.3.5    &lt;br /&gt;
  Core             System (system)                                                         Enabled   11.3.5    &lt;br /&gt;
  Core             Taxonomy (taxonomy)                                                     Enabled   11.3.5    &lt;br /&gt;
  Field types      Text (text)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Toolbar (toolbar)                                                       Enabled   11.3.5    &lt;br /&gt;
  Core             Update Status (update)                                                  Enabled   11.3.5    &lt;br /&gt;
  Core             User (user)                                                             Enabled   11.3.5    &lt;br /&gt;
  Core             Views (views)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Views UI (views_ui)                                                     Enabled   11.3.5    &lt;br /&gt;
  Core             Workspaces (workspaces)                                                 Enabled   11.3.5    &lt;br /&gt;
  Core             Workspaces UI (workspaces_ui)                                           Enabled   11.3.5    &lt;br /&gt;
  Field types      Address (address)                                                       Enabled   2.0.4     &lt;br /&gt;
  Development      Asset Injector (asset_injector)                                         Enabled   8.x-2.21  &lt;br /&gt;
  Field types      Color Field (color_field)                                               Enabled   3.0.2     &lt;br /&gt;
  Other            Computed Field (computed_field)                                         Enabled   4.0.0     &lt;br /&gt;
  Other            Contact Formatter (contact_formatter)                                   Enabled   2.0.4     &lt;br /&gt;
  Core             Dashboard (dashboard)                                                   Enabled   2.2.0     &lt;br /&gt;
  Field types      Entity Reference Revisions (entity_reference_revisions)                 Enabled   8.x-1.14  &lt;br /&gt;
  Fields           Field Group (field_group)                                               Enabled   4.0.0     &lt;br /&gt;
  File metadata    File metadata manager (file_mdm)                                        Enabled   3.2.0     &lt;br /&gt;
  File metadata    File metadata - EXIF (file_mdm_exif)                                    Enabled   3.2.0     &lt;br /&gt;
  File metadata    File metadata - Font (file_mdm_font)                                    Enabled   3.2.0     &lt;br /&gt;
  Geocoding        Geocoder (geocoder)                                                     Enabled   8.x-4.31  &lt;br /&gt;
  Geocoding        Geocoder Address (geocoder_address)                                     Enabled   8.x-4.31  &lt;br /&gt;
  Geocoding        Geocoder Field (geocoder_field)                                         Enabled   8.x-4.31  &lt;br /&gt;
  Geocoding        Geocoder Geofield (geocoder_geofield)                                   Enabled   8.x-4.31  &lt;br /&gt;
  Geofield         Geofield (geofield)                                                     Enabled   10.3.4    &lt;br /&gt;
  Geolocation      Geolocation (geolocation)                                               Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Address (geolocation_address)                             Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Baidu Maps (geolocation_baidu)                            Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Here Maps (geolocation_here)                              Enabled   8.x-3.14  &lt;br /&gt;
  Geolocation      Geolocation - Leaflet (geolocation_leaflet)                             Enabled   8.x-3.14  &lt;br /&gt;
  Other            Gin Toolbar (gin_toolbar)                                               Enabled   3.0.3     &lt;br /&gt;
  Media            Image Effects (image_effects)                                           Enabled   5.0.0     &lt;br /&gt;
  Media            ImageMagick (imagemagick)                                               Enabled   5.0.1     &lt;br /&gt;
  jQuery UI        jQuery UI (jquery_ui)                                                   Enabled   8.x-1.8   &lt;br /&gt;
  jQuery UI        jQuery UI Autocomplete (jquery_ui_autocomplete)                         Enabled   2.1.0     &lt;br /&gt;
  jQuery UI        jQuery UI Menu (jquery_ui_menu)                                         Enabled   2.1.0     &lt;br /&gt;
  User interface   Klaro Cookie &amp;amp; Consent Manager (klaro)                                  Enabled   3.0.8     &lt;br /&gt;
  Geofield         Leaflet (leaflet)                                                       Enabled   10.4.4    &lt;br /&gt;
  Geofield         Leaflet Markercluster (leaflet_markercluster)                           Enabled   10.4.4    &lt;br /&gt;
  Geofield         Leaflet Views (leaflet_views)                                           Enabled   10.4.4    &lt;br /&gt;
  User interface   Linkit (linkit)                                                         Enabled   7.0.13    &lt;br /&gt;
  Other            Media Library Form Element (media_library_form_element)                 Enabled   2.1.4     &lt;br /&gt;
  Paragraphs       Paragraphs Type Permissions (paragraphs_type_permissions)               Enabled   8.x-1.20  &lt;br /&gt;
  Paragraphs       Paragraphs (paragraphs)                                                 Enabled   8.x-1.20  &lt;br /&gt;
  Paragraphs       Paragraph Bundle 3D Carousel (paragraph_bundle_3d_carousel)             Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle 3D Flip Box (paragraph_bundle_3d_flip_box)             Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Accordion (paragraph_bundle_accordion)                 Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Alert (paragraph_bundle_alert)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Block (paragraph_bundle_block)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Block Content (paragraph_bundle_block_content)         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Card (paragraph_bundle_card)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Carousel (paragraph_bundle_carousel)                   Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Contact Form (paragraph_bundle_contact_form)           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Content (paragraph_bundle_content)                     Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Grid (paragraph_bundle_grid)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Hero (paragraph_bundle_hero)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Icon (paragraph_bundle_icon)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image (paragraph_bundle_image)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image Background (paragraph_bundle_image_background)   Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image Grid (Image Gallery - Lightbox)                  Enabled   1.0.16    &lt;br /&gt;
                   (paragraph_bundle_image_grid)                                                               &lt;br /&gt;
  Paragraphs       Paragraph Bundle Image Overlay (paragraph_bundle_image_overlay)         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Layout (paragraph_bundle_layout)                       Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Modal (paragraph_bundle_modal)                         Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Node Reference (paragraph_bundle_node_reference)       Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Parallax (paragraph_bundle_parallax)                   Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Slideshow (paragraph_bundle_slideshow)                 Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraph Bundle Tabs (paragraph_bundle_tabs)                           Enabled   1.0.16    &lt;br /&gt;
  Paragraphs       Paragraphs Bundles (paragraphs_bundles)                                 Enabled   1.0.16    &lt;br /&gt;
  Mail             SMTP Authentication Support (smtp)                                      Enabled   8.x-1.4   &lt;br /&gt;
  Solo Suite       Solo Utilities (solo_utilities)                                         Enabled   1.0.6     &lt;br /&gt;
  Other            Sophron (sophron)                                                       Enabled   3.1.0     &lt;br /&gt;
  Media            SVG Image Responsive (svg_image_responsive)                             Enabled   3.2.2     &lt;br /&gt;
  Media            SVG image (svg_image)                                                   Enabled   3.2.2     &lt;br /&gt;
  Costasano        Costasano Asset (costasano_asset)                                       Enabled             &lt;br /&gt;
  Heritage         Heritage Codes (heritage_codes)                                         Enabled   1.0.0     &lt;br /&gt;
  Heritage         Heritage Tweaks (heritage_tweaks)                                       Enabled             &lt;br /&gt;
  Core             Claro (claro)                                                           Enabled   11.3.5    &lt;br /&gt;
  Core             Olivero (olivero)                                                       Enabled   11.3.5    &lt;br /&gt;
                   Gin (gin)                                                               Enabled   5.0.12    &lt;br /&gt;
  Solo             Solo (solo)                                                             Enabled   1.0.31    &lt;br /&gt;
 ---------------- ----------------------------------------------------------------------- --------- ---------- &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mngr</name></author>
	</entry>
</feed>