Compare commits
242 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fcacd01af9 | |||
| 940caa3d2d | |||
| eec803edac | |||
| cf5400a047 | |||
| 436a3b1029 | |||
| 9b07bfa167 | |||
| d2139df514 | |||
| 159b71b1bd | |||
| e1b0fafd70 | |||
| 64e0aec932 | |||
| 52b1f33f3e | |||
| 9e55617f0f | |||
| 7228bb9744 | |||
| 20922e7a1c | |||
| 4579cf684b | |||
| f193d62652 | |||
| 7b0aa534ed | |||
| 416725c212 | |||
| a4e6bdd2af | |||
| b6b212e7d2 | |||
| a984b03e07 | |||
| 6f92653ed7 | |||
| 53da1d7457 | |||
| 6ec75f2b5d | |||
| 07a4841dec | |||
| fff64be76d | |||
| f01d7ba140 | |||
| 0b691be220 | |||
| d9c8752081 | |||
| 9373459301 | |||
| d3494e1f36 | |||
| 978e1774aa | |||
| bbf9ef3310 | |||
| 010a467f5c | |||
| 8cb83668e1 | |||
| 01e6778664 | |||
| 2b5734207c | |||
| 33979440b4 | |||
| 983e56fc16 | |||
| 54b27702e9 | |||
| 67ee1d8282 | |||
| 2c86454989 | |||
| 0856361ebc | |||
| 802bab7777 | |||
| f4ed92c0c3 | |||
| 0ee3b181a5 | |||
| 3226a75086 | |||
| d4ac99c7d9 | |||
| e65ff7e422 | |||
| 998e2f0622 | |||
| e0a7348e2c | |||
| a47d229cbb | |||
| 693ab27fe7 | |||
| 93cf3ff81a | |||
| 694947586f | |||
| f2c1ce0764 | |||
| 20d085f91c | |||
| fc359ec5a5 | |||
| 63e76c27fd | |||
| 87853baf75 | |||
| f35ccb197a | |||
| 3e19ca7408 | |||
| 65d156e15b | |||
| d38766d0b8 | |||
| c3bb1a1295 | |||
| d6f0e1fe0c | |||
| a1ba923155 | |||
| 60b2ee2436 | |||
| 3efcba855d | |||
| 2614e6d930 | |||
| 71bf7c78b7 | |||
| 3a29a94920 | |||
| abdc861dfb | |||
| d42f23dae9 | |||
| 63613aa48c | |||
| f33e0f2701 | |||
| fae7614c29 | |||
| 5b4d5e4d42 | |||
| 0e7a022478 | |||
| da96e87969 | |||
| af3e53b734 | |||
| d9b14431e6 | |||
| e8d9755a2d | |||
| ea347ea41b | |||
| 6e28f9c89a | |||
| 785bfca673 | |||
| eef5d54774 | |||
| 1d99daf4a5 | |||
| 81eaf0d4e3 | |||
| fb376ae368 | |||
| 5bd15387c1 | |||
| 6594bb6239 | |||
| f2930c401f | |||
| f2f91f2f2c | |||
| 649b5c1f23 | |||
| 2923918e0b | |||
| 45e57510f4 | |||
| f9aa1b91ce | |||
| 92491d5559 | |||
| d9db104dd7 | |||
| 660d65aee4 | |||
| 22d8965be1 | |||
| 4d104b8dc7 | |||
| 7b4f03d1f2 | |||
| d201fa0384 | |||
| a1a722c267 | |||
| 739b9f974a | |||
| f65afd370c | |||
| 92609bbdf3 | |||
| 9bae409466 | |||
| ccd9e3c0e4 | |||
| cd780ce8a3 | |||
| 687fdb9ed7 | |||
| 20b56b9b00 | |||
| afdd7b4fe8 | |||
| e65ef8a140 | |||
| 7800ca140f | |||
| f269dfd441 | |||
| 7af5c44406 | |||
| f807f985c8 | |||
| 1d6854b4be | |||
| 147bcc8630 | |||
| c4b1983e9e | |||
| ad3d46180b | |||
| 5dfcf926be | |||
| 50ee63ef80 | |||
| 0b6a72a6ce | |||
| 113c73430c | |||
| 2426b5d85e | |||
| ec945dab73 | |||
| 6371c7240c | |||
| 2810caa810 | |||
| 39e71eb198 | |||
| bba336ff86 | |||
| 4efce6b8ef | |||
| ba8d23d862 | |||
| e4534a62f1 | |||
| 7d497dfdc1 | |||
| 3bf89ed10d | |||
| dda39e64ae | |||
| 6a457d2924 | |||
| 62f72ae215 | |||
| 350a365a84 | |||
| 17cd29c663 | |||
| b308de6bf2 | |||
| 8f7f50f359 | |||
| 06d846c38a | |||
| 302329ad77 | |||
| 09f80ccbe6 | |||
| 396a3e8115 | |||
| 0c167080f2 | |||
| cc4d1c4600 | |||
| 528e6568fc | |||
| 21325b79fd | |||
| 3efacc344f | |||
| 90b2479e63 | |||
| 54f24b364b | |||
| 8ad94c071b | |||
| d4512ae32b | |||
| 5bd1310685 | |||
| 121c4628a3 | |||
| b0776f17c4 | |||
| 972f443865 | |||
| f06adc08eb | |||
| 63851f4119 | |||
| cc581fd0f8 | |||
| 82e3049187 | |||
| 67f2ad7ab0 | |||
| cd188a46ab | |||
| 27fbc090f5 | |||
| 79ebcea9c4 | |||
| 59c29735dc | |||
| b639f72ffd | |||
| d263faeea9 | |||
| 525412e408 | |||
| 6e69768e4f | |||
| c8c114a24c | |||
| e9f13c43f6 | |||
| a5a46a56a8 | |||
| cb8ccf061d | |||
| 238e1e4fbf | |||
| 81800d4159 | |||
| 381446b8ba | |||
| dcf3c75dfb | |||
| c62a1d0a69 | |||
| e1a73cc9de | |||
| db578ad249 | |||
| 3f305d0c60 | |||
| 262c02a234 | |||
| 91a162f922 | |||
| 27a2d2b387 | |||
| 5c0c3ea9b6 | |||
| f6d4680d23 | |||
| 633c6d4b68 | |||
| 99b42e844a | |||
| c38d20d805 | |||
| ae015ee0df | |||
| b669f6c768 | |||
| 65a091c90c | |||
| cec71a88f2 | |||
| 7c5fbe1626 | |||
| 45c78e1f5f | |||
| eacfc432d7 | |||
| ee1c8a2262 | |||
| f910b99f4d | |||
| 5a32a540be | |||
| 1e9b0a5856 | |||
| 273e8b4649 | |||
| 81b26cfdd9 | |||
| b7d48d21bf | |||
| 11d8e42708 | |||
| 1b0eb2b1b9 | |||
| 4f58ec9d1d | |||
| 6d9a832a27 | |||
| ad6660f898 | |||
| bd907f55f7 | |||
| fde1fa0356 | |||
| 86e35fb5e7 | |||
| 8052fe9130 | |||
| 9adc34357c | |||
| 16fa16469e | |||
| 2495f5022a | |||
| 13e5f07951 | |||
| 5dd2656b6f | |||
| 357685e463 | |||
| 45dea64d01 | |||
| 4886090575 | |||
| c312ae2207 | |||
| 25c6778871 | |||
| 3a366e7956 | |||
| 05bcbd8169 | |||
| 939d2a5e6c | |||
| 61ba7f2cd0 | |||
| 2fa69c6a2f | |||
| 6da271b7b2 | |||
| 1092de762f | |||
| 8fe9497c85 | |||
| 61edf950e6 | |||
| cdbe8712df | |||
| 4340aa8dd6 | |||
| 0028e72e8d | |||
| 0e281bd7f9 |
@@ -1,20 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoStandards.Templates.Config
|
||||
# INGROUP: MokoStandards.Templates
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/configs/moko-standards.yml
|
||||
# VERSION: 04.01.00
|
||||
# BRIEF: Governance attachment template — synced to .moko-standards in every governed repository
|
||||
# NOTE: Tokens replaced at sync time: mokoconsulting-tech, MokoJoomTOS, waas-component, 04.00.04
|
||||
#
|
||||
# This file is managed automatically by MokoStandards bulk sync.
|
||||
# Do not edit manually — changes will be overwritten on the next sync.
|
||||
# To update governance settings, open a PR in MokoStandards instead:
|
||||
# https://github.com/mokoconsulting-tech/MokoStandards
|
||||
|
||||
standards_source: "https://github.com/mokoconsulting-tech/MokoStandards"
|
||||
standards_version: "04.00.04"
|
||||
platform: "waas-component"
|
||||
governed_repo: "mokoconsulting-tech/MokoJoomTOS"
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
name: WaaS Client Site Issue
|
||||
about: Report an issue with a WaaS client site (branding, deployment, media sync)
|
||||
title: '[WAAS] '
|
||||
labels: 'waas, client-site'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Site Issue Type
|
||||
- [ ] Branding / CSS not applying
|
||||
- [ ] Deployment failure
|
||||
- [ ] Media sync issue
|
||||
- [ ] Template override not working
|
||||
- [ ] Module positioning issue
|
||||
- [ ] Mobile / responsive layout
|
||||
- [ ] Performance issue
|
||||
|
||||
## Client Site
|
||||
- **Client Org**: [e.g., ClarksvilleFurs]
|
||||
- **Repo**: [e.g., client-waas-clarksvillefurs]
|
||||
- **Environment**: [Dev / Production]
|
||||
- **Site URL**: [dev or production URL — omit if private]
|
||||
|
||||
## Issue Description
|
||||
Describe the issue clearly.
|
||||
|
||||
## Steps to Reproduce
|
||||
1. Visit [page URL]
|
||||
2. Look at [element]
|
||||
3. See error
|
||||
|
||||
## Expected Behavior
|
||||
What the site should look like or how it should behave.
|
||||
|
||||
## Actual Behavior
|
||||
What is happening instead.
|
||||
|
||||
## Screenshots
|
||||
Attach screenshots showing the issue (desktop and mobile if relevant).
|
||||
|
||||
## Deployment Status
|
||||
- **Last deploy**: [date or "unknown"]
|
||||
- **Deploy workflow**: [succeeded / failed / not run]
|
||||
- **Branch**: [dev / main]
|
||||
|
||||
## Media Sync
|
||||
- [ ] Images missing after sync
|
||||
- [ ] Sync direction: [dev-to-prod / prod-to-dev / bidirectional]
|
||||
- [ ] Last sync: [date]
|
||||
|
||||
## Template Details
|
||||
- **Joomla Version**: [e.g., 5.x]
|
||||
- **Template Name**: [e.g., clienttemplate]
|
||||
- **MokoWaaS Plugin**: [Active / Inactive]
|
||||
- **MokoOnyx Admin**: [Active / Inactive]
|
||||
|
||||
## CSS Custom Properties
|
||||
If branding issue, list the relevant CSS variables:
|
||||
```css
|
||||
:root {
|
||||
--client-primary: #...;
|
||||
--client-secondary: #...;
|
||||
}
|
||||
```
|
||||
|
||||
## Browser / Device
|
||||
- **Browser**: [e.g., Chrome 120, Safari 17]
|
||||
- **Device**: [Desktop / Tablet / Mobile]
|
||||
- **Screen Width**: [e.g., 1920px, 768px, 375px]
|
||||
|
||||
## Checklist
|
||||
- [ ] I have cleared Joomla cache
|
||||
- [ ] I have hard-refreshed the browser (Ctrl+Shift+R)
|
||||
- [ ] I have checked the deploy workflow completed
|
||||
- [ ] I have verified the change is on the correct branch
|
||||
- [ ] No credentials or PII are included in this issue
|
||||
@@ -1,25 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
MokoStandards Repository Manifest
|
||||
Auto-generated by cleanup script.
|
||||
Moko Platform Repository Manifest
|
||||
See: https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home
|
||||
-->
|
||||
<moko-platform xmlns="https://standards.mokoconsulting.tech/moko-platform/1.0" schema-version="1.0">
|
||||
<identity>
|
||||
<name>MokoJoomTOS</name>
|
||||
<org>MokoConsulting</org>
|
||||
<description>A component to present a sites Term of Service and privacy policy even through offline.</description>
|
||||
<description>Joomla system plugin to keep legal pages accessible during offline mode</description>
|
||||
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
||||
</identity>
|
||||
<governance>
|
||||
<platform>joomla</platform>
|
||||
<standards-version>04.07.00</standards-version>
|
||||
<standards-version>05.00.00</standards-version>
|
||||
<standards-source>https://git.mokoconsulting.tech/MokoConsulting/moko-platform</standards-source>
|
||||
<last-synced>2026-05-10T19:51:05+00:00</last-synced>
|
||||
<last-synced>2026-05-16T17:30:00+00:00</last-synced>
|
||||
</governance>
|
||||
<build>
|
||||
<language>PHP</language>
|
||||
<package-type>joomla</package-type>
|
||||
<package-type>joomla-extension</package-type>
|
||||
<entry-point>src/</entry-point>
|
||||
</build>
|
||||
</moko-platform>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Release
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
||||
# INGROUP: moko-platform.Release
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||
# PATH: /templates/workflows/universal/auto-release.yml.template
|
||||
# VERSION: 05.00.00
|
||||
# BRIEF: Universal build & release � detects platform from manifest.xml
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
- name: Setup moko-platform tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
@@ -69,9 +69,9 @@ jobs:
|
||||
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
|
||||
fi
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
|
||||
/tmp/mokostandards-api
|
||||
cd /tmp/mokostandards-api
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||
/tmp/moko-platform-api
|
||||
cd /tmp/moko-platform-api
|
||||
composer install --no-dev --no-interaction --quiet
|
||||
|
||||
|
||||
@@ -79,118 +79,37 @@ jobs:
|
||||
- name: Detect platform
|
||||
id: platform
|
||||
run: |
|
||||
# Read platform from XML manifest (<platform> tag) or plain text fallback
|
||||
PLATFORM=$(sed -n 's/.*<platform>\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1)
|
||||
[ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]')
|
||||
[ -z "$PLATFORM" ] && PLATFORM="generic"
|
||||
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
|
||||
echo "Platform detected: ${PLATFORM}"
|
||||
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1)
|
||||
php /tmp/moko-platform-api/cli/manifest_read.php --path . --github-output
|
||||
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)
|
||||
MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1 || true)
|
||||
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
|
||||
echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# -- STEP 1: Read version -----------------------------------------------
|
||||
- name: "Step 1: Read version from README.md"
|
||||
- name: "Step 1: Read version"
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null)
|
||||
VERSION=$(php /tmp/moko-platform-api/cli/version_read.php --path .)
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "No VERSION in README.md — skipping release"
|
||||
echo "::error::No VERSION in README.md"
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
# Derive major.minor for branch naming (patches update existing branch)
|
||||
MINOR=$(echo "$VERSION" | awk -F. '{printf "%s.%s", $1, $2}')
|
||||
PATCH=$(echo "$VERSION" | awk -F. '{print $3}')
|
||||
|
||||
MAJOR=$(echo "$VERSION" | awk -F. '{print $1}')
|
||||
MINOR_NUM=$(echo "$VERSION" | awk -F. '{print $2}')
|
||||
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||
echo "minor=$MINOR" >> "$GITHUB_OUTPUT"
|
||||
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
|
||||
echo "stability=stable" >> "$GITHUB_OUTPUT"
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
if [ "$PATCH" = "00" ] || [ "$PATCH" = "01" ]; then
|
||||
echo "is_minor=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Version: $VERSION (first release for this minor — full pipeline)"
|
||||
else
|
||||
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
||||
echo "Version: $VERSION (patch — platform version + badges only)"
|
||||
fi
|
||||
|
||||
# -- STEP 1b: Bump minor version (stable = minor bump, reset patch) ------
|
||||
- name: "Step 1b: Bump minor version for stable release"
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
id: bump
|
||||
run: |
|
||||
CURRENT=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' README.md 2>/dev/null | head -1)
|
||||
[ -z "$CURRENT" ] && { echo "skip=true" >> "$GITHUB_OUTPUT"; exit 0; }
|
||||
|
||||
MAJOR=$((10#$(echo "$CURRENT" | cut -d. -f1)))
|
||||
MINOR=$((10#$(echo "$CURRENT" | cut -d. -f2)))
|
||||
|
||||
# Minor bump, reset patch. Rollover if minor > 99
|
||||
MINOR=$((MINOR + 1))
|
||||
if [ $MINOR -gt 99 ]; then
|
||||
MINOR=0
|
||||
MAJOR=$((MAJOR + 1))
|
||||
fi
|
||||
|
||||
VERSION=$(printf "%02d.%02d.00" $MAJOR $MINOR)
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
echo "Stable bump: ${CURRENT} → ${VERSION} (minor)"
|
||||
|
||||
# Update README.md
|
||||
sed -i "s/VERSION:[[:space:]]*${CURRENT}/VERSION: ${VERSION}/" README.md
|
||||
|
||||
# Update platform-specific manifest
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
MANIFEST="${{ steps.platform.outputs.manifest }}"
|
||||
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
|
||||
case "$PLATFORM" in
|
||||
joomla)
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
MANIFEST_VER=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1)
|
||||
[ -n "$MANIFEST_VER" ] && sed -i "s|<version>${MANIFEST_VER}</version>|<version>${VERSION}</version>|" "$MANIFEST"
|
||||
sed -i "s|<creationDate>[^<]*</creationDate>|<creationDate>${TODAY}</creationDate>|" "$MANIFEST"
|
||||
fi
|
||||
;;
|
||||
dolibarr)
|
||||
if [ -n "$MOD_FILE" ]; then
|
||||
sed -i "s/\$this->version = '[^']*'/\$this->version = '${VERSION}'/" "$MOD_FILE"
|
||||
fi
|
||||
echo "${VERSION}" > update.txt
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
# Promote [Unreleased] section in CHANGELOG.md to new version
|
||||
if [ -f "CHANGELOG.md" ] && grep -qi "Unreleased" CHANGELOG.md; then
|
||||
sed -i "s|## \[Unreleased\]|## [${VERSION}] --- ${TODAY}|" CHANGELOG.md
|
||||
sed -i "s|## Unreleased|## [${VERSION}] --- ${TODAY}|" CHANGELOG.md
|
||||
sed -i "2i ## [Unreleased]" CHANGELOG.md
|
||||
sed -i "3i \\ " CHANGELOG.md
|
||||
echo "CHANGELOG promoted to [${VERSION}]"
|
||||
fi
|
||||
|
||||
# Commit and push
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore(version): bump ${CURRENT} → ${VERSION} [skip ci]"
|
||||
git push origin HEAD:main 2>&1
|
||||
}
|
||||
|
||||
# Override version output for rest of pipeline
|
||||
MAJOR=$(echo "$VERSION" | cut -d. -f1)
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "major=$(printf "%02d" $MAJOR)" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: "Step 1b: Bump version"
|
||||
id: bump
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
run: |
|
||||
MOKO_API="/tmp/moko-platform-api/cli"
|
||||
BUMP=$(php ${MOKO_API}/version_bump.php --path . --minor)
|
||||
VERSION=$(echo "$BUMP" | grep -oP '\d{2}\.\d{2}\.\d{2}$' || true)
|
||||
[ -z "$VERSION" ] && VERSION=$(php ${MOKO_API}/version_read.php --path .)
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "Bumped to: ${VERSION}"
|
||||
|
||||
- name: Check if already released
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
@@ -335,170 +254,27 @@ jobs:
|
||||
steps.check.outputs.already_released != 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
php /tmp/mokostandards-api/cli/version_set_platform.php \
|
||||
php /tmp/moko-platform-api/cli/version_set_platform.php \
|
||||
--path . --version "$VERSION" --branch main
|
||||
|
||||
# -- STEP 4: Update version badges ----------------------------------------
|
||||
- name: "Step 4: Update version badges"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.check.outputs.already_released != 'true'
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
find . -name "*.md" ! -path "./.git/*" ! -path "./vendor/*" | while read -r f; do
|
||||
if grep -q '\[VERSION:' "$f" 2>/dev/null; then
|
||||
sed -i "s/\[VERSION:[[:space:]]*[0-9]\{2\}\.[0-9]\{2\}\.[0-9]\{2\}\]/[VERSION: ${VERSION}]/" "$f"
|
||||
fi
|
||||
done
|
||||
php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true
|
||||
|
||||
# -- STEP 5: Write updates.xml (Joomla update server) ---------------------
|
||||
- name: "Step 5: Write update stream"
|
||||
id: updates
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.check.outputs.already_released != 'true'
|
||||
steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
REPO="${{ github.repository }}"
|
||||
php /tmp/moko-platform-api/cli/updates_xml_build.php \
|
||||
--path . --version "${VERSION}" --stability stable \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
--github-output
|
||||
|
||||
# -- Parse extension metadata from XML manifest ----------------
|
||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "Warning: No Joomla XML manifest found — skipping updates.xml" >> $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract fields using sed (portable — no grep -P)
|
||||
EXT_NAME=$(sed -n 's/.*<name>\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_CLIENT=$(sed -n 's/.*<extension[^>]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
TARGET_PLATFORM=$(sed -n 's/.*\(<targetplatform[^/]*\/>\).*/\1/p' "$MANIFEST" | head -1)
|
||||
PHP_MINIMUM=$(sed -n 's/.*<php_minimum>\([^<]*\)<\/php_minimum>.*/\1/p' "$MANIFEST" | head -1)
|
||||
|
||||
# If EXT_NAME is a language key (e.g. PLG_SYSTEM_MOKOJGDPC), resolve from .ini
|
||||
if echo "$EXT_NAME" | grep -qE '^[A-Z_]+$'; then
|
||||
INI_NAME=$(find . -name "*.sys.ini" -path "*/en-GB/*" -exec grep -h "^${EXT_NAME}=" {} \; 2>/dev/null | head -1 | cut -d'"' -f2)
|
||||
[ -z "$INI_NAME" ] && INI_NAME=$(find . -name "*.sys.ini" -exec grep -h "^${EXT_NAME}=" {} \; 2>/dev/null | head -1 | cut -d'"' -f2)
|
||||
[ -n "$INI_NAME" ] && EXT_NAME="$INI_NAME"
|
||||
fi
|
||||
|
||||
# Fallbacks
|
||||
[ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
|
||||
[ -z "$EXT_TYPE" ] && EXT_TYPE="component"
|
||||
|
||||
# Derive element if not in manifest:
|
||||
# 1. plugin="xxx" attribute (plugins)
|
||||
# 2. module="xxx" attribute (modules)
|
||||
# 3. XML filename (components, packages)
|
||||
# 4. Repo name fallback (templates, anything else)
|
||||
if [ -z "$EXT_ELEMENT" ]; then
|
||||
EXT_ELEMENT=$(sed -n 's/.*plugin="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
fi
|
||||
if [ -z "$EXT_ELEMENT" ]; then
|
||||
EXT_ELEMENT=$(sed -n 's/.*module="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
fi
|
||||
if [ -z "$EXT_ELEMENT" ]; then
|
||||
FNAME=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
|
||||
# If filename is generic (templateDetails, manifest), use repo name
|
||||
case "$FNAME" in
|
||||
templatedetails|manifest) EXT_ELEMENT=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
|
||||
*) EXT_ELEMENT="$FNAME" ;;
|
||||
esac
|
||||
fi
|
||||
# Final fallback
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
|
||||
# Save for Steps 7, 8, 8b
|
||||
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
||||
echo "ext_name=${EXT_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "ext_type=${EXT_TYPE}" >> "$GITHUB_OUTPUT"
|
||||
echo "ext_folder=${EXT_FOLDER}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Build client tag: plugins and frontend modules need <client>site</client>
|
||||
CLIENT_TAG=""
|
||||
if [ -n "$EXT_CLIENT" ]; then
|
||||
CLIENT_TAG="<client>${EXT_CLIENT}</client>"
|
||||
elif [ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]; then
|
||||
CLIENT_TAG="<client>site</client>"
|
||||
fi
|
||||
|
||||
# Build folder tag for plugins (required for Joomla to match the update)
|
||||
FOLDER_TAG=""
|
||||
if [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ]; then
|
||||
FOLDER_TAG="<folder>${EXT_FOLDER}</folder>"
|
||||
fi
|
||||
|
||||
# Build targetplatform (fallback to Joomla 5 if not in manifest)
|
||||
if [ -z "$TARGET_PLATFORM" ]; then
|
||||
TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" %s>' "/")
|
||||
fi
|
||||
|
||||
# Build php_minimum tag
|
||||
PHP_TAG=""
|
||||
if [ -n "$PHP_MINIMUM" ]; then
|
||||
PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>"
|
||||
fi
|
||||
|
||||
# Build TYPE_PREFIX for download URL
|
||||
TYPE_PREFIX=""
|
||||
case "${EXT_TYPE}" in
|
||||
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
|
||||
module) TYPE_PREFIX="mod_" ;;
|
||||
component) TYPE_PREFIX="com_" ;;
|
||||
template) TYPE_PREFIX="tpl_" ;;
|
||||
library) TYPE_PREFIX="lib_" ;;
|
||||
package) TYPE_PREFIX="pkg_" ;;
|
||||
esac
|
||||
|
||||
DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/stable/${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip"
|
||||
INFO_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/stable"
|
||||
|
||||
# -- Build update entry for a given stability tag
|
||||
build_entry() {
|
||||
local TAG_NAME="$1"
|
||||
printf '%s\n' ' <update>'
|
||||
printf '%s\n' " <name>${EXT_NAME}</name>"
|
||||
printf '%s\n' " <description>${EXT_NAME} update</description>"
|
||||
printf '%s\n' " <element>${EXT_ELEMENT}</element>"
|
||||
printf '%s\n' " <type>${EXT_TYPE}</type>"
|
||||
printf '%s\n' " <version>${VERSION}</version>"
|
||||
[ -n "$CLIENT_TAG" ] && printf '%s\n' " ${CLIENT_TAG}"
|
||||
[ -n "$FOLDER_TAG" ] && printf '%s\n' " ${FOLDER_TAG}"
|
||||
printf '%s\n' " <tags><tag>${TAG_NAME}</tag></tags>"
|
||||
printf '%s\n' " <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>"
|
||||
printf '%s\n' ' <downloads>'
|
||||
printf '%s\n' " <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>"
|
||||
printf '%s\n' ' </downloads>'
|
||||
printf '%s\n' " ${TARGET_PLATFORM}"
|
||||
[ -n "$PHP_TAG" ] && printf '%s\n' " ${PHP_TAG}"
|
||||
printf '%s\n' ' <maintainer>Moko Consulting</maintainer>'
|
||||
printf '%s\n' ' <maintainerurl>https://mokoconsulting.tech</maintainerurl>'
|
||||
printf '%s\n' ' </update>'
|
||||
}
|
||||
|
||||
# -- Write updates.xml with cascading channels
|
||||
# Stable release updates ALL channels (development, alpha, beta, rc, stable)
|
||||
{
|
||||
printf '%s\n' "<?xml version='1.0' encoding='UTF-8'?>"
|
||||
printf '%s\n' "<!-- Copyright (C) $(date +%Y) Moko Consulting <hello@mokoconsulting.tech>"
|
||||
printf '%s\n' " SPDX-License-Identifier: GPL-3.0-or-later"
|
||||
printf '%s\n' " VERSION: ${VERSION}"
|
||||
printf '%s\n' " -->"
|
||||
printf '%s\n' ""
|
||||
printf '%s\n' '<updates>'
|
||||
build_entry "development"
|
||||
build_entry "alpha"
|
||||
build_entry "beta"
|
||||
build_entry "rc"
|
||||
build_entry "stable"
|
||||
printf '%s\n' '</updates>'
|
||||
} > updates.xml
|
||||
|
||||
echo "updates.xml: ${VERSION} (all channels updated to stable)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- Commit all changes ---------------------------------------------------
|
||||
- name: Commit release changes
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
@@ -559,7 +335,7 @@ jobs:
|
||||
fi
|
||||
[ -z "$EXT_NAME" ] && EXT_NAME="${GITEA_REPO}"
|
||||
|
||||
NOTES=$(php /tmp/mokostandards-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null)
|
||||
NOTES=$(php /tmp/moko-platform-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null)
|
||||
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
||||
|
||||
# Build release name: "Pretty Name VERSION (type_element-VERSION)"
|
||||
@@ -648,19 +424,18 @@ jobs:
|
||||
# -- Build install packages from src/ ----------------------------
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ — skipping package"; exit 0; }
|
||||
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/"; exit 0; }
|
||||
|
||||
EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*"
|
||||
# ZIP package (type-aware via moko-platform PHP API)
|
||||
php /tmp/moko-platform-api/cli/joomla_build.php --path . --version "${VERSION}" --output /tmp
|
||||
# Match the expected ZIP_NAME for upload
|
||||
BUILT_ZIP=$(ls /tmp/${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip 2>/dev/null | head -1 || true)
|
||||
if [ -n "$BUILT_ZIP" ] && [ "$BUILT_ZIP" != "/tmp/${ZIP_NAME}" ]; then
|
||||
mv "$BUILT_ZIP" "/tmp/${ZIP_NAME}"
|
||||
fi
|
||||
|
||||
# ZIP package
|
||||
cd "$SOURCE_DIR"
|
||||
zip -r "/tmp/${ZIP_NAME}" . -x $EXCLUDES
|
||||
cd ..
|
||||
|
||||
# tar.gz package
|
||||
tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" \
|
||||
--exclude='.ftpignore' --exclude='sftp-config*' \
|
||||
--exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
|
||||
# tar.gz package (flat source archive)
|
||||
tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" --exclude='.ftpignore' --exclude='sftp-config*' --exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
|
||||
|
||||
ZIP_SIZE=$(stat -c%s "/tmp/${ZIP_NAME}" 2>/dev/null || stat -f%z "/tmp/${ZIP_NAME}" 2>/dev/null || echo "unknown")
|
||||
TAR_SIZE=$(stat -c%s "/tmp/${TAR_NAME}" 2>/dev/null || stat -f%z "/tmp/${TAR_NAME}" 2>/dev/null || echo "unknown")
|
||||
@@ -866,7 +641,7 @@ jobs:
|
||||
BRANCH="${{ steps.version.outputs.branch }}"
|
||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||
|
||||
NOTES=$(php /tmp/mokostandards-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null || true)
|
||||
NOTES=$(php /tmp/moko-platform-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null || true)
|
||||
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
||||
echo "$NOTES" > /tmp/release_notes.md
|
||||
|
||||
@@ -913,33 +688,14 @@ jobs:
|
||||
# -- Clean up lesser pre-releases (cascade) ---------------------------------
|
||||
# stable → deletes all | rc → beta,alpha,dev | beta → alpha,dev | alpha → dev
|
||||
- name: "Delete lesser pre-release channels"
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
php /tmp/moko-platform-api/cli/release_cascade.php \
|
||||
--stability stable \
|
||||
--token "${{ secrets.GA_TOKEN }}" \
|
||||
--org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
--gitea-url "${GITEA_URL}" 2>/dev/null || true
|
||||
|
||||
# Stable deletes all pre-release channels
|
||||
TAGS_TO_DELETE="development alpha beta release-candidate"
|
||||
|
||||
DELETED=0
|
||||
for TAG in $TAGS_TO_DELETE; do
|
||||
RELEASE_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/releases/tags/${TAG}" 2>/dev/null | \
|
||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}" 2>/dev/null || true
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/tags/${TAG}" 2>/dev/null || true
|
||||
echo "Deleted: ${TAG} (id: ${RELEASE_ID})"
|
||||
DELETED=$((DELETED + 1))
|
||||
fi
|
||||
done
|
||||
echo "Cleaned up ${DELETED} pre-release channel(s)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- STEP 11: Reset dev branch from main ------------------------------------
|
||||
- name: "Step 11: Delete and recreate dev branch from main"
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Maintenance
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
||||
# INGROUP: moko-platform.Maintenance
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||
# PATH: /templates/workflows/cascade-dev.yml.template
|
||||
# VERSION: 02.00.00
|
||||
# BRIEF: Forward-merge main → all open branches after every push to main
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Maintenance
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# INGROUP: moko-platform.Maintenance
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /.gitea/workflows/cleanup.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Deploy
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
|
||||
# INGROUP: moko-platform.Deploy
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
|
||||
# VERSION: 04.07.00
|
||||
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
run: |
|
||||
php -v && composer --version
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
- name: Setup moko-platform tools
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
@@ -48,10 +48,10 @@ jobs:
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
|
||||
/tmp/mokostandards-api 2>/dev/null || true
|
||||
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
|
||||
cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||
/tmp/moko-platform-api 2>/dev/null || true
|
||||
if [ -d "/tmp/moko-platform-api" ] && [ -f "/tmp/moko-platform-api/composer.json" ]; then
|
||||
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Check FTP configuration
|
||||
@@ -101,15 +101,28 @@ jobs:
|
||||
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
|
||||
[ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote)
|
||||
|
||||
PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/mokostandards-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
|
||||
PLATFORM=$(php /tmp/moko-platform-api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/moko-platform-api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/moko-platform-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
|
||||
else
|
||||
php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||
php /tmp/moko-platform-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||
fi
|
||||
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
|
||||
|
||||
- name: Post-deploy health check
|
||||
if: success() && steps.check.outputs.skip != 'true'
|
||||
run: |
|
||||
if [ -f "deploy/health-check.php" ]; then
|
||||
SITE_URL="${{ vars.DEV_SITE_URL }}"
|
||||
if [ -n "$SITE_URL" ]; then
|
||||
php deploy/health-check.php --url "$SITE_URL" --checks http --timeout 30 || echo "::warning::Health check failed after deploy"
|
||||
else
|
||||
echo "DEV_SITE_URL not configured, skipping health check"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
||||
# INGROUP: moko-platform.Security
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||
# PATH: /templates/workflows/gitleaks.yml.template
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Notifications
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# INGROUP: moko-platform.Notifications
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /.gitea/workflows/notify.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Push notifications via ntfy on release success or workflow failure
|
||||
@@ -18,7 +18,6 @@ on:
|
||||
- "Joomla Build & Release"
|
||||
- "Joomla Extension CI"
|
||||
- "Deploy"
|
||||
- "Cascade Main → Dev"
|
||||
types:
|
||||
- completed
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.CI
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
||||
# INGROUP: moko-platform.CI
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||
# PATH: /templates/workflows/universal/pr-check.yml.template
|
||||
# VERSION: 05.00.00
|
||||
# BRIEF: PR gate — branch policy + code validation before merge
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# INGROUP: moko-platform.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||
# VERSION: 05.00.00
|
||||
# BRIEF: Manual pre-release — builds dev/alpha/beta/rc packages from any branch
|
||||
@@ -49,26 +49,26 @@ jobs:
|
||||
run: |
|
||||
if ! command -v php &> /dev/null; then
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip >/dev/null 2>&1
|
||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
- name: Setup moko-platform tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
run: |
|
||||
git clone --depth 1 --branch main --quiet "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" /tmp/moko-platform-api
|
||||
|
||||
- name: Detect platform
|
||||
id: platform
|
||||
run: |
|
||||
# Read platform from XML manifest (<platform> tag) or plain text fallback
|
||||
PLATFORM=$(sed -n 's/.*<platform>\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1)
|
||||
[ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]')
|
||||
[ -z "$PLATFORM" ] && PLATFORM="generic"
|
||||
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
|
||||
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1)
|
||||
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
|
||||
echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT"
|
||||
php /tmp/moko-platform-api/cli/manifest_read.php --path . --github-output
|
||||
|
||||
- name: Resolve metadata
|
||||
id: meta
|
||||
run: |
|
||||
STABILITY="${{ inputs.stability }}"
|
||||
MOKO_API="/tmp/moko-platform-api/cli"
|
||||
|
||||
case "$STABILITY" in
|
||||
development) SUFFIX="-dev"; TAG="development" ;;
|
||||
@@ -77,55 +77,14 @@ jobs:
|
||||
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
|
||||
esac
|
||||
|
||||
# Read and bump patch version (with rollover)
|
||||
CURRENT=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' README.md 2>/dev/null | head -1)
|
||||
[ -z "$CURRENT" ] && CURRENT="00.00.00"
|
||||
|
||||
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
|
||||
MINOR=$(echo "$CURRENT" | cut -d. -f2)
|
||||
PATCH=$(echo "$CURRENT" | cut -d. -f3)
|
||||
|
||||
# Patch bump with rollover: ZZ=99 → bump minor, YY=99 → bump major
|
||||
NEW_PATCH=$((10#$PATCH + 1))
|
||||
NEW_MINOR=$((10#$MINOR))
|
||||
NEW_MAJOR=$((10#$MAJOR))
|
||||
|
||||
if [ $NEW_PATCH -gt 99 ]; then
|
||||
NEW_PATCH=0
|
||||
NEW_MINOR=$((NEW_MINOR + 1))
|
||||
fi
|
||||
if [ $NEW_MINOR -gt 99 ]; then
|
||||
NEW_MINOR=0
|
||||
NEW_MAJOR=$((NEW_MAJOR + 1))
|
||||
fi
|
||||
|
||||
VERSION=$(printf "%02d.%02d.%02d" $NEW_MAJOR $NEW_MINOR $NEW_PATCH)
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
echo "Bumping: ${CURRENT} → ${VERSION} (patch)"
|
||||
|
||||
# Update README.md
|
||||
sed -i "s/VERSION:[[:space:]]*${CURRENT}/VERSION: ${VERSION}/" README.md
|
||||
# Bump patch version
|
||||
BUMP_OUTPUT=$(php ${MOKO_API}/version_bump.php --path .)
|
||||
VERSION=$(echo "$BUMP_OUTPUT" | grep -oP '\d{2}\.\d{2}\.\d{2}$' || true)
|
||||
[ -z "$VERSION" ] && VERSION=$(php ${MOKO_API}/version_read.php --path .)
|
||||
echo "Version: ${VERSION}"
|
||||
|
||||
# Update platform-specific manifest
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
MANIFEST="${{ steps.platform.outputs.manifest }}"
|
||||
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
|
||||
case "$PLATFORM" in
|
||||
joomla)
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
MANIFEST_VER=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1)
|
||||
sed -i "s|<version>${MANIFEST_VER}</version>|<version>${VERSION}</version>|" "$MANIFEST"
|
||||
sed -i "s|<creationDate>[^<]*</creationDate>|<creationDate>${TODAY}</creationDate>|" "$MANIFEST"
|
||||
fi
|
||||
;;
|
||||
dolibarr)
|
||||
if [ -n "$MOD_FILE" ]; then
|
||||
sed -i "s/\$this->version = '[^']*'/\$this->version = '${VERSION}'/" "$MOD_FILE"
|
||||
fi
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
php ${MOKO_API}/version_set_platform.php --path . --version "${VERSION}"
|
||||
|
||||
# Commit version bump
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
@@ -133,40 +92,22 @@ jobs:
|
||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore(version): bump ${CURRENT} → ${VERSION} [skip ci]"
|
||||
git commit -m "chore(version): bump to ${VERSION} [skip ci]"
|
||||
git push origin HEAD 2>&1
|
||||
}
|
||||
|
||||
# Auto-detect element (platform-aware)
|
||||
case "$PLATFORM" in
|
||||
joomla)
|
||||
MANIFEST="${{ steps.platform.outputs.manifest }}"
|
||||
EXT_ELEMENT=""
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
|
||||
if [ -z "$EXT_ELEMENT" ]; then
|
||||
EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
|
||||
case "$EXT_ELEMENT" in
|
||||
templatedetails|manifest) EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
fi
|
||||
;;
|
||||
dolibarr)
|
||||
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
|
||||
if [ -n "$MOD_FILE" ]; then
|
||||
MOD_BASENAME=$(basename "$MOD_FILE" .class.php)
|
||||
EXT_ELEMENT=$(echo "$MOD_BASENAME" | sed 's/^mod//' | tr '[:upper:]' '[:lower:]')
|
||||
else
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
;;
|
||||
esac
|
||||
# Detect element from Joomla/Dolibarr manifest
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
EXT_ELEMENT=$(php ${MOKO_API}/manifest_read.php --path . --field name 2>/dev/null | tr -d ' ' | tr '[:upper:]' '[:lower:]' || true)
|
||||
# For Joomla, prefer <element> tag
|
||||
if [ "$PLATFORM" = "joomla" ]; then
|
||||
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
ELEM=$(grep -oP "<element>\K[^<]+" "$MANIFEST" 2>/dev/null | head -1)
|
||||
[ -n "$ELEM" ] && EXT_ELEMENT="$ELEM"
|
||||
fi
|
||||
fi
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
|
||||
ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
|
||||
|
||||
@@ -176,51 +117,42 @@ jobs:
|
||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
||||
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
||||
|
||||
- name: Build package
|
||||
run: |
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
if [ ! -d "$SOURCE_DIR" ]; then
|
||||
echo "::error::No src/ or htdocs/ directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build/package
|
||||
rsync -a \
|
||||
--exclude='sftp-config*' \
|
||||
--exclude='.ftpignore' \
|
||||
--exclude='*.ppk' \
|
||||
--exclude='*.pem' \
|
||||
--exclude='*.key' \
|
||||
--exclude='.env*' \
|
||||
--exclude='*.local' \
|
||||
--exclude='.build-trigger' \
|
||||
"${SOURCE_DIR}/" build/package/
|
||||
|
||||
- name: Create ZIP
|
||||
id: zip
|
||||
run: |
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
cd build/package
|
||||
zip -r "../${ZIP_NAME}" .
|
||||
cd ..
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
SUFFIX="${{ steps.meta.outputs.suffix }}"
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
|
||||
SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1)
|
||||
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
|
||||
echo "ZIP: ${ZIP_NAME} (SHA: ${SHA256:0:16}...)"
|
||||
if [ "$PLATFORM" = "joomla" ]; then
|
||||
php /tmp/moko-platform-api/cli/joomla_build.php --path . --version "${VERSION}" --suffix "${SUFFIX}" --output build --github-output
|
||||
else
|
||||
# Generic build: zip src/ directory
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && { echo "::error::No src/ or htdocs/"; exit 1; }
|
||||
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
|
||||
ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
|
||||
mkdir -p build
|
||||
cd "$SOURCE_DIR" && zip -r "../build/${ZIP_NAME}" . && cd ..
|
||||
SHA256=$(sha256sum "build/${ZIP_NAME}" | cut -d' ' -f1)
|
||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "zip_path=build/${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Create or replace Gitea release
|
||||
id: release
|
||||
continue-on-error: true
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
ZIP_NAME="${{ steps.zip.outputs.zip_name }}"
|
||||
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
@@ -258,99 +190,29 @@ jobs:
|
||||
curl -sS -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \
|
||||
--data-binary "@build/${ZIP_NAME}"
|
||||
--data-binary "@${{ steps.zip.outputs.zip_path }}"
|
||||
|
||||
echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})"
|
||||
|
||||
- name: Update updates.xml
|
||||
- name: "Update updates.xml"
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
echo "No updates.xml — skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
export PY_STABILITY="$STABILITY" PY_VERSION="$VERSION" PY_SHA256="$SHA256" \
|
||||
PY_ZIP_NAME="$ZIP_NAME" PY_TAG="$TAG" PY_DATE="$DATE" \
|
||||
PY_GITEA_ORG="$GITEA_ORG" PY_GITEA_REPO="$GITEA_REPO"
|
||||
python3 << 'PYEOF'
|
||||
import re, os
|
||||
|
||||
stability = os.environ["PY_STABILITY"]
|
||||
version = os.environ["PY_VERSION"]
|
||||
sha256 = os.environ["PY_SHA256"]
|
||||
zip_name = os.environ["PY_ZIP_NAME"]
|
||||
tag = os.environ["PY_TAG"]
|
||||
date = os.environ["PY_DATE"]
|
||||
gitea_org = os.environ["PY_GITEA_ORG"]
|
||||
gitea_repo = os.environ["PY_GITEA_REPO"]
|
||||
download_url = f"https://git.mokoconsulting.tech/{gitea_org}/{gitea_repo}/releases/download/{tag}/{zip_name}"
|
||||
|
||||
with open("updates.xml", "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Map stability to XML tag name
|
||||
tag_map = {"development": "development", "alpha": "alpha", "beta": "beta", "release-candidate": "rc"}
|
||||
xml_tag = tag_map.get(stability, stability)
|
||||
|
||||
pattern = r"(<update>(?:(?!</update>).)*?<tag>" + re.escape(xml_tag) + r"</tag>.*?</update>)"
|
||||
match = re.search(pattern, content, re.DOTALL)
|
||||
if match:
|
||||
block = match.group(1)
|
||||
updated = re.sub(r"<version>[^<]*</version>", f"<version>{version}</version>", block)
|
||||
updated = re.sub(r"<creationDate>[^<]*</creationDate>", f"<creationDate>{date}</creationDate>", updated)
|
||||
if "<sha256>" in updated:
|
||||
updated = re.sub(r"<sha256>[^<]*</sha256>", f"<sha256>{sha256}</sha256>", updated)
|
||||
else:
|
||||
updated = updated.replace("</downloads>", f"</downloads>\n <sha256>{sha256}</sha256>")
|
||||
updated = re.sub(r"(<downloadurl[^>]*>)[^<]*(</downloadurl>)", rf"\g<1>{download_url}\g<2>", updated)
|
||||
content = content.replace(block, updated)
|
||||
print(f"Updated {xml_tag} channel: version={version}")
|
||||
else:
|
||||
print(f"WARNING: No <tag>{xml_tag}</tag> block in updates.xml")
|
||||
|
||||
with open("updates.xml", "w") as f:
|
||||
f.write(content)
|
||||
PYEOF
|
||||
|
||||
# Commit and push to current branch
|
||||
php /tmp/moko-platform-api/cli/updates_xml_build.php --path . --version "$VERSION" --stability "$STABILITY" --sha "$SHA256" --gitea-url "$GITEA_URL" --org "$GITEA_ORG" --repo "$GITEA_REPO"
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git add updates.xml
|
||||
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
||||
git commit -m "chore: update $STABILITY channel $VERSION [skip ci]"
|
||||
git push origin HEAD 2>&1 || echo "WARNING: push failed"
|
||||
fi
|
||||
|
||||
- name: "Sync updates.xml to all branches"
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
|
||||
# Sync updates.xml to main and dev (whichever isn't current)
|
||||
for BRANCH in main dev; do
|
||||
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
|
||||
|
||||
echo "Syncing updates.xml → ${BRANCH}"
|
||||
git fetch origin "${BRANCH}" 2>/dev/null || continue
|
||||
git checkout "origin/${BRANCH}" -- . 2>/dev/null || continue
|
||||
git checkout "${CURRENT_BRANCH}" -- updates.xml
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git add updates.xml
|
||||
git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
|
||||
git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
|
||||
fi
|
||||
git checkout "${CURRENT_BRANCH}" 2>/dev/null
|
||||
done
|
||||
php /tmp/moko-platform-api/cli/updates_xml_sync.php --path . --current "${{ github.ref_name }}" --branches main,dev --version "${{ steps.meta.outputs.version }}" --token "${{ secrets.GA_TOKEN }}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" --gitea-url "${GITEA_URL}"
|
||||
|
||||
- name: "Delete lesser pre-release channels (cascade)"
|
||||
continue-on-error: true
|
||||
|
||||
@@ -7,18 +7,14 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Validation
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
||||
# INGROUP: moko-platform.Validation
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||
# PATH: /templates/workflows/joomla/repo_health.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts.
|
||||
# ============================================================================
|
||||
|
||||
name: "Joomla: Repo Health"
|
||||
|
||||
concurrency:
|
||||
group: repo-health-${{ github.repository }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
name: "Generic: Repo Health"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -288,7 +284,7 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
IFS=',' read -r -a required_dirs <<< "${SCRIPTS_REQUIRED_DIRS}"
|
||||
if [ -n "${SCRIPTS_REQUIRED_DIRS:-}" ]; then IFS=',' read -r -a required_dirs <<< "${SCRIPTS_REQUIRED_DIRS}"; else required_dirs=(); fi
|
||||
IFS=',' read -r -a allowed_dirs <<< "${SCRIPTS_ALLOWED_DIRS}"
|
||||
|
||||
missing_dirs=()
|
||||
@@ -392,23 +388,27 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Source directory: src/ or htdocs/ (either is valid)
|
||||
IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}"
|
||||
IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}"
|
||||
if [ -n "${REPO_DISALLOWED_DIRS:-}" ]; then IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"; else disallowed_dirs=(); fi
|
||||
IFS=',' read -r -a disallowed_files <<< "${REPO_DISALLOWED_FILES:-}"
|
||||
|
||||
missing_required=()
|
||||
missing_optional=()
|
||||
|
||||
# Source directory: src/ or htdocs/ (either is valid for extension repos)
|
||||
SOURCE_DIR=""
|
||||
if [ -d "src" ]; then
|
||||
SOURCE_DIR="src"
|
||||
elif [ -d "htdocs" ]; then
|
||||
SOURCE_DIR="htdocs"
|
||||
elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then
|
||||
# Platform/tooling repos don't need src/
|
||||
SOURCE_DIR=""
|
||||
else
|
||||
missing_required+=("src/ or htdocs/ (source directory required)")
|
||||
fi
|
||||
|
||||
IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}"
|
||||
IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}"
|
||||
IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"
|
||||
IFS=',' read -r -a disallowed_files <<< "${REPO_DISALLOWED_FILES}"
|
||||
|
||||
missing_required=()
|
||||
missing_optional=()
|
||||
|
||||
for item in "${required_artifacts[@]}"; do
|
||||
if printf '%s' "${item}" | grep -q '/$'; then
|
||||
d="${item%/}"
|
||||
@@ -450,12 +450,8 @@ jobs:
|
||||
fi
|
||||
done < <(git branch -r --list 'origin/dev*' | sed 's/^ *//')
|
||||
|
||||
if [ "${#dev_paths[@]}" -eq 0 ]; then
|
||||
missing_required+=("dev/* branch (e.g. dev/01.00.00)")
|
||||
fi
|
||||
|
||||
if [ "${#dev_branches[@]}" -gt 0 ]; then
|
||||
missing_required+=("invalid branch dev (must be dev/<version>)")
|
||||
if [ "${#dev_paths[@]}" -eq 0 ] && [ "${#dev_branches[@]}" -eq 0 ]; then
|
||||
missing_required+=("dev or dev/* branch")
|
||||
fi
|
||||
|
||||
content_warnings=()
|
||||
@@ -481,26 +477,7 @@ jobs:
|
||||
export MISSING_OPTIONAL="$(printf '%s\n' "${missing_optional[@]:-}")"
|
||||
export CONTENT_WARNINGS="$(printf '%s\n' "${content_warnings[@]:-}")"
|
||||
|
||||
report_json="$(python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
|
||||
profile = os.environ.get('PROFILE_RAW') or 'all'
|
||||
|
||||
missing_required = os.environ.get('MISSING_REQUIRED', '').splitlines() if os.environ.get('MISSING_REQUIRED') else []
|
||||
missing_optional = os.environ.get('MISSING_OPTIONAL', '').splitlines() if os.environ.get('MISSING_OPTIONAL') else []
|
||||
content_warnings = os.environ.get('CONTENT_WARNINGS', '').splitlines() if os.environ.get('CONTENT_WARNINGS') else []
|
||||
|
||||
out = {
|
||||
'profile': profile,
|
||||
'missing_required': [x for x in missing_required if x],
|
||||
'missing_optional': [x for x in missing_optional if x],
|
||||
'content_warnings': [x for x in content_warnings if x],
|
||||
}
|
||||
|
||||
print(json.dumps(out, indent=2))
|
||||
PY
|
||||
)"
|
||||
report_json=$(printf '{"profile":"%s","missing_required":%d,"missing_optional":%d,"content_warnings":%d}' "$profile" "${#missing_required[@]}" "${#missing_optional[@]}" "${#content_warnings[@]}")
|
||||
|
||||
{
|
||||
printf '%s\n' '### Repository health'
|
||||
@@ -578,12 +555,14 @@ jobs:
|
||||
joomla_findings+=("updates.xml missing in root (required for Joomla update server)")
|
||||
fi
|
||||
|
||||
INDEX_DIRS=("${SOURCE_DIR}" "${SOURCE_DIR}/admin" "${SOURCE_DIR}/site")
|
||||
for dir in "${INDEX_DIRS[@]}"; do
|
||||
if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then
|
||||
joomla_findings+=("${dir}/index.html missing (directory listing protection)")
|
||||
fi
|
||||
done
|
||||
if [ -n "${SOURCE_DIR}" ]; then
|
||||
INDEX_DIRS=("${SOURCE_DIR}" "${SOURCE_DIR}/admin" "${SOURCE_DIR}/site")
|
||||
for dir in "${INDEX_DIRS[@]}"; do
|
||||
if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then
|
||||
joomla_findings+=("${dir}/index.html missing (directory listing protection)")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "${#joomla_findings[@]}" -gt 0 ]; then
|
||||
{
|
||||
@@ -629,43 +608,29 @@ jobs:
|
||||
fi
|
||||
|
||||
if [ -f "${DOCS_INDEX}" ]; then
|
||||
missing_links="$(python3 - <<'PY'
|
||||
import os
|
||||
import re
|
||||
|
||||
idx = os.environ.get('DOCS_INDEX', 'docs/docs-index.md')
|
||||
base = os.getcwd()
|
||||
|
||||
bad = []
|
||||
pat = re.compile(r'\[[^\]]+\]\(([^)]+)\)')
|
||||
|
||||
with open(idx, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
for m in pat.findall(line):
|
||||
link = m.strip()
|
||||
if link.startswith('http://') or link.startswith('https://') or link.startswith('#') or link.startswith('mailto:'):
|
||||
continue
|
||||
if link.startswith('/'):
|
||||
rel = link.lstrip('/')
|
||||
else:
|
||||
rel = os.path.normpath(os.path.join(os.path.dirname(idx), link))
|
||||
rel = rel.split('#', 1)[0]
|
||||
rel = rel.split('?', 1)[0]
|
||||
if not rel:
|
||||
continue
|
||||
p = os.path.join(base, rel)
|
||||
if not os.path.exists(p):
|
||||
bad.append(rel)
|
||||
|
||||
print('\n'.join(sorted(set(bad))))
|
||||
PY
|
||||
)"
|
||||
missing_links=""
|
||||
while IFS= read -r docline; do
|
||||
for link in $(echo "$docline" | grep -oE '\]\([^)]+\)' | sed 's/\](//' | sed 's/)$//' || true); do
|
||||
case "$link" in http://*|https://*|"#"*|mailto:*) continue ;; esac
|
||||
linkpath="${link%%#*}"
|
||||
linkpath="${linkpath%%\?*}"
|
||||
[ -z "$linkpath" ] && continue
|
||||
if [ "${linkpath:0:1}" = "/" ]; then
|
||||
testpath="${linkpath#/}"
|
||||
else
|
||||
testpath="$(dirname "${DOCS_INDEX}")/${linkpath}"
|
||||
fi
|
||||
[ ! -e "$testpath" ] && missing_links="${missing_links}${testpath} "
|
||||
done
|
||||
done < "${DOCS_INDEX}"
|
||||
if [ -n "${missing_links}" ]; then
|
||||
extended_findings+=("docs/docs-index.md contains broken relative links")
|
||||
{
|
||||
printf '%s\n' '### Docs index link integrity'
|
||||
printf '%s\n' 'Broken relative links:'
|
||||
while IFS= read -r l; do [ -n "${l}" ] && printf '%s\n' "- ${l}"; done <<< "${missing_links}"
|
||||
for bl in ${missing_links}; do
|
||||
printf '%s\n' "- ${bl}"
|
||||
done
|
||||
printf '\n'
|
||||
} >> "${GITHUB_STEP_SUMMARY}"
|
||||
fi
|
||||
@@ -764,3 +729,41 @@ jobs:
|
||||
fi
|
||||
|
||||
printf '%s\n' 'Repository health guardrails passed.' >> "${GITHUB_STEP_SUMMARY}"
|
||||
|
||||
|
||||
site-health:
|
||||
name: Site Health
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.3'
|
||||
|
||||
- name: Uptime check
|
||||
if: env.URLS != ''
|
||||
run: |
|
||||
echo "$URLS" > /tmp/urls.txt
|
||||
php monitoring/uptime-probe.php --urls /tmp/urls.txt --timeout 15 || echo "::warning::Some sites are down"
|
||||
rm -f /tmp/urls.txt
|
||||
env:
|
||||
URLS: ${{ vars.MONITORED_URLS }}
|
||||
|
||||
- name: SSL certificate check
|
||||
if: env.DOMAINS != ''
|
||||
run: |
|
||||
echo "$DOMAINS" > /tmp/domains.txt
|
||||
php monitoring/ssl-check.php --domains /tmp/domains.txt --warn-days 30 || echo "::warning::SSL certificates expiring soon"
|
||||
rm -f /tmp/domains.txt
|
||||
env:
|
||||
DOMAINS: ${{ vars.MONITORED_DOMAINS }}
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "### Site Health" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Uptime and SSL checks completed." >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# INGROUP: moko-platform.Security
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /.gitea/workflows/security-audit.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Dependency vulnerability scanning for composer and npm packages
|
||||
@@ -80,3 +80,19 @@ jobs:
|
||||
-H "Priority: high" \
|
||||
-d "Security audit found vulnerabilities. Review dependency updates." \
|
||||
"${NTFY_URL}/${NTFY_TOPIC}" || true
|
||||
|
||||
|
||||
- name: Joomla version audit
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f "monitoring/joomla-version-audit.php" ] && [ -n "$JOOMLA_SITES" ]; then
|
||||
echo "$JOOMLA_SITES" > /tmp/sites.json
|
||||
php monitoring/joomla-version-audit.php --sites /tmp/sites.json || true
|
||||
echo "### Joomla Version Audit" >> $GITHUB_STEP_SUMMARY
|
||||
rm -f /tmp/sites.json
|
||||
else
|
||||
echo "Joomla audit skipped (no script or JOOMLA_SITES_JSON not configured)"
|
||||
fi
|
||||
env:
|
||||
JOOMLA_SITES: ${{ vars.JOOMLA_SITES_JSON }}
|
||||
|
||||
|
||||
+76
-111
@@ -21,8 +21,8 @@
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: MokoJoomTOS
|
||||
INGROUP: plg_system_mokojoomtos
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoJoomTOS
|
||||
VERSION: 03.08.04
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS
|
||||
VERSION: 04.02.01
|
||||
PATH: ./CHANGELOG.md
|
||||
BRIEF: Version history and release notes
|
||||
-->
|
||||
@@ -36,134 +36,99 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Planned
|
||||
### Fixed
|
||||
|
||||
- GitHub Actions CI/CD workflows
|
||||
- Pre-commit hooks for code quality
|
||||
- Docker development environment
|
||||
- enablePlugin() now called unconditionally in postflight() (#89)
|
||||
- Replaced Table::addIncludePath() with bootComponent()->getMVCFactory() for Joomla 5 (#90)
|
||||
- Added non-SEF URL fallback via Itemid matching (#91)
|
||||
- enablePlugin() now fires on upgrade path (#92)
|
||||
- Removed $_GET superglobal mutation (#93)
|
||||
- Added params, metadata, attribs defaults to article creation (#94)
|
||||
- Applied urldecode() to URI path before slug comparison (#95)
|
||||
- Cast Registry return to array before iterating slugs (#96)
|
||||
- Fixed MenuslugField separator `disable` to `disabled` property (#99)
|
||||
|
||||
### Added
|
||||
|
||||
- SEF disabled warning in MenuslugField dropdown (#97)
|
||||
- Include Children toggle for offline-accessible menu items (defaults to Yes)
|
||||
|
||||
### Fixed (Manifest)
|
||||
|
||||
- Hardcode description in XML manifest (language variables don't resolve during install)
|
||||
|
||||
### Changed
|
||||
|
||||
- Stripped legacy mokojoomtos.php to minimal stub (#101)
|
||||
- Converted script.php indentation from spaces to tabs (#102)
|
||||
- Renamed installer class to PlgSystemMokojoomtosInstallerScript (#103)
|
||||
|
||||
## [04.01.00] - 2026-05-16
|
||||
|
||||
### Fixed
|
||||
|
||||
- Legacy `mokojoomtos.php` now uses `onAfterRoute` event instead of `onAfterInitialise`
|
||||
- Legacy plugin entry point now sets `tmpl=component` for component-only view during offline mode
|
||||
- Installation script language keys corrected to match actual INI file definitions
|
||||
- Installation script `$extension` property corrected from `plg_system_mokojoomtos_offline` to `plg_system_mokojoomtos`
|
||||
- README.md event reference corrected from `onAfterInitialise` to `onAfterRoute`
|
||||
- Use literal display name in manifest `<name>` tag so Joomla stores "System - Moko Terms of Service" directly in the DB
|
||||
- Updated plugin help text to describe multi-select workflow
|
||||
|
||||
## [04.00.00] - 2026-05-16
|
||||
|
||||
### Added
|
||||
|
||||
- Multi-select support: configure multiple menu items to remain accessible during offline mode
|
||||
- `services/provider.php` for Joomla 5 dependency injection container registration
|
||||
- Gitea-hosted update server URL in plugin manifest
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated all markdown FILE INFORMATION headers to reference MokoJoomTOS repository
|
||||
- Updated CONTRIBUTING.md title, clone URLs, and links to reference MokoJoomTOS
|
||||
- Updated CHANGELOG.md links to reference MokoJoomTOS repository
|
||||
- Updated CLAUDE.md and copilot-instructions.md for accuracy
|
||||
- Match against full menu route path instead of alias only (fixes nested routes like `/legal/terms-of-service`)
|
||||
- Plugin pretty name updated to Joomla convention: "System - Moko Terms of Service"
|
||||
- MenuslugField now stores and displays full route paths (e.g., `legal/terms-of-service`)
|
||||
|
||||
## [03.08.04] - 2026-02-28
|
||||
### Removed
|
||||
|
||||
- GitHub update server references (fully migrated to Gitea)
|
||||
- Legacy `src/plugins/` directory
|
||||
|
||||
## [03.09.00] - 2026-05-16
|
||||
|
||||
### Fixed
|
||||
|
||||
- Installation script now resolves Uncategorised category ID dynamically (no longer assumes ID 2)
|
||||
- Installation script now resolves `com_content` component ID dynamically (removed hardcoded fallback 22)
|
||||
- All `Exception` catches qualified with backslash for namespace safety
|
||||
- Added missing `PLG_SYSTEM_MOKOJOOMTOS_ERROR_LOADING_MENU_ITEMS` language key to all locale files
|
||||
- Standardized help text URL across all locale files
|
||||
- Articles created during install are now owned by the installing admin user (not user ID 0)
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated version number to 03.08.04 across all files
|
||||
- Added Gitea update server URL to plugin manifest (`updates.xml` on `main`)
|
||||
- Fixed template chrome loading issue by changing event hook from onAfterInitialise to onAfterRoute
|
||||
|
||||
### Fixed
|
||||
|
||||
- Template chrome (header, footer, modules) no longer loads when accessing TOS page in offline mode
|
||||
- Component-only view now properly applied when site is offline
|
||||
|
||||
### Removed
|
||||
|
||||
- Legacy duplicate manifest at `src/plugins/system/mokojoomtos/mokojoomtos.xml`
|
||||
|
||||
## [1.0.0] - 2026-01-16
|
||||
|
||||
### Added
|
||||
|
||||
- Initial template repository structure
|
||||
- Comprehensive Makefile with 30+ build targets
|
||||
- Complete README.md with usage documentation
|
||||
- CONTRIBUTING.md with contribution guidelines
|
||||
- SECURITY.md with security policy and best practices
|
||||
- CHANGELOG.md for version tracking
|
||||
- LICENSE file (GPL-3.0-or-later)
|
||||
- EditorConfig for consistent code formatting
|
||||
- Documentation structure in `docs/` directory
|
||||
- Placeholders for component directories (`admin/`, `site/`, `media/`)
|
||||
- MokoStandards compliance:
|
||||
- File header standards with copyright and SPDX identifiers
|
||||
- Joomla coding standards configuration
|
||||
- PHPStan static analysis setup
|
||||
- Dependency security auditing
|
||||
- Makefile targets for:
|
||||
- Dependency management (install-deps, update-deps)
|
||||
- Code validation (lint, phpcs, phpstan)
|
||||
- Testing (test, test-coverage)
|
||||
- Building (build, build-assets)
|
||||
- Development (dev-install, watch-assets)
|
||||
- Release management (release, bump-version)
|
||||
- Utility commands (clean, version, help)
|
||||
- Documentation index files with MokoStandards metadata
|
||||
|
||||
### Documentation
|
||||
|
||||
- Comprehensive README with:
|
||||
- Quick start guide
|
||||
- Prerequisites and installation
|
||||
- Usage examples
|
||||
- Project structure overview
|
||||
- Makefile command reference
|
||||
- Standards compliance information
|
||||
- Detailed CONTRIBUTING guide with:
|
||||
- Commit message conventions
|
||||
- DCO sign-off requirements
|
||||
- Development setup instructions
|
||||
- Code review process
|
||||
- Security policy with:
|
||||
- Vulnerability reporting procedures
|
||||
- Security best practices
|
||||
- Code examples for secure development
|
||||
- Template structure for Joomla 4.x and 5.x compatibility
|
||||
|
||||
## Version Guidelines
|
||||
|
||||
### Version Numbering
|
||||
|
||||
This template follows [Semantic Versioning](https://semver.org/):
|
||||
|
||||
- **MAJOR** version for incompatible changes (e.g., 2.0.0)
|
||||
- **MINOR** version for new features (e.g., 1.1.0)
|
||||
- **PATCH** version for bug fixes (e.g., 1.0.1)
|
||||
|
||||
### Change Categories
|
||||
|
||||
- **Added**: New features
|
||||
- **Changed**: Changes to existing functionality
|
||||
- **Deprecated**: Soon-to-be removed features
|
||||
- **Removed**: Removed features
|
||||
- **Fixed**: Bug fixes
|
||||
- **Security**: Security fixes
|
||||
|
||||
## Release Notes
|
||||
|
||||
### [1.0.0] Release Notes
|
||||
|
||||
This is the initial release of the MokoJoomTOS plugin. It provides a production-ready Joomla system plugin for offline TOS access with:
|
||||
|
||||
- **Complete build system** via comprehensive Makefile
|
||||
- **MokoStandards compliance** out of the box
|
||||
- **Developer-friendly** workflow with automation
|
||||
- **Security-focused** with built-in best practices
|
||||
- **Well-documented** with clear usage instructions
|
||||
|
||||
This template is ready for use in creating new Joomla components that follow organizational coding standards and best practices.
|
||||
|
||||
### Migration Guide
|
||||
|
||||
**For New Projects**: Simply use this template to create your new component repository.
|
||||
|
||||
**For Existing Projects**: Review the Makefile, documentation structure, and standards compliance files to gradually adopt features that benefit your project.
|
||||
- Initial plugin release
|
||||
- Offline mode bypass for configured menu slug
|
||||
- Auto-provisioning installer (creates article, menu type, menu item)
|
||||
- Component-only rendering (`tmpl=component`)
|
||||
- Language files for en-GB and en-US
|
||||
- MokoStandards compliance
|
||||
|
||||
## Links
|
||||
|
||||
- [Repository](https://github.com/mokoconsulting-tech/MokoJoomTOS)
|
||||
- [Issues](https://github.com/mokoconsulting-tech/MokoJoomTOS/issues)
|
||||
- [Pull Requests](https://github.com/mokoconsulting-tech/MokoJoomTOS/pulls)
|
||||
- [MokoStandards](https://github.com/mokoconsulting-tech/MokoCodingDefaults)
|
||||
- [Repository](https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS)
|
||||
- [Issues](https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/issues)
|
||||
- [Releases](https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases)
|
||||
|
||||
[Unreleased]: https://github.com/mokoconsulting-tech/MokoJoomTOS/compare/v03.08.04...HEAD
|
||||
[03.08.04]: https://github.com/mokoconsulting-tech/MokoJoomTOS/releases/tag/v03.08.04
|
||||
[1.0.0]: https://github.com/mokoconsulting-tech/MokoJoomTOS/releases/tag/v1.0.0
|
||||
[Unreleased]: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/compare/stable...dev
|
||||
[04.01.00]: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/tag/stable
|
||||
[04.00.00]: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/compare/v03.09.00...stable
|
||||
[03.09.00]: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/tag/v03.09.00
|
||||
[1.0.0]: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/tag/v1.0.0
|
||||
|
||||
@@ -6,7 +6,7 @@ MokoJoomTOS is a lightweight Joomla 4.x/5.x system plugin that allows Terms of S
|
||||
|
||||
```
|
||||
/
|
||||
├── .github/ # GitHub workflows, issue templates, copilot-instructions.md
|
||||
├── .mokogitea/ # GitHub workflows, issue templates, copilot-instructions.md
|
||||
├── docs/ # Detailed documentation (currently minimal with index.md)
|
||||
├── scripts/ # Build and utility scripts (validate/, package scripts)
|
||||
├── src/ # Plugin source code at root level (NOT nested under plugins/)
|
||||
@@ -75,7 +75,7 @@ XML files use the MokoStandard header format:
|
||||
DEFGROUP: MokoJoomTOS
|
||||
INGROUP: plg_system_mokojoomtos
|
||||
PATH: src/mokojoomtos.xml
|
||||
VERSION: 03.08.04
|
||||
VERSION: 04.00.00
|
||||
BRIEF: [Brief description of file purpose]
|
||||
=========================================================================
|
||||
-->
|
||||
@@ -88,7 +88,7 @@ Markdown files use an HTML comment format with the same structure.
|
||||
- **DEFGROUP**: Top-level group (always `MokoJoomTOS` for this repo)
|
||||
- **INGROUP**: Subgroup/component (always `plg_system_mokojoomtos`)
|
||||
- **PATH**: Relative path from repository root (e.g., `src/mokojoomtos.xml`)
|
||||
- **VERSION**: Current plugin version (currently `03.08.04`)
|
||||
- **VERSION**: Current plugin version (currently `04.00.00`)
|
||||
- **BRIEF**: One-line description of file's purpose
|
||||
|
||||
### Exempt Files
|
||||
@@ -327,7 +327,7 @@ No automated test infrastructure exists in this repository. Manual testing requi
|
||||
```bash
|
||||
# Manual packaging (build scripts being migrated to scripts/ directory)
|
||||
cd src/
|
||||
zip -r ../plg_system_mokojoomtos-03.08.04.zip .
|
||||
zip -r ../plg_system_mokojoomtos-04.00.00.zip .
|
||||
```
|
||||
|
||||
Package should contain: `mokojoomtos.php`, `mokojoomtos.xml`, `script.php`, `src/`, `language/`, `administrator/`
|
||||
@@ -397,7 +397,7 @@ Before opening a pull request, ensure:
|
||||
|
||||
- [ ] CHANGELOG.md updated with changes under correct version
|
||||
- [ ] README.md updated if user-facing changes
|
||||
- [ ] All markdown file headers include VERSION: 03.08.04
|
||||
- [ ] All markdown file headers include VERSION: 04.00.00
|
||||
- [ ] XML file headers include complete FILE INFORMATION block
|
||||
|
||||
## Version Management
|
||||
@@ -469,6 +469,6 @@ This repository follows minimal documentation structure with essential docs in r
|
||||
2. **SECURITY.md** - Security policy and vulnerability reporting procedures
|
||||
3. **CODE_OF_CONDUCT.md** - Community standards and behavior expectations
|
||||
4. **CHANGELOG.md** - Version history following Keep a Changelog format
|
||||
5. **.github/copilot-instructions.md** - Comprehensive guidance for GitHub Copilot (includes all Joomla patterns)
|
||||
5. **.mokogitea/copilot-instructions.md** - Comprehensive guidance for GitHub Copilot (includes all Joomla patterns)
|
||||
|
||||
Currently no `docs/policy/` directory exists - all policy is in root-level markdown files.
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
A Joomla system plugin that keeps your Terms of Service, Privacy Policy, or any legal page accessible to visitors -- even when the site is in offline (maintenance) mode.
|
||||
|
||||
    
|
||||
    
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Author** | [Moko Consulting](https://mokoconsulting.tech) |
|
||||
| **License** | GPL-3.0-or-later |
|
||||
| **Platform** | [Gitea](https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS) |
|
||||
| **Version** | 03.08.04 |
|
||||
| **Version** | 04.02.00 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# Docs Index: /templates/repos/joomla/component/docs
|
||||
|
||||
## Purpose
|
||||
|
||||
This index provides navigation to documentation within this folder.
|
||||
|
||||
## Metadata
|
||||
|
||||
- **Document Type:** index
|
||||
- **Auto-generated:** This file is automatically generated by rebuild_indexes.py
|
||||
|
||||
## Revision History
|
||||
|
||||
| Change | Notes | Author |
|
||||
| --- | --- | --- |
|
||||
| Automated update | Generated by documentation index automation | rebuild_indexes.py |
|
||||
@@ -1,119 +0,0 @@
|
||||
<!--
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: MokoJoomTOS.Documentation
|
||||
INGROUP: MokoStandards.Templates
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoJoomTOS
|
||||
PATH: /docs/update-server.md
|
||||
VERSION: 04.04.00
|
||||
BRIEF: How this extension's Joomla update server file (update.xml) is managed
|
||||
-->
|
||||
|
||||
# Joomla Update Server
|
||||
|
||||
[](https://github.com/mokoconsulting-tech/MokoStandards)
|
||||
|
||||
This document explains how `update.xml` is automatically managed for this Joomla extension following the [Joomla Update Server specification](https://docs.joomla.org/Deploying_an_Update_Server).
|
||||
|
||||
## How It Works
|
||||
|
||||
Joomla checks for extension updates by fetching an XML file from the URL defined in the `<updateservers>` tag in the extension's XML manifest. MokoStandards generates this file automatically.
|
||||
|
||||
### Automatic Generation
|
||||
|
||||
| Event | Workflow | `<tag>` | `<version>` |
|
||||
|-------|----------|---------|-------------|
|
||||
| Merge to `main` | `auto-release.yml` | `stable` | `XX.YY.ZZ` |
|
||||
| Push to `dev/**` | `deploy-dev.yml` | `development` | `development` |
|
||||
| Push to `rc/**` | `deploy-dev.yml` | `rc` | `XX.YY.ZZ-rc` |
|
||||
|
||||
### Generated XML Structure
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<updates>
|
||||
<update>
|
||||
<name>Extension Name</name>
|
||||
<description>Extension Name update</description>
|
||||
<element>com_extensionname</element>
|
||||
<type>component</type>
|
||||
<version>01.02.03</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder> <!-- plugins only -->
|
||||
<tags>
|
||||
<tag>stable</tag>
|
||||
</tags>
|
||||
<infourl title="Extension Name">https://github.com/.../releases/tag/v01.02.03</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://github.com/.../releases/download/v01.02.03/com_ext-01.02.03.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="((5\.[0-9])|(6\.[0-9]))" />
|
||||
<php_minimum>8.2</php_minimum> <!-- if present in manifest -->
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
</updates>
|
||||
```
|
||||
|
||||
### Metadata Source
|
||||
|
||||
All metadata is extracted from the extension's XML manifest (`src/*.xml`) at build time:
|
||||
|
||||
| XML Element | Source | Notes |
|
||||
|-------------|--------|-------|
|
||||
| `<name>` | `<name>` in manifest | Extension display name |
|
||||
| `<element>` | `<element>` in manifest | Must match installed extension identifier |
|
||||
| `<type>` | `type` attribute on `<extension>` | `component`, `module`, `plugin`, `library`, `package`, `template` |
|
||||
| `<client>` | `client` attribute on `<extension>` | `site` or `administrator` — **required for plugins and modules** |
|
||||
| `<folder>` | `group` attribute on `<extension>` | Plugin group (e.g., `system`, `content`) — **required for plugins** |
|
||||
| `<targetplatform>` | `<targetplatform>` in manifest | Falls back to Joomla 5.x / 6.x if not specified |
|
||||
| `<php_minimum>` | `<php_minimum>` in manifest | Included only if present |
|
||||
|
||||
### Extension Manifest Setup
|
||||
|
||||
Your XML manifest must include an `<updateservers>` tag pointing to the `update.xml` on the `main` branch:
|
||||
|
||||
```xml
|
||||
<extension type="component" client="site" method="upgrade">
|
||||
<name>My Extension</name>
|
||||
<element>com_myextension</element>
|
||||
<!-- ... -->
|
||||
<updateservers>
|
||||
<server type="extension" name="My Extension Updates">
|
||||
https://raw.githubusercontent.com/mokoconsulting-tech/MokoJoomTOS/main/update.xml
|
||||
</server>
|
||||
</updateservers>
|
||||
</extension>
|
||||
```
|
||||
|
||||
### Branch Lifecycle
|
||||
|
||||
```
|
||||
dev/XX.YY.ZZ → rc/XX.YY.ZZ → main → version/XX.YY
|
||||
(development) (rc) (stable) (frozen snapshot)
|
||||
```
|
||||
|
||||
1. **Development** (`dev/**`): `update.xml` with `<tag>development</tag>`, download points to branch archive
|
||||
2. **Release Candidate** (`rc/**`): `update.xml` with `<tag>rc</tag>`, version set to `XX.YY.ZZ-rc`
|
||||
3. **Stable Release** (merge to `main`): `update.xml` with `<tag>stable</tag>`, download points to GitHub Release asset
|
||||
4. **Frozen Snapshot** (`version/XX.YY`): immutable, never force-pushed
|
||||
|
||||
### Health Checks
|
||||
|
||||
The `repo_health.yml` workflow verifies on every commit:
|
||||
|
||||
- `update.xml` exists in the repository root
|
||||
- XML manifest exists with `<extension>` tag
|
||||
- `<version>`, `<name>`, `<author>`, `<namespace>` tags present
|
||||
- Extension `type` attribute is valid
|
||||
- Language `.ini` files exist
|
||||
- `index.html` directory listing protection in `src/`, `src/admin/`, `src/site/`
|
||||
|
||||
---
|
||||
|
||||
*Managed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). See [docs/workflows/update-server.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/workflows/update-server.md) for the full specification.*
|
||||
@@ -3,17 +3,26 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Moko Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when the site is in offline mode. Simply configure the menu slug (e.g., 'terms-of-service') and that page will remain accessible even when the site is offline."
|
||||
|
||||
; Configuration
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELDSET_BASIC="Basic Settings"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL="Terms of Service Menu Slug"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC="Enter the menu slug for your Terms of Service page (e.g., 'terms-of-service'). This page will be accessible even when the site is offline. The slug must match the menu item alias exactly."
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL="Offline-Accessible Menu Items"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC="Select one or more menu items that should remain accessible when the site is in offline mode. Hold Ctrl/Cmd to select multiple items."
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_LABEL="Include Child Menu Items"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_DESC="When enabled, child menu items under the selected items will also be accessible during offline mode. For example, selecting 'legal' will also allow access to 'legal/terms-of-service' and 'legal/privacy-policy'."
|
||||
|
||||
; Help
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_LABEL="How to Use This Plugin"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_DESC="<strong>Step 1:</strong> Create a Joomla article for your Terms of Service.<br/><strong>Step 2:</strong> Create a menu item pointing to that article.<br/><strong>Step 3:</strong> Set the menu item alias/slug (e.g., 'terms-of-service').<br/><strong>Step 4:</strong> Enter that same slug above.<br/><strong>Step 5:</strong> When your site goes offline, visitors can still access your-site.com/terms-of-service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_DESC="<strong>Step 1:</strong> Create articles for your legal pages (Terms of Service, Privacy Policy, etc.).<br/><strong>Step 2:</strong> Create menu items pointing to those articles.<br/><strong>Step 3:</strong> Select the menu items above (hold Ctrl/Cmd to select multiple).<br/><strong>Step 4:</strong> When your site goes offline, visitors can still access the selected pages.<br/><br/><em>Tip:</em> The dropdown shows the full URL path for each menu item (e.g., /legal/terms-of-service)."
|
||||
|
||||
; Warnings
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_SEF_WARNING="⚠ SEF URLs are disabled — path matching requires SEF. Itemid fallback is active."
|
||||
|
||||
; Errors
|
||||
PLG_SYSTEM_MOKOJOOMTOS_ERROR_LOADING_MENU_ITEMS="Error loading menu items: %s"
|
||||
|
||||
; Installation messages
|
||||
PLG_SYSTEM_MOKOJOOMTOS_INSTALL_SUCCESS="MokoJoomTOS Plugin installed successfully!"
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Moko Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when site is offline"
|
||||
|
||||
@@ -3,17 +3,26 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Moko Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when the site is in offline mode. Simply configure the menu slug (e.g., 'terms-of-service') and that page will remain accessible even when the site is offline."
|
||||
|
||||
; Configuration
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELDSET_BASIC="Basic Settings"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL="Terms of Service Menu Slug"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC="Enter the menu slug for your Terms of Service page (e.g., 'terms-of-service'). This page will be accessible even when the site is offline. The slug must match the menu item alias exactly."
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL="Offline-Accessible Menu Items"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC="Select one or more menu items that should remain accessible when the site is in offline mode. Hold Ctrl/Cmd to select multiple items."
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_LABEL="Include Child Menu Items"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_DESC="When enabled, child menu items under the selected items will also be accessible during offline mode. For example, selecting 'legal' will also allow access to 'legal/terms-of-service' and 'legal/privacy-policy'."
|
||||
|
||||
; Help
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_LABEL="How to Use This Plugin"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_DESC="<strong>Step 1:</strong> Create a Joomla article for your Terms of Service.<br/><strong>Step 2:</strong> Create a menu item pointing to that article.<br/><strong>Step 3:</strong> Set the menu item alias/slug (e.g., 'terms-of-service').<br/><strong>Step 4:</strong> Enter that same slug above.<br/><strong>Step 5:</strong> When your site goes offline, visitors can still access your-site.com/terms-of-service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_DESC="<strong>Step 1:</strong> Create articles for your legal pages (Terms of Service, Privacy Policy, etc.).<br/><strong>Step 2:</strong> Create menu items pointing to those articles.<br/><strong>Step 3:</strong> Select the menu items above (hold Ctrl/Cmd to select multiple).<br/><strong>Step 4:</strong> When your site goes offline, visitors can still access the selected pages.<br/><br/><em>Tip:</em> The dropdown shows the full URL path for each menu item (e.g., /legal/terms-of-service)."
|
||||
|
||||
; Warnings
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_SEF_WARNING="⚠ SEF URLs are disabled — path matching requires SEF. Itemid fallback is active."
|
||||
|
||||
; Errors
|
||||
PLG_SYSTEM_MOKOJOOMTOS_ERROR_LOADING_MENU_ITEMS="Error loading menu items: %s"
|
||||
|
||||
; Installation messages
|
||||
PLG_SYSTEM_MOKOJOOMTOS_INSTALL_SUCCESS="MokoJoomTOS Plugin installed successfully!"
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Moko Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when site is offline"
|
||||
|
||||
@@ -3,17 +3,23 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Moko Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when the site is in offline mode. Simply configure the menu slug (e.g., 'terms-of-service') and that page will remain accessible even when the site is offline."
|
||||
|
||||
; Configuration
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELDSET_BASIC="Basic Settings"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL="Terms of Service Menu Slug"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC="Enter the menu slug for your Terms of Service page (e.g., 'terms-of-service'). This page will be accessible even when the site is offline. The slug must match the menu item alias exactly."
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL="Offline-Accessible Menu Items"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC="Select one or more menu items that should remain accessible when the site is in offline mode. Hold Ctrl/Cmd to select multiple items."
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_LABEL="Include Child Menu Items"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_DESC="When enabled, child menu items under the selected items will also be accessible during offline mode. For example, selecting 'legal' will also allow access to 'legal/terms-of-service' and 'legal/privacy-policy'."
|
||||
|
||||
; Help
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_LABEL="How to Use This Plugin"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_DESC="<strong>Step 1:</strong> Create a Joomla article for your Terms of Service.<br/><strong>Step 2:</strong> Create a menu item pointing to that article.<br/><strong>Step 3:</strong> Set the menu item alias/slug (e.g., 'terms-of-service').<br/><strong>Step 4:</strong> Enter that same slug above.<br/><strong>Step 5:</strong> When your site goes offline, visitors can still access yoursite.com/terms-of-service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_DESC="<strong>Step 1:</strong> Create articles for your legal pages (Terms of Service, Privacy Policy, etc.).<br/><strong>Step 2:</strong> Create menu items pointing to those articles.<br/><strong>Step 3:</strong> Select the menu items above (hold Ctrl/Cmd to select multiple).<br/><strong>Step 4:</strong> When your site goes offline, visitors can still access the selected pages.<br/><br/><em>Tip:</em> The dropdown shows the full URL path for each menu item (e.g., /legal/terms-of-service)."
|
||||
|
||||
; Errors
|
||||
PLG_SYSTEM_MOKOJOOMTOS_ERROR_LOADING_MENU_ITEMS="Error loading menu items: %s"
|
||||
|
||||
; Installation messages
|
||||
PLG_SYSTEM_MOKOJOOMTOS_INSTALL_SUCCESS="MokoJoomTOS Plugin installed successfully!"
|
||||
|
||||
@@ -3,17 +3,23 @@
|
||||
; License GNU General Public License version 3 or later; see LICENSE
|
||||
; Note: All ini files need to be saved as UTF-8
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Offline Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS="System - Moko Terms of Service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION="Allows Terms of Service to be accessible via menu slug when the site is in offline mode. Simply configure the menu slug (e.g., 'terms-of-service') and that page will remain accessible even when the site is offline."
|
||||
|
||||
; Configuration
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELDSET_BASIC="Basic Settings"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL="Terms of Service Menu Slug"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC="Enter the menu slug for your Terms of Service page (e.g., 'terms-of-service'). This page will be accessible even when the site is offline. The slug must match the menu item alias exactly."
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL="Offline-Accessible Menu Items"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC="Select one or more menu items that should remain accessible when the site is in offline mode. Hold Ctrl/Cmd to select multiple items."
|
||||
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_LABEL="Include Child Menu Items"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_DESC="When enabled, child menu items under the selected items will also be accessible during offline mode. For example, selecting 'legal' will also allow access to 'legal/terms-of-service' and 'legal/privacy-policy'."
|
||||
|
||||
; Help
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_LABEL="How to Use This Plugin"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_DESC="<strong>Step 1:</strong> Create a Joomla article for your Terms of Service.<br/><strong>Step 2:</strong> Create a menu item pointing to that article.<br/><strong>Step 3:</strong> Set the menu item alias/slug (e.g., 'terms-of-service').<br/><strong>Step 4:</strong> Enter that same slug above.<br/><strong>Step 5:</strong> When your site goes offline, visitors can still access yoursite.com/terms-of-service"
|
||||
PLG_SYSTEM_MOKOJOOMTOS_HELP_DESC="<strong>Step 1:</strong> Create articles for your legal pages (Terms of Service, Privacy Policy, etc.).<br/><strong>Step 2:</strong> Create menu items pointing to those articles.<br/><strong>Step 3:</strong> Select the menu items above (hold Ctrl/Cmd to select multiple).<br/><strong>Step 4:</strong> When your site goes offline, visitors can still access the selected pages.<br/><br/><em>Tip:</em> The dropdown shows the full URL path for each menu item (e.g., /legal/terms-of-service)."
|
||||
|
||||
; Errors
|
||||
PLG_SYSTEM_MOKOJOOMTOS_ERROR_LOADING_MENU_ITEMS="Error loading menu items: %s"
|
||||
|
||||
; Installation messages
|
||||
PLG_SYSTEM_MOKOJOOMTOS_INSTALL_SUCCESS="MokoJoomTOS Plugin installed successfully!"
|
||||
|
||||
+4
-85
@@ -8,98 +8,17 @@
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
|
||||
/**
|
||||
* MokoJoomTOS Offline Mode Bypass Plugin
|
||||
* MokoJoomTOS Legacy Entry Point
|
||||
*
|
||||
* Allows Terms of Service menu to be accessible via slug when the site
|
||||
* is in offline mode.
|
||||
* This file is required by Joomla's plugin loader (<filename plugin="mokojoomtos">)
|
||||
* but is NOT executed under Joomla 5's DI container. The actual plugin logic lives
|
||||
* in src/Extension/MokoJoomTOS.php, bootstrapped via services/provider.php.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class PlgSystemMokojoomtos extends CMSPlugin
|
||||
{
|
||||
/**
|
||||
* Load the language file on instantiation.
|
||||
*
|
||||
* @var boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $autoloadLanguage = true;
|
||||
|
||||
/**
|
||||
* Application object
|
||||
*
|
||||
* @var \Joomla\CMS\Application\CMSApplication
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* After route event handler
|
||||
*
|
||||
* Checks if the current request is for the Terms of Service slug and if
|
||||
* the site is in offline mode. If both conditions are met, temporarily
|
||||
* disables offline mode and sets component-only view for this request.
|
||||
*
|
||||
* This event fires after routing but before template selection, making it
|
||||
* the correct place to set tmpl=component to prevent template chrome loading.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 03.08.04
|
||||
*/
|
||||
public function onAfterRoute()
|
||||
{
|
||||
// Only process for site application
|
||||
if (!$this->app->isClient('site'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the global configuration
|
||||
$config = $this->app->getConfig();
|
||||
|
||||
// Only proceed if site is offline
|
||||
if (!$config->get('offline'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the configured Terms of Service slug
|
||||
$tosSlug = trim($this->params->get('tos_slug', 'terms-of-service'));
|
||||
|
||||
if (empty($tosSlug))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current URI path
|
||||
$uri = Uri::getInstance();
|
||||
$path = trim($uri->getPath(), '/');
|
||||
|
||||
// Remove the base path if present
|
||||
$base = trim(Uri::base(true), '/');
|
||||
if (!empty($base) && strpos($path, $base) === 0)
|
||||
{
|
||||
$path = trim(substr($path, strlen($base)), '/');
|
||||
}
|
||||
|
||||
// Check if the path matches the Terms of Service slug
|
||||
if ($path === $tosSlug || strpos($path, $tosSlug . '/') === 0)
|
||||
{
|
||||
// Temporarily disable offline mode for this request
|
||||
$config->set('offline', 0);
|
||||
|
||||
// Set component-only view (no template chrome)
|
||||
$input = $this->app->input;
|
||||
$input->set('tmpl', 'component');
|
||||
|
||||
// Also set in GET superglobal to ensure recognition
|
||||
$_GET['tmpl'] = 'component';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-10
@@ -25,20 +25,20 @@
|
||||
DEFGROUP: MokoJoomTOS
|
||||
INGROUP: plg_system_mokojoomtos
|
||||
PATH: src/mokojoomtos.xml
|
||||
VERSION: 03.08.04
|
||||
VERSION: 04.00.00
|
||||
BRIEF: Plugin manifest XML file for MokoJoomTOS system plugin
|
||||
=========================================================================
|
||||
-->
|
||||
<extension type="plugin" group="system" method="upgrade">
|
||||
<name>plg_system_mokojoomtos</name>
|
||||
<name>System - Moko Terms of Service</name>
|
||||
<author>Moko Consulting</author>
|
||||
<creationDate>2026-01-01</creationDate>
|
||||
<creationDate>2026-05-16</creationDate>
|
||||
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
|
||||
<license>GNU General Public License version 3 or later; see LICENSE</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>03.08.04</version>
|
||||
<description>PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION</description>
|
||||
<version>04.02.01</version>
|
||||
<description>Allows Terms of Service to be accessible via menu slug when site is offline</description>
|
||||
|
||||
<namespace path="src">Joomla\Plugin\System\MokoJoomTOS</namespace>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<files>
|
||||
<filename plugin="mokojoomtos">mokojoomtos.php</filename>
|
||||
<folder>src</folder>
|
||||
<folder>services</folder>
|
||||
<folder>language</folder>
|
||||
<folder>administrator</folder>
|
||||
</files>
|
||||
@@ -68,9 +69,20 @@
|
||||
type="menuslug"
|
||||
label="PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL"
|
||||
description="PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC"
|
||||
default="terms-of-service"
|
||||
required="true"
|
||||
multiple="true"
|
||||
/>
|
||||
|
||||
<field
|
||||
name="include_children"
|
||||
type="radio"
|
||||
label="PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_LABEL"
|
||||
description="PLG_SYSTEM_MOKOJOOMTOS_FIELD_INCLUDE_CHILDREN_DESC"
|
||||
default="1"
|
||||
class="btn-group btn-group-yesno"
|
||||
>
|
||||
<option value="1">JYES</option>
|
||||
<option value="0">JNO</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="help_spacer"
|
||||
@@ -84,8 +96,6 @@
|
||||
</config>
|
||||
|
||||
<updateservers>
|
||||
<server type="extension" priority="1" name="MokoJoomTOS Update Server (Gitea)">
|
||||
https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/raw/branch/main/updates.xml
|
||||
</server>
|
||||
<server type="extension" name="MokoJoomTOS Plugin">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/raw/branch/main/updates.xml</server>
|
||||
</updateservers>
|
||||
</extension>
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<extension type="plugin" group="system" method="upgrade">
|
||||
<name>plg_system_mokojoomtos</name>
|
||||
<author>Moko Consulting</author>
|
||||
<creationDate>2026-01-01</creationDate>
|
||||
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
|
||||
<license>GNU General Public License version 3 or later; see LICENSE</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>03.08.04</version>
|
||||
<description>PLG_SYSTEM_MOKOJOOMTOS_XML_DESCRIPTION</description>
|
||||
|
||||
<namespace path="src">Joomla\Plugin\System\MokoJoomTOS</namespace>
|
||||
|
||||
<scriptfile>script.php</scriptfile>
|
||||
|
||||
<files>
|
||||
<filename plugin="mokojoomtos">mokojoomtos.php</filename>
|
||||
<folder>src</folder>
|
||||
<folder>language</folder>
|
||||
<folder>administrator</folder>
|
||||
</files>
|
||||
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_system_mokojoomtos.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_system_mokojoomtos.sys.ini</language>
|
||||
<language tag="en-US">language/en-US/plg_system_mokojoomtos.ini</language>
|
||||
<language tag="en-US">language/en-US/plg_system_mokojoomtos.sys.ini</language>
|
||||
<language tag="en-GB" folder="administrator">administrator/language/en-GB/plg_system_mokojoomtos.ini</language>
|
||||
<language tag="en-GB" folder="administrator">administrator/language/en-GB/plg_system_mokojoomtos.sys.ini</language>
|
||||
<language tag="en-US" folder="administrator">administrator/language/en-US/plg_system_mokojoomtos.ini</language>
|
||||
<language tag="en-US" folder="administrator">administrator/language/en-US/plg_system_mokojoomtos.sys.ini</language>
|
||||
</languages>
|
||||
|
||||
<config>
|
||||
<fields name="params" addfieldprefix="Joomla\Plugin\System\MokoJoomTOS\Field">
|
||||
<fieldset name="basic" label="PLG_SYSTEM_MOKOJOOMTOS_FIELDSET_BASIC">
|
||||
<field
|
||||
name="tos_slug"
|
||||
type="menuslug"
|
||||
label="PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_LABEL"
|
||||
description="PLG_SYSTEM_MOKOJOOMTOS_FIELD_TOS_SLUG_DESC"
|
||||
default="terms-of-service"
|
||||
required="true"
|
||||
/>
|
||||
|
||||
<field
|
||||
name="help_spacer"
|
||||
type="spacer"
|
||||
label="PLG_SYSTEM_MOKOJOOMTOS_HELP_LABEL"
|
||||
description="PLG_SYSTEM_MOKOJOOMTOS_HELP_DESC"
|
||||
class="alert alert-info"
|
||||
/>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
|
||||
<updateservers>
|
||||
<server type="extension" priority="1" name="MokoJoomTOS Update Server (Gitea)">
|
||||
https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/raw/branch/main/updates.xml
|
||||
</server>
|
||||
<server type="extension" priority="2" name="MokoJoomTOS Update Server (GitHub)">
|
||||
https://raw.githubusercontent.com/mokoconsulting-tech/MokoJoomTOS/main/updates.xml
|
||||
</server>
|
||||
</updateservers>
|
||||
</extension>
|
||||
+375
-346
@@ -13,369 +13,398 @@ use Joomla\CMS\Installer\InstallerAdapter;
|
||||
use Joomla\CMS\Installer\InstallerScript;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Log\Log;
|
||||
use Joomla\CMS\Table\Table;
|
||||
|
||||
/**
|
||||
* Installation script for MokoJoomTOS Offline Plugin
|
||||
* Installation script for MokoJoomTOS Plugin
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class PlgSystemMokojoomtosOfflineInstallerScript extends InstallerScript
|
||||
class PlgSystemMokojoomtosInstallerScript extends InstallerScript
|
||||
{
|
||||
/**
|
||||
* Minimum Joomla version required to install the plugin
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $minimumJoomla = '4.0.0';
|
||||
/**
|
||||
* Minimum Joomla version required to install the plugin
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $minimumJoomla = '4.0.0';
|
||||
|
||||
/**
|
||||
* Minimum PHP version required to install the plugin
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $minimumPhp = '7.4.0';
|
||||
/**
|
||||
* Minimum PHP version required to install the plugin
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $minimumPhp = '7.4.0';
|
||||
|
||||
/**
|
||||
* Extension type (used by parent class)
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $extension = 'plg_system_mokojoomtos';
|
||||
/**
|
||||
* Extension type (used by parent class)
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $extension = 'plg_system_mokojoomtos';
|
||||
|
||||
/**
|
||||
* Function called before plugin installation/update/uninstall
|
||||
*
|
||||
* @param string $type Installation type (install, update, discover_install)
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return boolean True on success
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function preflight($type, $parent)
|
||||
{
|
||||
// Check minimum requirements
|
||||
if (!parent::preflight($type, $parent)) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Function called before plugin installation/update/uninstall
|
||||
*
|
||||
* @param string $type Installation type (install, update, discover_install)
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return boolean True on success
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function preflight($type, $parent)
|
||||
{
|
||||
if (!parent::preflight($type, $parent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called after plugin installation
|
||||
*
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function install($parent)
|
||||
{
|
||||
echo '<p>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_INSTALL_SUCCESS') . '</p>';
|
||||
}
|
||||
/**
|
||||
* Function called after plugin installation
|
||||
*
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function install($parent)
|
||||
{
|
||||
echo '<p>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_INSTALL_SUCCESS') . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called after plugin update
|
||||
*
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function update($parent)
|
||||
{
|
||||
echo '<p>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_UPDATE_SUCCESS') . '</p>';
|
||||
}
|
||||
/**
|
||||
* Function called after plugin update
|
||||
*
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function update($parent)
|
||||
{
|
||||
echo '<p>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_UPDATE_SUCCESS') . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called after plugin uninstallation
|
||||
*
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function uninstall($parent)
|
||||
{
|
||||
echo '<p>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_UNINSTALL_SUCCESS') . '</p>';
|
||||
}
|
||||
/**
|
||||
* Function called after plugin uninstallation
|
||||
*
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function uninstall($parent)
|
||||
{
|
||||
echo '<p>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_UNINSTALL_SUCCESS') . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called after extension installation/update/discover_install
|
||||
*
|
||||
* @param string $type Installation type (install, update, discover_install)
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function postflight($type, $parent)
|
||||
{
|
||||
if ($type === 'install' || $type === 'discover_install') {
|
||||
// Create Terms of Service article and menu item
|
||||
$this->createTermsOfServiceSetup();
|
||||
|
||||
echo '<div class="alert alert-success">';
|
||||
echo '<h4>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_POSTINSTALL_TITLE') . '</h4>';
|
||||
echo '<p>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_POSTINSTALL_DESC') . '</p>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Function called after extension installation/update/discover_install
|
||||
*
|
||||
* Fixes #89: enablePlugin() is now called unconditionally for both
|
||||
* install and upgrade paths.
|
||||
* Fixes #92: enablePlugin() is called on upgrade to re-enable if disabled.
|
||||
*
|
||||
* @param string $type Installation type (install, update, discover_install)
|
||||
* @param InstallerAdapter $parent Parent installer adapter
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function postflight($type, $parent)
|
||||
{
|
||||
// Always enable the plugin on install or upgrade
|
||||
$this->enablePlugin();
|
||||
|
||||
/**
|
||||
* Create Terms of Service article and menu item
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createTermsOfServiceSetup()
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// Check if Terms of Service article already exists
|
||||
$query = $db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__content'))
|
||||
->where($db->quoteName('alias') . ' = ' . $db->quote('terms-of-service'));
|
||||
$db->setQuery($query);
|
||||
$articleId = $db->loadResult();
|
||||
|
||||
// Create article if it doesn't exist
|
||||
if (!$articleId) {
|
||||
$articleId = $this->createTermsArticle();
|
||||
}
|
||||
|
||||
if ($articleId) {
|
||||
// Check if menu item already exists
|
||||
$query = $db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__menu'))
|
||||
->where($db->quoteName('alias') . ' = ' . $db->quote('terms-of-service'))
|
||||
->where($db->quoteName('published') . ' >= 0');
|
||||
$db->setQuery($query);
|
||||
$menuId = $db->loadResult();
|
||||
|
||||
// Create menu item if it doesn't exist
|
||||
if (!$menuId) {
|
||||
$this->createTermsMenuItem($articleId);
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::add('Error creating Terms of Service setup: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
}
|
||||
if ($type === 'install' || $type === 'discover_install') {
|
||||
$this->createTermsOfServiceSetup();
|
||||
|
||||
/**
|
||||
* Create Terms of Service article
|
||||
*
|
||||
* @return int|null Article ID or null on failure
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createTermsArticle()
|
||||
{
|
||||
try {
|
||||
Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_content/tables');
|
||||
$table = Table::getInstance('Content', 'Joomla\\Component\\Content\\Administrator\\Table\\');
|
||||
|
||||
if (!$table) {
|
||||
Log::add('Failed to get Content table instance', Log::WARNING, 'jerror');
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'title' => 'Terms of Service',
|
||||
'alias' => 'terms-of-service',
|
||||
'introtext' => '<h2>Terms of Service</h2><p>Welcome to our Terms of Service page.</p><p>This page will remain accessible even when the site is in offline/maintenance mode.</p>',
|
||||
'fulltext' => '',
|
||||
'state' => 1,
|
||||
'catid' => 2, // Uncategorised
|
||||
'created' => Factory::getDate()->toSql(),
|
||||
'created_by' => 0, // System-created content
|
||||
'language' => '*',
|
||||
'access' => 1, // Public
|
||||
];
|
||||
|
||||
// Bind data to table object first
|
||||
if (!$table->bind($data)) {
|
||||
Log::add('Failed to bind data to Content table: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check data validity
|
||||
if (!$table->check()) {
|
||||
Log::add('Content table check failed: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Save the table
|
||||
if (!$table->store()) {
|
||||
Log::add('Failed to store Content table: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return null;
|
||||
}
|
||||
|
||||
echo '<p class="alert alert-info">✓ Created Terms of Service article</p>';
|
||||
return $table->id;
|
||||
} catch (Exception $e) {
|
||||
Log::add('Error creating Terms of Service article: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
echo '<div class="alert alert-success">';
|
||||
echo '<h4>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_POSTINSTALL_TITLE') . '</h4>';
|
||||
echo '<p>' . Text::_('PLG_SYSTEM_MOKOJOOMTOS_POSTINSTALL_DESC') . '</p>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Terms of Service menu item
|
||||
*
|
||||
* @param int $articleId The article ID to link to
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createTermsMenuItem($articleId)
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// Check if "Legal" menu type exists
|
||||
$query = $db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__menu_types'))
|
||||
->where($db->quoteName('menutype') . ' = ' . $db->quote('legal'));
|
||||
$db->setQuery($query);
|
||||
$legalMenuExists = $db->loadResult();
|
||||
|
||||
// Create "Legal" menu type if it doesn't exist
|
||||
if (!$legalMenuExists) {
|
||||
$this->createLegalMenuType();
|
||||
}
|
||||
|
||||
// Get com_content component ID dynamically
|
||||
$query = $db->getQuery(true)
|
||||
->select('extension_id')
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('component'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('com_content'));
|
||||
$db->setQuery($query);
|
||||
$componentId = (int) $db->loadResult() ?: 22; // Fallback to 22 if query fails
|
||||
|
||||
Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_menus/tables');
|
||||
$table = Table::getInstance('Menu', 'Joomla\\Component\\Menus\\Administrator\\Table\\');
|
||||
|
||||
if (!$table) {
|
||||
Log::add('Failed to get Menu table instance', Log::WARNING, 'jerror');
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'menutype' => 'legal',
|
||||
'title' => 'Terms of Service',
|
||||
'alias' => 'terms-of-service',
|
||||
'link' => 'index.php?option=com_content&view=article&id=' . $articleId,
|
||||
'type' => 'component',
|
||||
'published' => 1,
|
||||
'parent_id' => 1,
|
||||
'component_id' => $componentId,
|
||||
'level' => 1,
|
||||
'language' => '*',
|
||||
'access' => 1, // Public
|
||||
'params' => '{"show_title":"1","link_titles":"0","show_intro":"","info_block_position":"","show_category":"0","link_category":"0","show_parent_category":"0","link_parent_category":"0","show_author":"0","link_author":"0","show_create_date":"0","show_modify_date":"0","show_publish_date":"0","show_item_navigation":"0","show_icons":"0","show_print_icon":"0","show_email_icon":"0","show_hits":"0","show_noauth":"0","urls_position":"","menu-anchor_title":"","menu-anchor_css":"","menu_image":"","menu_text":1,"page_title":"","show_page_heading":0,"page_heading":"","pageclass_sfx":"","menu-meta_description":"","menu-meta_keywords":"","robots":"","secure":0}',
|
||||
];
|
||||
|
||||
// Set the location in the menu tree
|
||||
$table->setLocation($data['parent_id'], 'last-child');
|
||||
|
||||
// Bind data to table object
|
||||
if (!$table->bind($data)) {
|
||||
Log::add('Failed to bind data to Menu table: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check data validity
|
||||
if (!$table->check()) {
|
||||
Log::add('Menu table check failed: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the menu item
|
||||
if (!$table->store()) {
|
||||
Log::add('Failed to store Menu table: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<p class="alert alert-info">✓ Created Terms of Service menu item in Legal menu with slug: terms-of-service</p>';
|
||||
|
||||
// Enable the plugin
|
||||
$this->enablePlugin();
|
||||
} catch (Exception $e) {
|
||||
Log::add('Error creating Terms of Service menu item: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create Terms of Service article and menu item
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createTermsOfServiceSetup()
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
|
||||
/**
|
||||
* Create Legal menu type
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createLegalMenuType()
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// Insert the Legal menu type
|
||||
$query = $db->getQuery(true)
|
||||
->insert($db->quoteName('#__menu_types'))
|
||||
->columns($db->quoteName(['menutype', 'title', 'description']))
|
||||
->values(
|
||||
$db->quote('legal') . ', ' .
|
||||
$db->quote('Legal') . ', ' .
|
||||
$db->quote('Legal documents and policies menu')
|
||||
);
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
|
||||
echo '<p class="alert alert-info">✓ Created Legal menu type</p>';
|
||||
} catch (Exception $e) {
|
||||
Log::add('Error creating Legal menu type: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
}
|
||||
// Check if Terms of Service article already exists (with catid filter)
|
||||
$query = $db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__content'))
|
||||
->where($db->quoteName('alias') . ' = ' . $db->quote('terms-of-service'))
|
||||
->where($db->quoteName('state') . ' >= 0');
|
||||
$db->setQuery($query);
|
||||
$articleId = $db->loadResult();
|
||||
|
||||
/**
|
||||
* Enable the plugin after installation
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function enablePlugin()
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__extensions'))
|
||||
->set($db->quoteName('enabled') . ' = 1')
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('mokojoomtos'));
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
|
||||
echo '<p class="alert alert-success">✓ Plugin enabled automatically</p>';
|
||||
} catch (Exception $e) {
|
||||
Log::add('Error enabling plugin: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
}
|
||||
if (!$articleId) {
|
||||
$articleId = $this->createTermsArticle();
|
||||
}
|
||||
|
||||
if ($articleId) {
|
||||
$query = $db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__menu'))
|
||||
->where($db->quoteName('alias') . ' = ' . $db->quote('terms-of-service'))
|
||||
->where($db->quoteName('published') . ' >= 0');
|
||||
$db->setQuery($query);
|
||||
$menuId = $db->loadResult();
|
||||
|
||||
if (!$menuId) {
|
||||
$this->createTermsMenuItem($articleId);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::add('Error creating Terms of Service setup: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Terms of Service article using Joomla 5 MVCFactory
|
||||
*
|
||||
* Fixes #90: Uses bootComponent()->getMVCFactory() instead of
|
||||
* the removed Table::addIncludePath() / Table::getInstance().
|
||||
* Fixes #94: Includes params, metadata, and attribs defaults.
|
||||
*
|
||||
* @return int|null Article ID or null on failure
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createTermsArticle()
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
$app = Factory::getApplication();
|
||||
|
||||
// Get content table via MVCFactory (Joomla 4/5 compatible)
|
||||
$table = $app->bootComponent('com_content')
|
||||
->getMVCFactory()
|
||||
->createTable('Article', 'Administrator');
|
||||
|
||||
if (!$table) {
|
||||
Log::add('Failed to get Content table instance', Log::WARNING, 'jerror');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get Uncategorised category ID dynamically
|
||||
$query = $db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__categories'))
|
||||
->where($db->quoteName('extension') . ' = ' . $db->quote('com_content'))
|
||||
->where($db->quoteName('alias') . ' = ' . $db->quote('uncategorised'))
|
||||
->where($db->quoteName('published') . ' = 1');
|
||||
$db->setQuery($query);
|
||||
$catId = (int) $db->loadResult();
|
||||
|
||||
if (!$catId) {
|
||||
Log::add('Could not find Uncategorised category for com_content', Log::WARNING, 'jerror');
|
||||
return null;
|
||||
}
|
||||
|
||||
$createdBy = $app->getIdentity()->id ?: 0;
|
||||
|
||||
$data = [
|
||||
'title' => 'Terms of Service',
|
||||
'alias' => 'terms-of-service',
|
||||
'introtext' => '<h2>Terms of Service</h2><p>Welcome to our Terms of Service page.</p><p>This page will remain accessible even when the site is in offline/maintenance mode.</p>',
|
||||
'fulltext' => '',
|
||||
'state' => 1,
|
||||
'catid' => $catId,
|
||||
'created' => Factory::getDate()->toSql(),
|
||||
'created_by' => $createdBy,
|
||||
'language' => '*',
|
||||
'access' => 1,
|
||||
'params' => '{}',
|
||||
'metadata' => '{"robots":"","author":"","rights":""}',
|
||||
'attribs' => '{}',
|
||||
];
|
||||
|
||||
if (!$table->bind($data)) {
|
||||
Log::add('Failed to bind data to Content table: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$table->check()) {
|
||||
Log::add('Content table check failed: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$table->store()) {
|
||||
Log::add('Failed to store Content table: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return null;
|
||||
}
|
||||
|
||||
echo '<p class="alert alert-info">Created Terms of Service article</p>';
|
||||
return $table->id;
|
||||
} catch (\Exception $e) {
|
||||
Log::add('Error creating Terms of Service article: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Terms of Service menu item using Joomla 5 MVCFactory
|
||||
*
|
||||
* Fixes #90: Uses bootComponent()->getMVCFactory() instead of
|
||||
* the removed Table::addIncludePath() / Table::getInstance().
|
||||
*
|
||||
* @param int $articleId The article ID to link to
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createTermsMenuItem($articleId)
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
$app = Factory::getApplication();
|
||||
|
||||
// Check if "Legal" menu type exists
|
||||
$query = $db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__menu_types'))
|
||||
->where($db->quoteName('menutype') . ' = ' . $db->quote('legal'));
|
||||
$db->setQuery($query);
|
||||
$legalMenuExists = $db->loadResult();
|
||||
|
||||
if (!$legalMenuExists) {
|
||||
$this->createLegalMenuType();
|
||||
}
|
||||
|
||||
// Get com_content component ID dynamically
|
||||
$query = $db->getQuery(true)
|
||||
->select('extension_id')
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('component'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('com_content'));
|
||||
$db->setQuery($query);
|
||||
$componentId = (int) $db->loadResult();
|
||||
|
||||
if (!$componentId) {
|
||||
Log::add('Could not determine com_content component ID', Log::WARNING, 'jerror');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get menu table via MVCFactory (Joomla 4/5 compatible)
|
||||
$table = $app->bootComponent('com_menus')
|
||||
->getMVCFactory()
|
||||
->createTable('Menu', 'Administrator');
|
||||
|
||||
if (!$table) {
|
||||
Log::add('Failed to get Menu table instance', Log::WARNING, 'jerror');
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'menutype' => 'legal',
|
||||
'title' => 'Terms of Service',
|
||||
'alias' => 'terms-of-service',
|
||||
'link' => 'index.php?option=com_content&view=article&id=' . $articleId,
|
||||
'type' => 'component',
|
||||
'published' => 1,
|
||||
'parent_id' => 1,
|
||||
'component_id' => $componentId,
|
||||
'level' => 1,
|
||||
'language' => '*',
|
||||
'access' => 1,
|
||||
'params' => '{"show_title":"1","link_titles":"0","show_intro":"","info_block_position":"","show_category":"0","link_category":"0","show_parent_category":"0","link_parent_category":"0","show_author":"0","link_author":"0","show_create_date":"0","show_modify_date":"0","show_publish_date":"0","show_item_navigation":"0","show_icons":"0","show_print_icon":"0","show_email_icon":"0","show_hits":"0","show_noauth":"0","urls_position":"","menu-anchor_title":"","menu-anchor_css":"","menu_image":"","menu_text":1,"page_title":"","show_page_heading":0,"page_heading":"","pageclass_sfx":"","menu-meta_description":"","menu-meta_keywords":"","robots":"","secure":0}',
|
||||
];
|
||||
|
||||
$table->setLocation($data['parent_id'], 'last-child');
|
||||
|
||||
if (!$table->bind($data)) {
|
||||
Log::add('Failed to bind data to Menu table: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$table->check()) {
|
||||
Log::add('Menu table check failed: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$table->store()) {
|
||||
Log::add('Failed to store Menu table: ' . $table->getError(), Log::WARNING, 'jerror');
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<p class="alert alert-info">Created Terms of Service menu item in Legal menu</p>';
|
||||
} catch (\Exception $e) {
|
||||
Log::add('Error creating Terms of Service menu item: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Legal menu type
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createLegalMenuType()
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->insert($db->quoteName('#__menu_types'))
|
||||
->columns($db->quoteName(['menutype', 'title', 'description']))
|
||||
->values(
|
||||
$db->quote('legal') . ', ' .
|
||||
$db->quote('Legal') . ', ' .
|
||||
$db->quote('Legal documents and policies menu')
|
||||
);
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
|
||||
echo '<p class="alert alert-info">Created Legal menu type</p>';
|
||||
} catch (\Exception $e) {
|
||||
// Duplicate key is expected if race condition — safe to ignore
|
||||
Log::add('Error creating Legal menu type: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the plugin after installation
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function enablePlugin()
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__extensions'))
|
||||
->set($db->quoteName('enabled') . ' = 1')
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('mokojoomtos'));
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
} catch (\Exception $e) {
|
||||
Log::add('Error enabling plugin: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* @package MokoJoomTOS
|
||||
* @subpackage plg_system_mokojoomtos
|
||||
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
||||
* @license GNU General Public License version 3 or later; see LICENSE
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\System\MokoJoomTOS\Extension\MokoJoomTOS;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 04.00.00
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new MokoJoomTOS(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('system', 'mokojoomtos')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -18,106 +18,227 @@ use Joomla\Event\SubscriberInterface;
|
||||
/**
|
||||
* MokoJoomTOS Offline Mode Bypass Plugin
|
||||
*
|
||||
* Allows Terms of Service menu to be accessible via slug when the site
|
||||
* Allows configured menu items to remain accessible when the site
|
||||
* is in offline mode.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class MokoJoomTOS extends CMSPlugin implements SubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Load the language file on instantiation.
|
||||
*
|
||||
* @var boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $autoloadLanguage = true;
|
||||
/**
|
||||
* Load the language file on instantiation.
|
||||
*
|
||||
* @var boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $autoloadLanguage = true;
|
||||
|
||||
/**
|
||||
* Application object
|
||||
*
|
||||
* @var \Joomla\CMS\Application\CMSApplication
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $app;
|
||||
/**
|
||||
* Returns an array of events this subscriber will listen to.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
'onAfterRoute' => 'onAfterRoute',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of events this subscriber will listen to.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
'onAfterRoute' => 'onAfterRoute',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* After route event handler
|
||||
*
|
||||
* Checks if the current request matches any configured menu slug and if
|
||||
* the site is in offline mode. If both conditions are met, temporarily
|
||||
* disables offline mode and sets component-only view for this request.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function onAfterRoute()
|
||||
{
|
||||
$app = $this->getApplication();
|
||||
|
||||
/**
|
||||
* After route event handler
|
||||
*
|
||||
* Checks if the current request is for the Terms of Service slug and if
|
||||
* the site is in offline mode. If both conditions are met, temporarily
|
||||
* disables offline mode and sets component-only view for this request.
|
||||
*
|
||||
* This event fires after routing but before template selection, making it
|
||||
* the correct place to set tmpl=component to prevent template chrome loading.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function onAfterRoute()
|
||||
{
|
||||
// Only process for site application
|
||||
if (!$this->app->isClient('site'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Only process for site application
|
||||
if (!$app->isClient('site'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the global configuration
|
||||
$config = $this->app->getConfig();
|
||||
|
||||
// Only proceed if site is offline
|
||||
if (!$config->get('offline'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
$config = $app->getConfig();
|
||||
|
||||
// Get the configured Terms of Service slug
|
||||
$tosSlug = trim($this->params->get('tos_slug', 'terms-of-service'));
|
||||
|
||||
if (empty($tosSlug))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Only proceed if site is offline
|
||||
if (!$config->get('offline'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current URI path
|
||||
$uri = Uri::getInstance();
|
||||
$path = trim($uri->getPath(), '/');
|
||||
|
||||
// Remove the base path if present
|
||||
$base = trim(Uri::base(true), '/');
|
||||
if (!empty($base) && strpos($path, $base) === 0)
|
||||
{
|
||||
$path = trim(substr($path, strlen($base)), '/');
|
||||
}
|
||||
// Get the configured slugs — cast to array to handle stdClass from Registry (#96)
|
||||
$slugs = $this->params->get('tos_slug', []);
|
||||
|
||||
// Check if the path matches the Terms of Service slug
|
||||
if ($path === $tosSlug || strpos($path, $tosSlug . '/') === 0)
|
||||
{
|
||||
// Temporarily disable offline mode for this request
|
||||
$config->set('offline', 0);
|
||||
|
||||
// Set component-only view (no template chrome)
|
||||
// This ensures clean display without full site template
|
||||
$input = $this->app->input;
|
||||
$input->set('tmpl', 'component');
|
||||
|
||||
// Also set it in the GET superglobal to ensure it's recognized
|
||||
$_GET['tmpl'] = 'component';
|
||||
}
|
||||
}
|
||||
// Handle legacy single-value string format
|
||||
if (is_string($slugs))
|
||||
{
|
||||
$slugs = array_filter([trim($slugs)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$slugs = (array) $slugs;
|
||||
}
|
||||
|
||||
if (empty($slugs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$includeChildren = (int) $this->params->get('include_children', 1);
|
||||
|
||||
// Try SEF path matching first, then fall back to Itemid matching (#91)
|
||||
if ($this->matchByPath($slugs, $config, $app, $includeChildren))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->matchByItemId($slugs, $config, $app, $includeChildren);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the current request path against configured slugs (SEF mode)
|
||||
*
|
||||
* @param array $slugs Configured slug values
|
||||
* @param object $config Joomla configuration object
|
||||
* @param object $app Application instance
|
||||
* @param integer $includeChildren Whether to include child menu items
|
||||
*
|
||||
* @return boolean True if a match was found and offline mode was bypassed
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
private function matchByPath(array $slugs, $config, $app, int $includeChildren = 1): bool
|
||||
{
|
||||
$uri = Uri::getInstance();
|
||||
$path = urldecode(trim($uri->getPath(), '/'));
|
||||
|
||||
// Remove the base path if present (subdirectory installs)
|
||||
$base = trim(Uri::base(true), '/');
|
||||
if (!empty($base) && strpos($path, $base) === 0)
|
||||
{
|
||||
$path = trim(substr($path, strlen($base)), '/');
|
||||
}
|
||||
|
||||
// Skip if path is empty or just index.php (non-SEF)
|
||||
if (empty($path) || $path === 'index.php')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($slugs as $slug)
|
||||
{
|
||||
$slug = trim((string) $slug);
|
||||
if (empty($slug))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$isMatch = ($path === $slug)
|
||||
|| ($includeChildren && strpos($path, $slug . '/') === 0);
|
||||
|
||||
if ($isMatch)
|
||||
{
|
||||
$this->bypassOffline($config, $app);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the current request Itemid against menu items for configured slugs (non-SEF fallback)
|
||||
*
|
||||
* When SEF URLs are disabled, the path is just index.php so we match by
|
||||
* checking if the requested Itemid belongs to a menu item whose path
|
||||
* matches a configured slug.
|
||||
*
|
||||
* @param array $slugs Configured slug values
|
||||
* @param object $config Joomla configuration object
|
||||
* @param object $app Application instance
|
||||
* @param integer $includeChildren Whether to include child menu items
|
||||
*
|
||||
* @return boolean True if a match was found and offline mode was bypassed
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
private function matchByItemId(array $slugs, $config, $app, int $includeChildren = 1): bool
|
||||
{
|
||||
$itemId = (int) $app->input->getInt('Itemid', 0);
|
||||
|
||||
if (!$itemId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('path'))
|
||||
->from($db->quoteName('#__menu'))
|
||||
->where($db->quoteName('id') . ' = ' . $itemId)
|
||||
->where($db->quoteName('published') . ' = 1')
|
||||
->where($db->quoteName('client_id') . ' = 0');
|
||||
$db->setQuery($query);
|
||||
$menuPath = $db->loadResult();
|
||||
|
||||
if (!$menuPath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$menuPath = trim($menuPath, '/');
|
||||
|
||||
foreach ($slugs as $slug)
|
||||
{
|
||||
$slug = trim((string) $slug);
|
||||
if (empty($slug))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$isMatch = ($menuPath === $slug)
|
||||
|| ($includeChildren && strpos($menuPath, $slug . '/') === 0);
|
||||
|
||||
if ($isMatch)
|
||||
{
|
||||
$this->bypassOffline($config, $app);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Silently fail — do not bypass offline mode on error
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bypass offline mode and set component-only view for this request
|
||||
*
|
||||
* @param object $config Joomla configuration object
|
||||
* @param object $app Application instance
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
private function bypassOffline($config, $app): void
|
||||
{
|
||||
$config->set('offline', 0);
|
||||
$app->input->set('tmpl', 'component');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,83 +17,99 @@ use Joomla\CMS\Language\Text;
|
||||
/**
|
||||
* Menu Slug Field
|
||||
*
|
||||
* Provides a dropdown list of menu items with their aliases (slugs)
|
||||
* Provides a multi-select dropdown of menu items with their full route paths
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class MenuslugField extends ListField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $type = 'Menuslug';
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $type = 'Menuslug';
|
||||
|
||||
/**
|
||||
* Method to get the field options.
|
||||
*
|
||||
* @return array The field option objects.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
$options = parent::getOptions();
|
||||
/**
|
||||
* Method to get the field options.
|
||||
*
|
||||
* @return array The field option objects.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
$options = parent::getOptions();
|
||||
|
||||
try
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName(['alias', 'title', 'menutype']))
|
||||
->from($db->quoteName('#__menu'))
|
||||
->where($db->quoteName('published') . ' = 1')
|
||||
->where($db->quoteName('client_id') . ' = 0')
|
||||
->where($db->quoteName('alias') . ' != ' . $db->quote(''))
|
||||
->order($db->quoteName('menutype') . ', ' . $db->quoteName('title'));
|
||||
// Warn if SEF URLs are disabled (#97)
|
||||
try
|
||||
{
|
||||
$sef = Factory::getApplication()->get('sef', true);
|
||||
if (!$sef)
|
||||
{
|
||||
$options[] = (object) [
|
||||
'value' => '',
|
||||
'text' => Text::_('PLG_SYSTEM_MOKOJOOMTOS_FIELD_SEF_WARNING'),
|
||||
'disabled' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Ignore — field still works without the warning
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
$menuItems = $db->loadObjectList();
|
||||
try
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName(['path', 'alias', 'title', 'menutype']))
|
||||
->from($db->quoteName('#__menu'))
|
||||
->where($db->quoteName('published') . ' = 1')
|
||||
->where($db->quoteName('client_id') . ' = 0')
|
||||
->where($db->quoteName('alias') . ' != ' . $db->quote(''))
|
||||
->order($db->quoteName('menutype') . ', ' . $db->quoteName('title'));
|
||||
|
||||
if ($menuItems)
|
||||
{
|
||||
$lastMenuType = '';
|
||||
|
||||
foreach ($menuItems as $item)
|
||||
{
|
||||
// Add menu type separator for better organization
|
||||
if ($item->menutype !== $lastMenuType)
|
||||
{
|
||||
if ($lastMenuType !== '')
|
||||
{
|
||||
// Add a separator between menu types
|
||||
$options[] = (object) [
|
||||
'value' => '',
|
||||
'text' => '──────────────',
|
||||
'disable' => true
|
||||
];
|
||||
}
|
||||
$lastMenuType = $item->menutype;
|
||||
}
|
||||
$db->setQuery($query);
|
||||
$menuItems = $db->loadObjectList();
|
||||
|
||||
$displayText = $item->title !== '' ? $item->title : ucwords(str_replace(['-', '_'], ' ', $item->alias));
|
||||
$options[] = (object) [
|
||||
'value' => $item->alias,
|
||||
'text' => $displayText . ' (' . $item->alias . ')'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Log error but don't break the form
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
Text::sprintf('PLG_SYSTEM_MOKOJOOMTOS_ERROR_LOADING_MENU_ITEMS', $e->getMessage()),
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
if ($menuItems)
|
||||
{
|
||||
$lastMenuType = '';
|
||||
|
||||
return $options;
|
||||
}
|
||||
foreach ($menuItems as $item)
|
||||
{
|
||||
// Add menu type separator for better organization
|
||||
if ($item->menutype !== $lastMenuType)
|
||||
{
|
||||
if ($lastMenuType !== '')
|
||||
{
|
||||
$options[] = (object) [
|
||||
'value' => '',
|
||||
'text' => '──────────────',
|
||||
'disabled' => true
|
||||
];
|
||||
}
|
||||
$lastMenuType = $item->menutype;
|
||||
}
|
||||
|
||||
$displayText = $item->title !== '' ? $item->title : ucwords(str_replace(['-', '_'], ' ', $item->alias));
|
||||
$options[] = (object) [
|
||||
'value' => $item->path,
|
||||
'text' => $displayText . ' (/' . $item->path . ')'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
Text::sprintf('PLG_SYSTEM_MOKOJOOMTOS_ERROR_LOADING_MENU_ITEMS', $e->getMessage()),
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
||||
+87
-38
@@ -1,44 +1,93 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
VERSION: 03.08.04
|
||||
VERSION: 04.01.00
|
||||
-->
|
||||
|
||||
Joomla Extension Update Server XML
|
||||
See: https://docs.joomla.org/Deploying_an_Update_Server
|
||||
|
||||
This file is the update server manifest for MokoJoomTOS.
|
||||
The Joomla installer polls this URL to check for new versions.
|
||||
|
||||
The manifest.xml in this repository must reference BOTH update servers:
|
||||
<updateservers>
|
||||
<server type="extension" priority="1" name="MokoJoomTOS Update Server (Gitea)">
|
||||
https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/raw/branch/main/updates.xml
|
||||
</server>
|
||||
<server type="extension" priority="2" name="MokoJoomTOS Update Server (GitHub)">
|
||||
https://raw.githubusercontent.com/mokoconsulting-tech/MokoJoomTOS/main/updates.xml
|
||||
</server>
|
||||
</updateservers>
|
||||
|
||||
When a new release is made, run `make release` or the release workflow to
|
||||
prepend a new <update> entry to this file automatically.
|
||||
-->
|
||||
<updates>
|
||||
<update>
|
||||
<name>plg_system_mokojoomtos</name>
|
||||
<description>MokoJoomTOS — Moko Consulting Joomla plugin</description>
|
||||
<element>plg_system_mokojoomtos</element>
|
||||
<type>plugin</type>
|
||||
<folder>system</folder>
|
||||
<version>03.08.04</version>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">
|
||||
https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/download/v03/plg_system_mokojoomtos-03.08.04.zip
|
||||
</downloadurl>
|
||||
<downloadurl type="full" format="zip">
|
||||
https://github.com/mokoconsulting-tech/MokoJoomTOS/releases/download/v03/plg_system_mokojoomtos-03.08.04.zip
|
||||
</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="[56].*"/>
|
||||
<php_minimum>8.1</php_minimum>
|
||||
</update>
|
||||
<update>
|
||||
<name>System - Moko Terms of Service</name>
|
||||
<description>System - Moko Terms of Service update</description>
|
||||
<element>mokojoomtos</element>
|
||||
<type>plugin</type>
|
||||
<version>04.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>development</tag></tags>
|
||||
<infourl title="System - Moko Terms of Service">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/download/stable/plg_system_mokojoomtos-04.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>System - Moko Terms of Service</name>
|
||||
<description>System - Moko Terms of Service update</description>
|
||||
<element>mokojoomtos</element>
|
||||
<type>plugin</type>
|
||||
<version>04.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>alpha</tag></tags>
|
||||
<infourl title="System - Moko Terms of Service">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/download/stable/plg_system_mokojoomtos-04.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>System - Moko Terms of Service</name>
|
||||
<description>System - Moko Terms of Service update</description>
|
||||
<element>mokojoomtos</element>
|
||||
<type>plugin</type>
|
||||
<version>04.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>beta</tag></tags>
|
||||
<infourl title="System - Moko Terms of Service">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/download/stable/plg_system_mokojoomtos-04.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>System - Moko Terms of Service</name>
|
||||
<description>System - Moko Terms of Service update</description>
|
||||
<element>mokojoomtos</element>
|
||||
<type>plugin</type>
|
||||
<version>04.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>rc</tag></tags>
|
||||
<infourl title="System - Moko Terms of Service">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/download/stable/plg_system_mokojoomtos-04.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>System - Moko Terms of Service</name>
|
||||
<description>System - Moko Terms of Service update</description>
|
||||
<element>mokojoomtos</element>
|
||||
<type>plugin</type>
|
||||
<version>04.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>stable</tag></tags>
|
||||
<infourl title="System - Moko Terms of Service">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomTOS/releases/download/stable/plg_system_mokojoomtos-04.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
</updates>
|
||||
|
||||
Reference in New Issue
Block a user