From ec75322bedbbfa84ce4c532c0a789bfc5aecaf4c Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 14:03:28 +0000 Subject: [PATCH 01/11] chore: sync updates.xml 02.20.00 from main [skip ci] --- updates.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/updates.xml b/updates.xml index 8d493ff..e4d4b6d 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ @@ -13,16 +13,16 @@ site 02.19.02-dev 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.02-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.02-dev.zip 5811cc175564744865053f5a2cf259e66016c4e41f2d87571d19b1dd5f7fd8ba dev https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md Moko Consulting https://mokoconsulting.tech - + 8.1.0 @@ -31,18 +31,18 @@ mokoonyx template site - 02.19.00 + 02.20.00 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.19.00.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.20.00.zip - 1b4907b450be64d1d23ea0da4314e542e6abf2c9e6e2085ad4b2e49079bd1d83 + c1a5c28f7a74da65b8f303bd72892e241e2ff5229a2f47dee744b3eb5e7a9163 stable https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md Moko Consulting https://mokoconsulting.tech - + 8.1.0 -- 2.52.0 From b1391c6d035c717c79b9c65ac6166cc2ed1b5123 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 14:20:22 +0000 Subject: [PATCH 02/11] chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/auto-release.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 2325032..44a2d64 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -102,13 +102,14 @@ jobs: run: | php /tmp/moko-platform-api/cli/release_publish.php \ --path . --stability rc --bump minor --branch rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --skip-update-stream - name: Summary if: always() run: | echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - echo "Branch renamed to rc, minor bump, RC + lesser stream releases built, updates.xml synced" >> $GITHUB_STEP_SUMMARY + echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── release: @@ -167,7 +168,8 @@ jobs: run: | php /tmp/moko-platform-api/cli/release_publish.php \ --path . --stability stable --bump minor --branch main \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --skip-update-stream # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - name: "Step 9: Mirror release to GitHub" -- 2.52.0 From 0cdd186eae0b04ad1414985f234f345dca3d6a77 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 09:50:11 -0500 Subject: [PATCH 03/11] fix(menu): strip Joomla-injected p-2 padding from FA icon classes Joomla's admin stores Bootstrap padding utility classes (p-0 through p-5) in menu_icon alongside the Font Awesome class. This makes icon padding too small. Strip these classes before rendering in all 12 menu override files (default, mainmenu, horizontal variants). Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/html/mod_menu/default_component.php | 2 ++ src/html/mod_menu/default_heading.php | 2 ++ src/html/mod_menu/default_separator.php | 2 ++ src/html/mod_menu/default_url.php | 2 ++ src/html/mod_menu/horizontal_component.php | 2 ++ src/html/mod_menu/horizontal_heading.php | 2 ++ src/html/mod_menu/horizontal_separator.php | 2 ++ src/html/mod_menu/horizontal_url.php | 2 ++ src/html/mod_menu/mainmenu_component.php | 2 ++ src/html/mod_menu/mainmenu_heading.php | 2 ++ src/html/mod_menu/mainmenu_separator.php | 2 ++ src/html/mod_menu/mainmenu_url.php | 2 ++ 12 files changed, 24 insertions(+) diff --git a/src/html/mod_menu/default_component.php b/src/html/mod_menu/default_component.php index 2242c7f..9568e3f 100644 --- a/src/html/mod_menu/default_component.php +++ b/src/html/mod_menu/default_component.php @@ -33,6 +33,8 @@ if ($item->anchor_rel) { $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); if ($itemParams->get('menu_text', 1)) { $linktype = '' . $item->title; } else { diff --git a/src/html/mod_menu/default_heading.php b/src/html/mod_menu/default_heading.php index eeaaf2e..60368f5 100644 --- a/src/html/mod_menu/default_heading.php +++ b/src/html/mod_menu/default_heading.php @@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: ''; $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); if ($itemParams->get('menu_text', 1)) { $linktype = '' . $item->title; } else { diff --git a/src/html/mod_menu/default_separator.php b/src/html/mod_menu/default_separator.php index 1a08dbc..7eb600c 100644 --- a/src/html/mod_menu/default_separator.php +++ b/src/html/mod_menu/default_separator.php @@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: ''; $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); if ($itemParams->get('menu_text', 1)) { $linktype = '' . $item->title; } else { diff --git a/src/html/mod_menu/default_url.php b/src/html/mod_menu/default_url.php index 55b6eab..affe564 100644 --- a/src/html/mod_menu/default_url.php +++ b/src/html/mod_menu/default_url.php @@ -33,6 +33,8 @@ if ($item->anchor_rel) { $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); if ($itemParams->get('menu_text', 1)) { $linktype = '' . $item->title; } else { diff --git a/src/html/mod_menu/horizontal_component.php b/src/html/mod_menu/horizontal_component.php index 7619390..30c76d2 100644 --- a/src/html/mod_menu/horizontal_component.php +++ b/src/html/mod_menu/horizontal_component.php @@ -33,6 +33,8 @@ if ($item->anchor_rel) { $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); if ($itemParams->get('menu_text', 1)) { $linktype = '' . $item->title; } else { diff --git a/src/html/mod_menu/horizontal_heading.php b/src/html/mod_menu/horizontal_heading.php index 2bc20ef..fe559a9 100644 --- a/src/html/mod_menu/horizontal_heading.php +++ b/src/html/mod_menu/horizontal_heading.php @@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: ''; $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); if ($itemParams->get('menu_text', 1)) { $linktype = '' . $item->title; } else { diff --git a/src/html/mod_menu/horizontal_separator.php b/src/html/mod_menu/horizontal_separator.php index d7625b6..ed469f1 100644 --- a/src/html/mod_menu/horizontal_separator.php +++ b/src/html/mod_menu/horizontal_separator.php @@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: ''; $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); if ($itemParams->get('menu_text', 1)) { $linktype = '' . $item->title; } else { diff --git a/src/html/mod_menu/horizontal_url.php b/src/html/mod_menu/horizontal_url.php index 2ed318d..caa2cc4 100644 --- a/src/html/mod_menu/horizontal_url.php +++ b/src/html/mod_menu/horizontal_url.php @@ -33,6 +33,8 @@ if ($item->anchor_rel) { $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); if ($itemParams->get('menu_text', 1)) { $linktype = '' . $item->title; } else { diff --git a/src/html/mod_menu/mainmenu_component.php b/src/html/mod_menu/mainmenu_component.php index d2887cb..6f0cc07 100644 --- a/src/html/mod_menu/mainmenu_component.php +++ b/src/html/mod_menu/mainmenu_component.php @@ -33,6 +33,8 @@ if ($item->anchor_rel) { $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); // The link is an icon if ($itemParams->get('menu_text', 1)) { // If the link text is to be displayed, the icon is added with aria-hidden diff --git a/src/html/mod_menu/mainmenu_heading.php b/src/html/mod_menu/mainmenu_heading.php index af61674..b1c2b50 100644 --- a/src/html/mod_menu/mainmenu_heading.php +++ b/src/html/mod_menu/mainmenu_heading.php @@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: ''; $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); // The link is an icon if ($itemParams->get('menu_text', 1)) { // If the link text is to be displayed, the icon is added with aria-hidden diff --git a/src/html/mod_menu/mainmenu_separator.php b/src/html/mod_menu/mainmenu_separator.php index 42579b9..59d0c58 100644 --- a/src/html/mod_menu/mainmenu_separator.php +++ b/src/html/mod_menu/mainmenu_separator.php @@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: ''; $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); // The link is an icon if ($itemParams->get('menu_text', 1)) { // If the link text is to be displayed, the icon is added with aria-hidden diff --git a/src/html/mod_menu/mainmenu_url.php b/src/html/mod_menu/mainmenu_url.php index 1d81553..e537255 100644 --- a/src/html/mod_menu/mainmenu_url.php +++ b/src/html/mod_menu/mainmenu_url.php @@ -33,6 +33,8 @@ if ($item->anchor_rel) { $linktype = $item->title; if ($item->menu_icon) { + // Strip Joomla-injected padding classes that conflict with FA icon sizing + $item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon)); // The link is an icon if ($itemParams->get('menu_text', 1)) { // If the link text is to be displayed, the icon is added with aria-hidden -- 2.52.0 From 5a9e8d86a11de442a0a155b965eaea4a65de0b65 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 14:50:30 +0000 Subject: [PATCH 04/11] chore(version): auto-bump 02.19.03-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 4 ++-- SECURITY.md | 2 +- src/html/layouts/joomla/module/card.php | 2 +- src/html/layouts/mokoonyx/article-metadata.php | 2 +- src/media/css/a11y-high-contrast.css | 2 +- src/templateDetails.xml | 2 +- updates.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 8891b59..be72701 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -9,7 +9,7 @@ Template - MokoOnyx MokoConsulting MokoOnyx - Joomla site template (successor to MokoCassiopeia) - 02.19.02 + 02.19.03 GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index f450d94..0839499 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 02.19.02 +# VERSION: 02.19.03 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 6226b5b..2600f8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,11 @@ DEFGROUP: Joomla.Template.Site INGROUP: MokoOnyx.Documentation PATH: ./CHANGELOG.md - VERSION: 02.19.02 + VERSION: 02.19.03 BRIEF: Changelog file documenting version history of MokoOnyx --> -# Changelog — MokoOnyx (VERSION: 02.19.02) +# Changelog — MokoOnyx (VERSION: 02.19.03) ## [Unreleased] ## [02.19.00] --- 2026-06-04 diff --git a/SECURITY.md b/SECURITY.md index d707c57..5fae7f7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,7 +10,7 @@ INGROUP: MokoOnyx.Governance REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx FILE: SECURITY.md - VERSION: 02.19.02 + VERSION: 02.19.03 BRIEF: Security policy and vulnerability reporting process for MokoOnyx. PATH: /SECURITY.md NOTE: This policy is process oriented and does not replace secure engineering practices. diff --git a/src/html/layouts/joomla/module/card.php b/src/html/layouts/joomla/module/card.php index 807b96b..566ab1a 100644 --- a/src/html/layouts/joomla/module/card.php +++ b/src/html/layouts/joomla/module/card.php @@ -10,7 +10,7 @@ * INGROUP: MokoOnyx * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx * PATH: /html/layouts/joomla/module/card.php - * VERSION: 02.19.02 + * VERSION: 02.19.03 * BRIEF: Custom card module chrome — renders module titles for all modules */ diff --git a/src/html/layouts/mokoonyx/article-metadata.php b/src/html/layouts/mokoonyx/article-metadata.php index 2e21eee..d95431f 100644 --- a/src/html/layouts/mokoonyx/article-metadata.php +++ b/src/html/layouts/mokoonyx/article-metadata.php @@ -11,7 +11,7 @@ * INGROUP: MokoOnyx.Layouts * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx * PATH: /src/html/layouts/mokoonyx/article-metadata.php - * VERSION: 02.19.02 + * VERSION: 02.19.03 * BRIEF: Article metadata footer layout -- renders jcfields grouped by field group */ diff --git a/src/media/css/a11y-high-contrast.css b/src/media/css/a11y-high-contrast.css index a7a5503..81e0b96 100644 --- a/src/media/css/a11y-high-contrast.css +++ b/src/media/css/a11y-high-contrast.css @@ -10,7 +10,7 @@ * INGROUP: MokoOnyx.Accessibility * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx * PATH: ./media/css/a11y-high-contrast.css - * VERSION: 02.19.02 + * VERSION: 02.19.03 * BRIEF: High-contrast stylesheet for accessibility toolbar */ diff --git a/src/templateDetails.xml b/src/templateDetails.xml index 02d4690..bcd11a5 100644 --- a/src/templateDetails.xml +++ b/src/templateDetails.xml @@ -36,7 +36,7 @@ mokoonyx - 02.19.02-dev + 02.19.03-dev script.php 2026-05-16 Jonathan Miller || Moko Consulting diff --git a/updates.xml b/updates.xml index e4d4b6d..4f90b38 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ -- 2.52.0 From 0cc2fea1bc670ccff39b775fdfcd226a0c7dcd9e Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 14:50:32 +0000 Subject: [PATCH 05/11] chore: update development channel 02.19.03-dev [skip ci] --- updates.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/updates.xml b/updates.xml index 4f90b38..44b0594 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ @@ -11,18 +11,18 @@ mokoonyx template site - 02.19.02-dev + 02.19.03-dev 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.02-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.03-dev.zip - 5811cc175564744865053f5a2cf259e66016c4e41f2d87571d19b1dd5f7fd8ba + 08e1a2533860d0e0fd8e8d100d76ff33fcb4dd59627765490d091d17667aa555 dev https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md Moko Consulting https://mokoconsulting.tech - + 8.1.0 @@ -33,16 +33,16 @@ site 02.20.00 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.20.00.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.20.00.zip c1a5c28f7a74da65b8f303bd72892e241e2ff5229a2f47dee744b3eb5e7a9163 stable https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md Moko Consulting https://mokoconsulting.tech - + 8.1.0 -- 2.52.0 From 0e4b0d55cb49355f42ffdd07b27f815dc1d68444 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:13:25 +0000 Subject: [PATCH 06/11] chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] --- .mokogitea/workflows/pr-check.yml | 108 ++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index e2c82ef..9d0cb35 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -105,6 +105,19 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Check for merge conflict markers + run: | + CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) + if [ -n "$CONFLICTS" ]; then + echo "::error::Merge conflict markers found in source files" + echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "No conflict markers found" + - name: Detect platform id: platform run: | @@ -183,6 +196,101 @@ jobs: ;; esac + - name: Validate Joomla language files + if: steps.platform.outputs.platform == 'joomla' + run: | + ERRORS=0 + WARNINGS=0 + + # Find all .ini language files + INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null) + if [ -z "$INI_FILES" ]; then + echo "No .ini language files found — skipping" + exit 0 + fi + + echo "Found $(echo "$INI_FILES" | wc -l) language file(s)" + + for FILE in $INI_FILES; do + FNAME=$(basename "$FILE") + LINENUM=0 + SEEN_KEYS="" + + while IFS= read -r line || [ -n "$line" ]; do + LINENUM=$((LINENUM + 1)) + + # Skip empty lines and comments + [ -z "$line" ] && continue + echo "$line" | grep -qE '^\s*;' && continue + echo "$line" | grep -qE '^\s*$' && continue + + # Must match KEY="VALUE" format + if ! echo "$line" | grep -qE '^[A-Z_][A-Z0-9_]*=".*"$'; then + echo "::error file=${FILE},line=${LINENUM}::Malformed line: ${line}" + ERRORS=$((ERRORS + 1)) + continue + fi + + # Extract key and check for duplicates + KEY=$(echo "$line" | sed 's/=.*//') + if echo "$SEEN_KEYS" | grep -qx "$KEY"; then + echo "::error file=${FILE},line=${LINENUM}::Duplicate key: ${KEY}" + ERRORS=$((ERRORS + 1)) + fi + SEEN_KEYS="${SEEN_KEYS} + ${KEY}" + done < "$FILE" + + echo " ${FILE}: checked ${LINENUM} lines" + done + + # Cross-check en-GB vs en-US key consistency + GB_DIR=$(find . -path "*/language/en-GB" -type d -not -path "./.git/*" 2>/dev/null | head -1) + US_DIR=$(find . -path "*/language/en-US" -type d -not -path "./.git/*" 2>/dev/null | head -1) + + if [ -n "$GB_DIR" ] && [ -n "$US_DIR" ]; then + for GB_FILE in "$GB_DIR"/*.ini; do + [ ! -f "$GB_FILE" ] && continue + FNAME=$(basename "$GB_FILE") + US_FILE="$US_DIR/$FNAME" + [ ! -f "$US_FILE" ] && continue + + GB_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$GB_FILE" 2>/dev/null | sort) + US_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$US_FILE" 2>/dev/null | sort) + + # Keys in en-GB but not en-US + MISSING_US=$(comm -23 <(echo "$GB_KEYS") <(echo "$US_KEYS")) + if [ -n "$MISSING_US" ]; then + echo "::warning::Keys in en-GB/$FNAME but missing from en-US/$FNAME:" + echo "$MISSING_US" | while read -r k; do echo " - $k"; done + WARNINGS=$((WARNINGS + 1)) + fi + + # Keys in en-US but not en-GB + MISSING_GB=$(comm -13 <(echo "$GB_KEYS") <(echo "$US_KEYS")) + if [ -n "$MISSING_GB" ]; then + echo "::warning::Keys in en-US/$FNAME but missing from en-GB/$FNAME:" + echo "$MISSING_GB" | while read -r k; do echo " - $k"; done + WARNINGS=$((WARNINGS + 1)) + fi + done + fi + + { + echo "### Language File Validation" + echo "| Metric | Count |" + echo "|---|---|" + echo "| Files checked | $(echo "$INI_FILES" | wc -l) |" + echo "| Errors | ${ERRORS} |" + echo "| Warnings | ${WARNINGS} |" + } >> $GITHUB_STEP_SUMMARY + + if [ "$ERRORS" -gt 0 ]; then + echo "::error::Language validation failed with ${ERRORS} error(s)" + exit 1 + fi + echo "Language files: OK (${WARNINGS} warning(s))" + - name: Check changelog has unreleased entry run: | if [ ! -f "CHANGELOG.md" ]; then -- 2.52.0 From c6b52100de1f46a344651d73b2b12d37beea2776 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:28:55 +0000 Subject: [PATCH 07/11] chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] --- .mokogitea/workflows/pr-check.yml | 39 ++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 9d0cb35..473eeb2 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -202,10 +202,47 @@ jobs: ERRORS=0 WARNINGS=0 + # Require both en-GB and en-US language directories + LANG_ROOT=$(find . -path "*/language" -type d -not -path "./.git/*" 2>/dev/null | head -1) + if [ -z "$LANG_ROOT" ]; then + echo "No language/ directory found — skipping" + exit 0 + fi + + if [ ! -d "$LANG_ROOT/en-GB" ]; then + echo "::error::Missing en-GB language directory (${LANG_ROOT}/en-GB)" + ERRORS=$((ERRORS + 1)) + fi + if [ ! -d "$LANG_ROOT/en-US" ]; then + echo "::error::Missing en-US language directory (${LANG_ROOT}/en-US)" + ERRORS=$((ERRORS + 1)) + fi + + # Check that en-GB and en-US have matching .ini files + if [ -d "$LANG_ROOT/en-GB" ] && [ -d "$LANG_ROOT/en-US" ]; then + for GB_INI in "$LANG_ROOT/en-GB"/*.ini; do + [ ! -f "$GB_INI" ] && continue + US_INI="$LANG_ROOT/en-US/$(basename "$GB_INI")" + if [ ! -f "$US_INI" ]; then + echo "::error::$(basename "$GB_INI") exists in en-GB but missing from en-US" + ERRORS=$((ERRORS + 1)) + fi + done + for US_INI in "$LANG_ROOT/en-US"/*.ini; do + [ ! -f "$US_INI" ] && continue + GB_INI="$LANG_ROOT/en-GB/$(basename "$US_INI")" + if [ ! -f "$GB_INI" ]; then + echo "::error::$(basename "$US_INI") exists in en-US but missing from en-GB" + ERRORS=$((ERRORS + 1)) + fi + done + fi + # Find all .ini language files INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null) if [ -z "$INI_FILES" ]; then - echo "No .ini language files found — skipping" + echo "No .ini language files found" + [ "$ERRORS" -gt 0 ] && exit 1 exit 0 fi -- 2.52.0 From 6e05b6aea08abd8cece05225c56c07fe7a1e9f9f Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:38:27 +0000 Subject: [PATCH 08/11] chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] --- .mokogitea/workflows/pr-check.yml | 92 +++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 473eeb2..3dd7540 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -147,6 +147,98 @@ jobs: echo "PHP lint: ${ERRORS} error(s)" [ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; } + - name: Joomla JEXEC guard check + if: steps.platform.outputs.platform == 'joomla' + run: | + ERRORS=0 + while IFS= read -r -d '' file; do + # Skip vendor, node_modules, and index.html stub files + case "$file" in ./vendor/*|./node_modules/*) continue ;; esac + # Check first 10 lines for JEXEC or JPATH guard + if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then + echo "::error file=${file}::Missing JEXEC guard: ${file}" + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0) + if [ "$ERRORS" -gt 0 ]; then + echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard" + echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY + echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "JEXEC guard: OK" + + - name: Joomla directory listing protection + if: steps.platform.outputs.platform == 'joomla' + run: | + MISSING=0 + SOURCE_DIR="src" + [ ! -d "$SOURCE_DIR" ] && exit 0 + while IFS= read -r dir; do + if [ ! -f "${dir}/index.html" ]; then + echo "::warning::Missing index.html in ${dir} (directory listing protection)" + MISSING=$((MISSING + 1)) + fi + done < <(find "$SOURCE_DIR" -type d -not -path "./.git/*" -not -path "*/vendor/*" -not -path "*/node_modules/*") + if [ "$MISSING" -gt 0 ]; then + echo "## Directory Protection" >> $GITHUB_STEP_SUMMARY + echo "${MISSING} director(ies) missing index.html" >> $GITHUB_STEP_SUMMARY + fi + echo "Directory protection: ${MISSING} missing (advisory)" + + - name: Joomla script file and asset checks + if: steps.platform.outputs.platform == 'joomla' + run: | + ERRORS=0 + MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) + [ -z "$MANIFEST" ] && exit 0 + MANIFEST_DIR=$(dirname "$MANIFEST") + + # Check scriptfile exists if declared + SCRIPTFILE=$(sed -n 's/.*\([^<]*\)<\/scriptfile>.*/\1/p' "$MANIFEST" 2>/dev/null) + if [ -n "$SCRIPTFILE" ]; then + if [ ! -f "${MANIFEST_DIR}/${SCRIPTFILE}" ]; then + echo "::error::Manifest declares ${SCRIPTFILE} but file not found at ${MANIFEST_DIR}/${SCRIPTFILE}" + ERRORS=$((ERRORS + 1)) + else + echo "Script file: ${MANIFEST_DIR}/${SCRIPTFILE} (OK)" + fi + fi + + # Require joomla.asset.json and validate it + ASSET_JSON=$(find "$MANIFEST_DIR" -name "joomla.asset.json" -not -path "./.git/*" 2>/dev/null | head -1) + if [ -z "$ASSET_JSON" ]; then + echo "::error::joomla.asset.json not found — Joomla asset system is required" + ERRORS=$((ERRORS + 1)) + else + if command -v php &> /dev/null; then + php -r "json_decode(file_get_contents('$ASSET_JSON')); if(json_last_error()!==JSON_ERROR_NONE){echo json_last_error_msg();exit(1);}" 2>&1 || { + echo "::error::joomla.asset.json is not valid JSON" + ERRORS=$((ERRORS + 1)) + } + fi + echo "joomla.asset.json: valid" + fi + + # Validate all XML files in src/ are well-formed + XML_ERRORS=0 + if command -v php &> /dev/null; then + while IFS= read -r -d '' xmlfile; do + if ! php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$xmlfile'); if(!\$x){foreach(libxml_get_errors() as \$e) echo trim(\$e->message) . ' in $xmlfile'; exit(1);}" 2>&1; then + XML_ERRORS=$((XML_ERRORS + 1)) + fi + done < <(find "$MANIFEST_DIR" -name "*.xml" -not -path "./.git/*" -print0) + fi + if [ "$XML_ERRORS" -gt 0 ]; then + echo "::error::${XML_ERRORS} XML file(s) are malformed" + ERRORS=$((ERRORS + 1)) + else + echo "XML well-formedness: OK" + fi + + [ "$ERRORS" -gt 0 ] && exit 1 + echo "Joomla asset checks: OK" + - name: Validate platform manifest run: | PLATFORM="${{ steps.platform.outputs.platform }}" -- 2.52.0 From 9e6cebb9aaf637652cc414624b703578836f637d Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:42:19 +0000 Subject: [PATCH 09/11] chore: remove updates.xml [skip ci] --- updates.xml | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) delete mode 100644 updates.xml diff --git a/updates.xml b/updates.xml deleted file mode 100644 index 44b0594..0000000 --- a/updates.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - Template - MokoOnyx - Template - MokoOnyx development build. - mokoonyx - template - site - 02.19.03-dev - 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development - - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.03-dev.zip - - 08e1a2533860d0e0fd8e8d100d76ff33fcb4dd59627765490d091d17667aa555 - dev - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md - Moko Consulting - https://mokoconsulting.tech - - 8.1.0 - - - Template - MokoOnyx - Template - MokoOnyx stable build. - mokoonyx - template - site - 02.20.00 - 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable - - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.20.00.zip - - c1a5c28f7a74da65b8f303bd72892e241e2ff5229a2f47dee744b3eb5e7a9163 - stable - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md - Moko Consulting - https://mokoconsulting.tech - - 8.1.0 - - -- 2.52.0 From 8428f290581785b5234b842af7b319e56a0254a2 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 10:47:16 -0500 Subject: [PATCH 10/11] chore: update changelog with unreleased entries [skip bump] Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2600f8b..9409eff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,17 @@ # Changelog — MokoOnyx (VERSION: 02.19.03) ## [Unreleased] +### Fixed +- Strip Joomla-injected `p-2` padding class from Font Awesome icons in all menu overrides (default, mainmenu, horizontal) + +### Changed +- Migrated update server URL from raw file endpoint to Gitea Pages +- Release workflow no longer manages updates.xml (decoupled to Gitea Pages) +- Added conflict-marker guard to PR check and release workflows +- Added Joomla language file validation (syntax, duplicates, en-GB/en-US consistency) +- Added JEXEC guard, joomla.asset.json, XML well-formedness, and script file CI checks +- Removed RS_FTP_PATH_SUFFIX from repo health requirements + ## [02.19.00] --- 2026-06-04 ## [02.18.00] --- 2026-06-02 -- 2.52.0 From 041111b8abc10d34fe5ccb2498fa053ada87b785 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:50:47 +0000 Subject: [PATCH 11/11] chore: standardize updateservers URL [skip ci] --- src/templateDetails.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/templateDetails.xml b/src/templateDetails.xml index bcd11a5..d192160 100644 --- a/src/templateDetails.xml +++ b/src/templateDetails.xml @@ -31,9 +31,7 @@ --> - - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/updates.xml - + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/updates.xml mokoonyx 02.19.03-dev -- 2.52.0