92 Commits

Author SHA1 Message Date
Jonathan Miller 7b5a83c71a fix(ci): strip type prefix from element in auto-release workflow
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 4s
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Failing after 2s
Joomla: Repo Health / Scripts governance (push) Successful in 3s
Joomla: Repo Health / Repository health (push) Failing after 3s
Prevents pkg_pkg_ duplication in release names, ZIP filenames, and
SHA256 checksums. Uses <packagename> for packages and strips existing
prefixes before prepending type prefix.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 22:59:07 -05:00
gitea-actions[bot] cff932dcbb chore(release): build 02.04.00 [skip ci] 2026-05-25 03:54:55 +00:00
jmiller 8c9e3e6d44 Merge pull request 'Release 02.03.12: Package structure, site aliases, plugin protection' (#35) from dev into main
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 3s
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Failing after 2s
Joomla: Repo Health / Scripts governance (push) Successful in 3s
Joomla: Repo Health / Repository health (push) Failing after 3s
2026-05-24 23:23:39 +00:00
jmiller 74e535c929 Merge pull request 'chore: cascade main → dev (f2b0c2e) [skip ci]' (#34) from main into dev
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 11s
chore: cascade main → dev [skip ci]
2026-05-24 22:57:49 +00:00
jmiller efdaaf479a Add RC pre-release trigger to CI workflow
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 2s
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Release configuration (push) Failing after 4s
Joomla: Repo Health / Repository health (push) Failing after 5s
Automatically triggers a release-candidate build when CI lint+tests
pass on a pull request.

Authored-by: Moko Consulting
2026-05-24 22:55:27 +00:00
jmiller f2b0c2e420 Add RC pre-release trigger to PR check workflow
Joomla: Repo Health / Access control (push) Successful in 1s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 4s
Joomla: Repo Health / Release configuration (push) Failing after 3s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 4s
Automatically triggers a release-candidate build when a PR passes
branch policy and validation checks.

Authored-by: Moko Consulting
2026-05-24 22:54:37 +00:00
Jonathan Miller 40e6a1f086 chore: cascade main -> dev [skip ci] 2026-05-24 14:49:22 -05:00
Jonathan Miller 7b7dc4a553 chore: slim README to overview, move details to wiki
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Failing after 6s
Joomla: Repo Health / Scripts governance (push) Successful in 6s
Joomla: Repo Health / Repository health (push) Failing after 7s
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 04:06:09 -05:00
jmiller edfd9fa326 fix: updates.xml targetplatform regex causing Gitea 500 [skip ci] 2026-05-24 09:01:46 +00:00
jmiller 2de4d08430 fix: updates.xml with stable + dev + legacy plugin entries [skip ci] 2026-05-24 08:57:04 +00:00
jmiller 872f55f376 chore: sync updates.xml 02.03.11 from dev [skip ci] 2026-05-24 08:54:22 +00:00
gitea-actions[bot] ee7a42e14b chore: update updates.xml (development: 02.03.12-dev) [skip ci] 2026-05-24 08:54:00 +00:00
jmiller 9f434aefdc chore: sync updates.xml from [skip ci] 2026-05-24 08:54:00 +00:00
gitea-actions[bot] 1000f028d2 chore(version): auto-bump patch 02.03.12 [skip ci] 2026-05-24 08:53:59 +00:00
Jonathan Miller b048b47e7c security: protected status prevents disable/uninstall
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 25s
Joomla: Repo Health / Release configuration (push) Failing after 4s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 3s
- Set protected=1, locked=0 on MokoWaaS extensions via package script
- Self-healing: plugin checks and restores protected flag each session
- Block non-master disable via plugin list toggle (plugins.publish)
- Block non-master uninstall via installer manage
- Joomla framework natively enforces protected status (greys out toggles)
- Master users can still manage settings and updates

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 03:53:33 -05:00
Jonathan Miller 6e0d5387cf chore: update CHANGELOG for 02.03.10
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Failing after 3s
Joomla: Repo Health / Scripts governance (push) Successful in 3s
Joomla: Repo Health / Repository health (push) Failing after 3s
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 03:49:16 -05:00
gitea-actions[bot] 76bbf7ad85 chore: update development channel 02.03.10 [skip ci] 2026-05-24 08:47:22 +00:00
jmiller b1c2b3c92a chore: sync updates.xml 02.03.10 from dev [skip ci] 2026-05-24 08:47:21 +00:00
gitea-actions[bot] a5dc00e056 chore: update development channel 02.03.10 [skip ci] 2026-05-24 08:47:21 +00:00
Jonathan Miller c6475ff29a feat: canonical URLs, alias heartbeats, package migration, cleanup
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Failing after 13m55s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 4s
Joomla: Repo Health / Release configuration (push) Failing after 5s
- manifest.xml: package-type plugin → package
- Canonical URL injection for alias domains (prevents SEO duplication)
- Heartbeat registration for alias domains (each alias gets Grafana datasource)
- Package script.php: enable plugins on every install/update, heartbeat on postflight
- Remove accidentally committed profile.ps1 and TODO.md
- Add profile.ps1 and TODO.md to .gitignore

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 03:46:23 -05:00
jmiller b7a52cc6a4 chore: sync updates.xml from [skip ci] 2026-05-24 08:38:28 +00:00
gitea-actions[bot] ffd98a19d9 chore: update updates.xml (development: 02.03.10-dev) [skip ci] 2026-05-24 08:38:28 +00:00
gitea-actions[bot] 34469609dd chore(version): auto-bump patch 02.03.10 [skip ci] 2026-05-24 08:38:27 +00:00
Jonathan Miller d766b0568a fix: alias offline uses Joomla native offline mode (template offline.php)
Joomla: Repo Health / Access control (push) Successful in 2s
Joomla: Update Server / Update updates.xml (push) Successful in 25s
Joomla: Repo Health / Release configuration (push) Failing after 4s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 4s
Instead of rendering a custom HTML page, set config('offline', 1) at
onAfterRoute so Joomla renders the site template's offline.php layout.
Custom offline_message is passed via config if set.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 03:38:01 -05:00
jmiller da4b544da7 chore: sync updates.xml from [skip ci] 2026-05-24 08:35:34 +00:00
gitea-actions[bot] cfea9fac99 chore: update updates.xml (development: 02.03.09-dev) [skip ci] 2026-05-24 08:35:34 +00:00
gitea-actions[bot] 7d6d654d6d chore(version): auto-bump patch 02.03.09 [skip ci] 2026-05-24 08:35:33 +00:00
Jonathan Miller dca452e49d fix: alias detection, offline page, backend redirect working
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 25s
Joomla: Repo Health / Release configuration (push) Has been cancelled
Joomla: Repo Health / Scripts governance (push) Has been cancelled
Joomla: Repo Health / Repository health (push) Has been cancelled
- Removed primaryHost check from getCurrentAlias() — just look up
  current host in aliases list directly
- Handle Joomla subform stdClass→array conversion
- Strip trailing slashes from alias domains
- Moved handleSiteAlias() to onAfterRoute (client type resolved)
- Use http_response_code(503) + die() for offline page
- Cast offline value to string for comparison

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 03:35:06 -05:00
jmiller f68a477c56 chore: sync updates.xml 02.03.07 from dev [skip ci] 2026-05-24 08:07:35 +00:00
gitea-actions[bot] b827b3382a chore: update development channel 02.03.07 [skip ci] 2026-05-24 08:07:35 +00:00
gitea-actions[bot] 42841f7335 chore: update development channel 02.03.07 [skip ci] 2026-05-24 08:07:34 +00:00
jmiller 8cfe596754 chore: sync updates.xml from [skip ci] 2026-05-24 04:53:47 +00:00
gitea-actions[bot] 0f354422aa chore: update updates.xml (development: 02.03.07-dev) [skip ci] 2026-05-24 04:53:47 +00:00
gitea-actions[bot] 86aae39be1 chore(version): auto-bump patch 02.03.07 [skip ci] 2026-05-24 04:53:46 +00:00
Jonathan Miller b5e932d78b fix: backend redirect uses primary_domain setting instead of Uri::root
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 25s
Joomla: Repo Health / Release configuration (push) Failing after 3s
Joomla: Repo Health / Scripts governance (push) Successful in 3s
Joomla: Repo Health / Repository health (push) Failing after 3s
Uri::root() returns the current request domain, so redirecting from an
alias to Uri::root()/administrator redirects back to the alias. Added
primary_domain field to Site Aliases tab and getPrimaryHost() method
that checks: plugin setting → $live_site → alias exclusion fallback.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 23:53:20 -05:00
jmiller b0e15b8747 fix: correct download URLs and restore stable entry [skip ci] 2026-05-24 04:43:00 +00:00
jmiller 8d232e8c7b chore: sync updates.xml 02.03.05 from dev [skip ci] 2026-05-24 04:40:26 +00:00
jmiller 485322ba08 chore: sync updates.xml from [skip ci] 2026-05-24 04:40:21 +00:00
gitea-actions[bot] c738eb6669 chore: update updates.xml (development: 02.03.06-dev) [skip ci] 2026-05-24 04:40:20 +00:00
gitea-actions[bot] e0f98dc5e2 chore(version): auto-bump patch 02.03.06 [skip ci] 2026-05-24 04:40:19 +00:00
Jonathan Miller ede07c6675 feat: dynamic plugin version + plugin protection (no lock)
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 28s
Joomla: Repo Health / Release configuration (push) Has been cancelled
Joomla: Repo Health / Scripts governance (push) Has been cancelled
Joomla: Repo Health / Repository health (push) Has been cancelled
- Read plugin_version from manifest XML instead of hardcoding
- Hide MokoWaaS from plugin/installer list for non-master users
- Block non-master uninstall and disable attempts
- No self-healing lock — master users can still disable if needed

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 23:39:46 -05:00
jmiller fa67ffaa00 fix: correct download URL in updates.xml [skip ci] 2026-05-24 04:27:37 +00:00
gitea-actions[bot] 1fe8422fc0 chore: update development channel 02.03.04 [skip ci] 2026-05-24 04:25:59 +00:00
jmiller f40998fc30 chore: sync updates.xml 02.03.04 from dev [skip ci] 2026-05-24 04:25:58 +00:00
gitea-actions[bot] 6e216de0dc chore: update development channel 02.03.04 [skip ci] 2026-05-24 04:25:58 +00:00
Jonathan Miller 86e40fb978 chore: sync plugin manifest version to 02.03.04 [skip ci]
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 23:25:26 -05:00
gitea-actions[bot] b2f52c191b chore: update development channel 02.03.03 [skip ci] 2026-05-24 04:21:25 +00:00
jmiller fb74a255d3 chore: sync updates.xml 02.03.03 from dev [skip ci] 2026-05-24 04:21:24 +00:00
gitea-actions[bot] 8e1040efee chore: update development channel 02.03.03 [skip ci] 2026-05-24 04:21:24 +00:00
gitea-actions[bot] c203e970b9 chore: update development channel 02.03.02 [skip ci] 2026-05-24 04:16:49 +00:00
jmiller 7e489b072a chore: sync updates.xml 02.03.02 from dev [skip ci] 2026-05-24 04:16:48 +00:00
gitea-actions[bot] bf3c986113 chore: update development channel 02.03.02 [skip ci] 2026-05-24 04:16:48 +00:00
Jonathan Miller 955c08a387 fix(ci): disable pipefail during element detection to prevent SIGPIPE exit
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Repository health (push) Failing after 5s
Joomla: Repo Health / Release configuration (push) Failing after 5s
Joomla: Repo Health / Scripts governance (push) Failing after 5s
The find|grep|head pipe under bash pipefail causes SIGPIPE when head
closes the pipe after reading one line, making the step exit with code 1
even though the version bump and push succeeded.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 23:16:22 -05:00
Jonathan Miller 52dbefbb14 fix(ci): version bump after release, not before
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Has been cancelled
Joomla: Repo Health / Scripts governance (push) Has been cancelled
Joomla: Repo Health / Repository health (push) Has been cancelled
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 23:13:49 -05:00
jmiller 379ca36613 Merge pull request 'chore: cascade main → dev [skip ci]' (#32) from main into dev
chore: cascade main → dev [skip ci]
2026-05-24 04:11:53 +00:00
jmiller 35ca1af6b8 Merge pull request 'feat: convert to package with webservices API + heartbeat fix' (#31) from dev into main
chore: cascade main → dev [skip ci]
2026-05-24 04:10:09 +00:00
Moko Consulting e9ec664f03 chore: update CHANGELOG for deploy workflow removal
Joomla: Repo Health / Access control (push) Successful in 2s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 3s
Joomla: Repo Health / Release configuration (push) Failing after 3s
Joomla: Repo Health / Scripts governance (push) Successful in 4s
Joomla: Repo Health / Repository health (push) Failing after 4s
2026-05-24 04:09:59 +00:00
gitea-actions[bot] 6929c636b9 chore(version): bump to 02.03.02 [skip ci] 2026-05-24 04:06:06 +00:00
gitea-actions[bot] 20797d663f chore(version): bump to 02.03.01 [skip ci] 2026-05-24 03:48:44 +00:00
jmiller 11d5fd2019 chore: sync updates.xml from [skip ci] 2026-05-24 03:44:46 +00:00
gitea-actions[bot] 1fa965dddb chore: update updates.xml (development: 02.01.46-dev) [skip ci] 2026-05-24 03:44:45 +00:00
gitea-actions[bot] 5a3ec7d9b1 chore(version): auto-bump patch 02.01.46 [skip ci] 2026-05-24 03:44:44 +00:00
Jonathan Miller aef5ca43f6 chore(version): bump to 02.03.00
Joomla: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 4s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 3s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been skipped
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been skipped
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been skipped
Joomla: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Changelog Updated (pull_request) Failing after 5s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Failing after 7s
Joomla: Update Server / Update updates.xml (push) Successful in 37s
Joomla: Repo Health / Release configuration (push) Failing after 3s
Joomla: Repo Health / Scripts governance (push) Successful in 3s
Joomla: Repo Health / Repository health (push) Failing after 4s
Joomla: Repo Health / Release configuration (pull_request) Has been cancelled
Joomla: Repo Health / Scripts governance (pull_request) Has been cancelled
Joomla: Repo Health / Repository health (pull_request) Has been cancelled
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 22:44:04 -05:00
jmiller 0631d80fa1 chore: sync updates.xml from [skip ci] 2026-05-24 03:42:32 +00:00
gitea-actions[bot] e84c10b14f chore: update updates.xml (development: 02.01.45-dev) [skip ci] 2026-05-24 03:42:31 +00:00
gitea-actions[bot] 87e543ef1c chore(version): auto-bump patch 02.01.45 [skip ci] 2026-05-24 03:42:31 +00:00
Jonathan Miller 32236ad7ff feat(package): convert to package with webservices API plugin
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Update Server / Update updates.xml (push) Successful in 26s
Joomla: Repo Health / Release configuration (push) Has been cancelled
Joomla: Repo Health / Scripts governance (push) Has been cancelled
Joomla: Repo Health / Repository health (push) Has been cancelled
Restructure MokoWaaS from a standalone system plugin into a Joomla
package containing:

- plg_system_mokowaas — existing system plugin (moved to packages/)
- com_mokowaas — minimal API-only component with health, cache, and
  update controllers for Joomla Web Services API
- plg_webservices_mokowaas — registers /api/v1/mokowaas/* routes

Package script auto-enables both plugins on fresh install.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 22:42:02 -05:00
jmiller d6e462f3b7 Merge pull request 'chore: cascade main → dev (d470669) [skip ci]' (#30) from main into dev
chore: cascade main → dev [skip ci]
2026-05-24 03:40:53 +00:00
jmiller d470669634 chore: remove deploy workflow — switching to Joomla update server method
Joomla: Repo Health / Access control (push) Successful in 2s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 3s
Joomla: Repo Health / Release configuration (push) Has been cancelled
Joomla: Repo Health / Scripts governance (push) Has been cancelled
Joomla: Repo Health / Repository health (push) Has been cancelled
2026-05-24 03:40:50 +00:00
jmiller feaccf0758 fix: updates.xml add stable channel entry [skip ci] 2026-05-24 03:39:45 +00:00
Jonathan Miller 9542c88ba4 fix: updates.xml missing stable entry — add both stable and dev channels
Joomla: Repo Health / Access control (push) Successful in 1s
Joomla: Repo Health / Release configuration (push) Has been cancelled
Joomla: Repo Health / Scripts governance (push) Has been cancelled
Joomla: Repo Health / Repository health (push) Has been cancelled
The pre-release workflow overwrites updates.xml with only the dev entry,
removing the stable channel. Joomla updater on sites set to stable channel
cannot see any updates.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 22:38:16 -05:00
jmiller 7e3d366043 chore: sync updates.xml 02.01.44 from dev [skip ci] 2026-05-24 03:33:54 +00:00
gitea-actions[bot] 4c5919f209 chore: update development channel 02.01.44 [skip ci] 2026-05-24 03:33:53 +00:00
gitea-actions[bot] 0b3e699f29 chore(version): bump to 02.01.44 [skip ci] 2026-05-24 03:33:52 +00:00
jmiller 406242fb7d chore: sync updates.xml 02.02.00 [skip ci] 2026-05-24 03:08:12 +00:00
jmiller e6cb6eb531 Merge pull request 'chore: cascade main → dev (bdceb42) [skip ci]' (#28) from main into dev
chore: cascade main → dev [skip ci]
2026-05-23 23:39:10 +00:00
jmiller bdceb4256f Merge pull request 'Release 02.01.43: Site aliases tab, API endpoints, heartbeat fix' (#27) from dev into main
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 3s
2026-05-23 23:39:06 +00:00
Jonathan Miller 81591477b2 chore: resolve merge conflicts (version 02.01.43)
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Successful in 4s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 5s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 5s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been skipped
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been skipped
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been skipped
Universal: PR Check / Changelog Updated (pull_request) Successful in 4s
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 26s
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 18:38:51 -05:00
Jonathan Miller cc907a5aa2 fix(heartbeat): only send heartbeat for primary domain, not aliases
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 3s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 4s
Joomla: Extension CI / Release Readiness Check (pull_request) Successful in 4s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been skipped
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been skipped
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 3s
Universal: PR Check / Changelog Updated (pull_request) Successful in 3s
Alias domains were creating separate Grafana datasources with unique
UIDs, causing provisioning failures (duplicate UID) when Grafana
restarts. Only the primary domain should be registered.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 18:34:57 -05:00
Jonathan Miller 5360c641e9 chore: update CHANGELOG and README for 02.01.43 release
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 18:34:26 -05:00
jmiller d7a4066261 chore: sync updates.xml 02.01.43 from dev [skip ci] 2026-05-23 23:12:31 +00:00
gitea-actions[bot] 478eb262b9 chore: update development channel 02.01.43 [skip ci] 2026-05-23 23:12:31 +00:00
gitea-actions[bot] ea934ba04b chore(version): bump to 02.01.43 [skip ci] 2026-05-23 23:12:30 +00:00
Jonathan Miller a92c1ce772 feat: site aliases tab with per-alias offline, robots, and backend redirect
Move site aliases from a comma-separated text field in Diagnostics to its
own tab using Joomla's subform repeatable-table layout. Each alias entry
now supports: domain, offline toggle with custom message, robots meta
directive, and backend redirect to primary domain. Frontend stays on the
alias domain while admin requests can be redirected to the primary.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 18:11:07 -05:00
jmiller 0e55a546ff chore: sync updates.xml 02.01.42 from dev [skip ci] 2026-05-23 22:58:03 +00:00
gitea-actions[bot] 14e94518ba chore: update development channel 02.01.42 [skip ci] 2026-05-23 22:58:03 +00:00
gitea-actions[bot] b32d91c446 chore(version): bump to 02.01.42 [skip ci] 2026-05-23 22:58:02 +00:00
Jonathan Miller 03e0b6d13b fix: accept any 200 status from heartbeat (registered/updated/ok)
The receiver returns 'updated' when re-registering an existing site,
but the code only accepted 'registered', causing false 'HTTP 200 — Unknown' warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 17:30:52 -05:00
jmiller 21cb335727 chore: stable 02.01.42 [skip ci] 2026-05-23 22:25:01 +00:00
jmiller d2700f96fe chore: stable 02.01.42 [skip ci] 2026-05-23 22:25:01 +00:00
gitea-actions[bot] 7f7cdb4cc9 chore: update release-candidate channel 02.01.42 [skip ci] 2026-05-23 22:22:55 +00:00
jmiller b1b21e79d8 chore: sync updates.xml 02.01.42 from main [skip ci] 2026-05-23 22:22:55 +00:00
52 changed files with 2635 additions and 1637 deletions
+2
View File
@@ -203,3 +203,5 @@ venv/
*.coverage
hypothesis/
profile.ps1
TODO.md
+1 -1
View File
@@ -18,7 +18,7 @@
</governance>
<build>
<language>PHP</language>
<package-type>plugin</package-type>
<package-type>package</package-type>
<entry-point>src/</entry-point>
</build>
</moko-platform>
+10
View File
@@ -339,6 +339,8 @@ jobs:
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
# Build release name: "Pretty Name VERSION (type_element-VERSION)"
# Strip existing type prefix to prevent duplication
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
TYPE_PREFIX=""
case "${EXT_TYPE}" in
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
@@ -409,6 +411,13 @@ jobs:
# ZIP name: type_folder_element-VERSION (e.g. plg_system_mokojgdpc-01.01.00.zip)
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
# For packages, prefer <packagename> over filename-derived element
if [ "$EXT_TYPE" = "package" ]; then
PKG_NAME=$(sed -n 's/.*<packagename>\([^<]*\)<\/packagename>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
[ -n "$PKG_NAME" ] && EXT_ELEMENT="$PKG_NAME"
fi
# Strip existing type prefix to prevent duplication (e.g. pkg_mokowaas → mokowaas)
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
TYPE_PREFIX=""
case "${EXT_TYPE}" in
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
@@ -570,6 +579,7 @@ jobs:
EXT_FOLDER="${{ steps.updates.outputs.ext_folder }}"
# Build TYPE_PREFIX to match Step 8's ZIP naming
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
TYPE_PREFIX=""
case "${EXT_TYPE}" in
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
+17
View File
@@ -448,3 +448,20 @@ jobs:
echo '```' >> $GITHUB_STEP_SUMMARY
fi
exit $EXIT
pre-release:
name: Build RC Pre-Release
runs-on: ubuntu-latest
needs: [lint-and-validate, test]
if: github.event_name == 'pull_request'
steps:
- name: Trigger pre-release build
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
REPO: ${{ github.repository }}
BRANCH: ${{ github.head_ref }}
run: |
curl -s -X POST "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
-126
View File
@@ -1,126 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Deploy
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
# VERSION: 04.07.00
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
name: "Universal: Deploy to Dev (Manual)"
on:
workflow_dispatch:
inputs:
clear_remote:
description: 'Delete all remote files before uploading'
required: false
default: 'false'
type: boolean
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read
jobs:
deploy:
name: SFTP Deploy to Dev
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup PHP
run: |
php -v && composer --version
- name: Setup MokoStandards tools
env:
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
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
fi
- name: Check FTP configuration
id: check
env:
HOST: ${{ vars.DEV_FTP_HOST }}
PATH_VAR: ${{ vars.DEV_FTP_PATH }}
PORT: ${{ vars.DEV_FTP_PORT }}
run: |
if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then
echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "host=$HOST" >> "$GITHUB_OUTPUT"
REMOTE="${PATH_VAR%/}"
echo "remote=$REMOTE" >> "$GITHUB_OUTPUT"
[ -z "$PORT" ] && PORT="22"
echo "port=$PORT" >> "$GITHUB_OUTPUT"
- name: Deploy via SFTP
if: steps.check.outputs.skip != 'true'
env:
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
run: |
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; }
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
"${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \
> /tmp/sftp-config.json
if [ -n "$SFTP_KEY" ]; then
echo "$SFTP_KEY" > /tmp/deploy_key
chmod 600 /tmp/deploy_key
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
else
printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json
fi
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[@]}"
else
php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
fi
rm -f /tmp/deploy_key /tmp/sftp-config.json
- name: Summary
if: always()
run: |
if [ "${{ steps.check.outputs.skip }}" = "true" ]; then
echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY
else
echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY
fi
+18
View File
@@ -222,3 +222,21 @@ jobs:
echo "::error::CHANGELOG.md must be updated before merging to main. Add [skip changelog] to the PR title to bypass."
exit 1
fi
# ── Pre-Release RC Build ─────────────────────────────────────────────────
pre-release:
name: Build RC Package
runs-on: ubuntu-latest
needs: [branch-policy, validate]
steps:
- name: Trigger RC pre-release
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
REPO: ${{ github.repository }}
BRANCH: ${{ github.head_ref }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: |
curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
+27 -13
View File
@@ -77,37 +77,32 @@ jobs:
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
esac
# 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 .)
# Read current version from manifest (priority) or README — no bump yet
VERSION=$(php ${MOKO_API}/version_read.php --path .)
echo "Version: ${VERSION}"
# Update platform-specific manifest
# Ensure platform-specific manifest matches
php ${MOKO_API}/version_set_platform.php --path . --version "${VERSION}"
# Commit version bump
# Git setup for later commits
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 to ${VERSION} [skip ci]"
git push origin HEAD 2>&1
}
# Detect element from Joomla/Dolibarr manifest
set +o pipefail
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)
MANIFEST=$(find . -maxdepth 4 -name "*.xml" ! -path "./.git/*" -print0 2>/dev/null | xargs -0 grep -l '<extension' 2>/dev/null | head -1 || true)
if [ -n "$MANIFEST" ]; then
ELEM=$(grep -oP "<element>\K[^<]+" "$MANIFEST" 2>/dev/null | head -1)
ELEM=$(grep -oP "<element>\K[^<]+" "$MANIFEST" 2>/dev/null | head -1 || true)
[ -n "$ELEM" ] && EXT_ELEMENT="$ELEM"
fi
fi
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
set -o pipefail
ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
@@ -244,3 +239,22 @@ jobs:
echo "Deleted: ${TAG} (id: ${RELEASE_ID})"
fi
done
- name: "Post-release version bump"
run: |
MOKO_API="/tmp/moko-platform-api/cli"
VERSION="${{ steps.meta.outputs.version }}"
# Bump patch for next dev cycle
BUMP_OUTPUT=$(php ${MOKO_API}/version_bump.php --path .)
NEXT=$(echo "$BUMP_OUTPUT" | grep -oP '\d{2}\.\d{2}\.\d{2}$' || true)
[ -z "$NEXT" ] && exit 0
# Update platform-specific manifest to next version
php ${MOKO_API}/version_set_platform.php --path . --version "${NEXT}"
git add -A
git diff --cached --quiet || {
git commit -m "chore: update development channel ${VERSION} [skip ci]"
git push origin HEAD 2>&1
}
+53
View File
@@ -31,6 +31,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- License/subscription check
- System email template branding (DB approach)
## [02.03.10] - 2026-05-24
### Added
- Canonical URL injection for alias domains (prevents SEO duplication)
- Primary Domain config field in Site Aliases tab
- Heartbeat registration for alias domains (each alias gets Grafana datasource)
- Plugin protection: hidden from non-master users, disable/uninstall blocked
- Dynamic plugin version read from manifest XML (no more hardcoded strings)
- Package structure: `pkg_mokowaas` with system plugin, webservices plugin, and component
### Changed
- Alias offline mode uses Joomla's native template offline.php (not custom HTML)
- Alias detection simplified: direct lookup in aliases list (no primary host comparison)
- handleSiteAlias() moved to onAfterRoute (client type resolved at that point)
- Package script.php enables plugins on every install/update and sends heartbeat
### Fixed
- Alias domain matching: strip trailing slashes, handle Joomla subform stdClass format
- Backend redirect: use primary_domain setting instead of Uri::root() (returned alias domain on mirrors)
- CI: version_bump reads manifest XML with priority over README.md VERSION header
- CI: version bump occurs after release build, not before
- CI: pipefail disabled during element detection (SIGPIPE on find|head)
- CI: pkg_pkg_ prefix duplication in zip names and updates.xml URLs
- CI: updates_xml_build preserves existing channel entries (stable not wiped by dev releases)
### Removed
- deploy-manual.yml workflow — using Joomla update server for distribution
- Accidentally committed profile.ps1 and TODO.md
## [02.01.43] - 2026-05-23
### Added
- Site Aliases tab with Joomla subform repeatable-table UI
- Per-alias offline toggle with custom maintenance message (503 response)
- Per-alias robots meta directive (index/noindex/follow/nofollow/none)
- Per-alias backend redirect (admin panel redirects to primary domain)
- 6 MokoWaaS API endpoints: health, install, update, cache, backup, info
- Remote plugin install via `/?mokowaas=install` endpoint
- Remote update trigger via `/?mokowaas=update` endpoint
- Remote cache clear via `/?mokowaas=cache` endpoint (site + admin + opcache)
- Remote Akeeba Backup trigger via `/?mokowaas=backup` endpoint
- Compact site info via `/?mokowaas=info` endpoint
### Changed
- Site aliases moved from comma-separated text field to structured subform
- Each alias now stores domain, offline, offline_message, robots, redirect_backend
- Heartbeat provisioning updated for subform alias format
- Grafana datasource names use domain-only (removed "MokoWaaS - " prefix)
### Fixed
- Heartbeat receiver accepts any 200 status (registered/updated/ok)
- script.php uses heartbeat receiver instead of Grafana API (fixes 403 RBAC)
## [02.01.37] - 2026-05-23
### Added
+26 -320
View File
@@ -5,356 +5,62 @@
SPDX-LICENSE-IDENTIFIER: GPL-3.0-or-later
This program is free software; you can redistribute it and modify it under the terms of the GNU General Public License version 3 or later.
This program is distributed in the hope that it will be useful but without warranty.
You should have received a copy of the GNU General Public License in LICENSE.md.
# FILE INFORMATION
DEFGROUP: Joomla.Plugin
INGROUP: MokoWaaS
REPO: https://github.com/mokoconsulting-tech/mokowaas
VERSION: 02.01.42
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS
VERSION: 02.04.00
PATH: /README.md
BRIEF: Rebranding plugin for MokoWaaS platform
NOTE: Internal WaaS identity abstraction layer
BRIEF: MokoWaaS platform plugin for Joomla
-->
# MokoWaaS Plugin
# MokoWaaS
[![Version](https://img.shields.io/badge/version-02.01.18-blue.svg?logo=v&logoColor=white)](https://github.com/mokoconsulting-tech/MokoWaaS/releases/tag/v02)
[![Version](https://img.shields.io/badge/version-02.03.11-blue.svg?logo=v&logoColor=white)](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases)
[![License](https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&logoColor=white)](LICENSE)
[![Joomla](https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&logoColor=white)](https://www.joomla.org)
[![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&logoColor=white)](https://www.php.net)
MokoWaaS is a Joomla 5.x / 6.x system plugin that provides a configurable white-label identity layer for the MokoWaaS platform. It replaces all visible Joomla branding with your own brand name, company name, and support URLs — configurable from the plugin admin without code changes.
## Table of Contents
- [Overview](#overview)
- [Features](#features)
- [System Requirements](#system-requirements)
- [Installation](#installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Technical Implementation](#technical-implementation)
- [Repository Structure](#repository-structure)
- [Development](#development)
- [Documentation](#documentation)
- [Support](#support)
- [License](#license)
- [Changelog](#changelog)
## Overview
The MokoWaaS plugin operationalizes a unified naming convention, brand-controlled visuals, and enforced terminology across all tenant sites. This ensures consistent service delivery within the WaaS (Website as a Service) framework by abstracting all upstream Joomla identifiers behind MokoWaaS-compliant terminology.
MokoWaaS is a Joomla 5.x / 6.x system plugin package that provides white-label branding, security hardening, tenant restrictions, health monitoring, and multi-domain management for the MokoWaaS platform.
## Features
- **Template-Based Overrides**: 50+ language keys with `{{BRAND_NAME}}`, `{{COMPANY_NAME}}`, `{{SUPPORT_URL}}` placeholders
- **Configurable Brand**: Change brand name, company, and support URL from plugin config — takes effect immediately
- **Safe Override Merging**: Sentinel-block pattern preserves existing site overrides during install/update
- **Clean Uninstall**: Only MokoWaaS keys are removed; all other overrides are preserved
- **Joomla 5.x / 6.x Compatible**: Built using modern Joomla plugin architecture with dependency injection
- **Multi-Language Support**: en-GB and en-US locales
- **Admin & Frontend Coverage**: Dashboard, footer, login, installer, system info, update component, error pages, and more
- **Health Monitoring**: 16 diagnostic checks via `/?mokowaas=health` — database, filesystem, cache, extensions, Akeeba Backup, Admin Tools, SSL, cron, errors, DB size, content, users, mail, SEO, templates, config
- **Grafana Integration**: Auto-provisions Infinity datasource via heartbeat receiver — 9-row dashboard with all health metrics
- **ntfy Notifications**: Heartbeat events pushed to `mokowaas-heartbeat` topic
- **Plugin Protection**: Hidden from non-super-admins, self-healing lock, uninstall blocked
- **Governance Compliant**: Aligned with [moko-platform](https://git.mokoconsulting.tech/MokoConsulting/moko-platform)
- **White-Label Branding** — configurable brand name, company, support URL, colors, favicon, custom CSS
- **Tenant Restrictions** — master user enforcement, installer/sysinfo/config/template access control
- **Health Monitoring** — 16 diagnostic checks via `/?mokowaas=health` with Grafana auto-provisioning
- **Site Aliases** — per-alias offline mode, robots directives, backend redirect, canonical URLs
- **Remote API** — 6 endpoints (health, install, update, cache, backup, info)
- **Security Hardening** — HTTPS enforcement, session timeouts, password policy, upload restrictions
- **Plugin Protection** — protected status, hidden from non-master users, disable/uninstall blocked
## System Requirements
## Requirements
- **Joomla**: 5.0+ or 6.x
- **PHP**: 8.1 or higher (8.3+ for Joomla 6)
- **Extensions**: Standard Joomla PHP extensions
- **Permissions**: Write access to language override directories
- Joomla 5.0+ or 6.x
- PHP 8.1+ (8.3+ for Joomla 6)
## Installation
### Method 1: Via Joomla Extension Manager (Recommended)
Download the latest `pkg_mokowaas-*.zip` from [Releases](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases) and install via **System → Install → Upload Package File**.
1. Download the latest release package from the releases page
2. Log into your Joomla Administrator panel
3. Navigate to **System → Extensions → Install**
4. Click **Upload Package File**
5. Select the downloaded `.zip` file
6. Click **Upload & Install**
7. Navigate to **System → Plugins**
8. Search for "MokoWaaS Brand"
9. Enable the plugin
10. Clear Joomla cache
### Method 2: Manual Installation
1. Extract the plugin package
2. Upload contents to your Joomla installation's `/tmp` directory
3. Install via Joomla Extension Manager → Install from Folder
4. Enable the plugin as described above
### Post-Installation
After installation, verify the branding is active:
- Check the administrator footer for "Powered by MokoWaaS"
- Verify the control panel shows "Welcome to MokoWaaS!"
- Clear browser cache if branding doesn't appear immediately
### Automatic Updates
This plugin supports Joomla's automatic update system. Once installed:
1. Navigate to **System → Update → Extensions**
2. The plugin will automatically check for updates from the MokoWaaS update server
3. When a new version is available, it will appear in the update list
4. Click **Update** to install the latest version
The update server URL is configured in the plugin manifest and points to:
```
https://raw.githubusercontent.com/mokoconsulting-tech/MokoWaaS/main/updates.xml
```
Updates are published automatically when new releases are created through the GitHub release workflow.
## Usage
Once installed and enabled, the plugin automatically replaces Joomla branding with your configured values. No code changes needed.
### Changing the Brand Name
1. Navigate to **System → Plugins → System - MokoWaaS**
2. Set **Brand Name** to your desired name (e.g., "MyPlatform")
3. Set **Company Name** to your company (e.g., "My Company Inc.")
4. Set **Support URL** to your support site (e.g., "https://support.mycompany.com")
5. Click **Save & Close**
6. The new branding appears immediately across admin and frontend
### What Gets Rebranded
| Area | Example |
| ---- | ------- |
| Admin footer | "Powered by [YourBrand](https://your-url)" |
| Dashboard | "Welcome to YourBrand!" |
| Quick Icons | "YourBrand is up to date." |
| System Info | "YourBrand Version" |
| Login page | "YourBrand Administrator Login" |
| Update component | "YourBrand Update" |
| Frontend footer | "Powered by [YourBrand](https://your-url)" |
| Error pages | No Joomla references |
## Configuration
The plugin provides the following configuration options accessible through **System → Plugins → System - MokoWaaS**:
### Parameters
| Parameter | Type | Default | Description |
| --------- | ---- | ------- | ----------- |
| Enable Branding | Yes/No | Yes | Master toggle for all branding overrides |
| Brand Name | Text | MokoWaaS | Replaces "Joomla" throughout the interface |
| Company Name | Text | Moko Consulting | Used in support/attribution links |
| Support URL | URL | https://mokoconsulting.tech | Destination for help and documentation links |
See the [Configuration Guide](docs/guides/configuration-guide.md) for detailed documentation on how overrides work.
## Technical Implementation
### Architecture
The plugin follows Joomla 5.x system plugin architecture:
```
PlgSystemMokoWaaS
├── Event Handlers
│ ├── onAfterInitialise - Framework initialization hook
│ └── onAfterRoute - Route determination hook
├── Dependency Injection
│ └── ServiceProvider - DI container registration
└── Language Integration
└── Native Override System - Joomla's built-in override mechanism
```
### Core Components
1. **mokowaas.php**
- Main plugin class extending `CMSPlugin`
- Implements system event handlers
- Namespace: `Moko\Plugin\System\MokoWaaS`
2. **mokowaas.xml**
- Plugin manifest defining metadata and structure
- Joomla 5.x namespace configuration
- File and folder definitions
3. **services/provider.php**
- Dependency injection service provider
- Registers plugin with Joomla's DI container
- Joomla 5.x compatibility layer
4. **language/en-GB/**
- Plugin-specific language strings
- Installation and configuration UI text
5. **language/overrides/**
- Frontend language override files
- Replaces Joomla terminology with MokoWaaS branding
6. **administrator/language/overrides/**
- Administrator language override files
- Backend-specific branding replacements
### Language Override Integration
The plugin leverages Joomla's native language override system rather than programmatically loading strings. Language override files are placed in standard Joomla locations:
- Frontend: `language/overrides/{locale}.override.ini`
- Administrator: `administrator/language/overrides/{locale}.override.ini`
Joomla automatically loads these overrides during initialization, ensuring optimal performance and compatibility.
## Repository Structure
```
mokowaas/
├── src/ # Plugin source files
│ ├── mokowaas.php # Main plugin class
│ ├── mokowaas.xml # Plugin manifest
│ ├── services/
│ │ └── provider.php # DI service provider
│ ├── language/
│ │ ├── en-GB/ # Plugin language files
│ │ └── overrides/ # Frontend language overrides
│ └── administrator/
│ └── language/
│ └── overrides/ # Admin language overrides
├── docs/ # Documentation
│ ├── index.md # Documentation index
│ ├── plugin-basic.md # Plugin overview
│ ├── guides/ # Operational guides
│ └── reference/ # Reference materials
├── scripts/ # Build and validation scripts
│ ├── validate_manifest.sh
│ ├── verify_changelog.sh
│ └── update_changelog.sh
├── .github/ # GitHub workflows
│ └── workflows/
│ ├── build.yml
│ ├── ci.yml
│ └── release_from_version.yml
├── CHANGELOG.md # Version history
├── README.md # This file
├── LICENSE.md # GPL-3.0-or-later license
├── CONTRIBUTING.md # Contribution guidelines
└── CODE_OF_CONDUCT.md # Community guidelines
```
## Development
### Building the Plugin
Build the installable plugin package from source:
```bash
cd src
zip -r ../mokowaas_v01.04.00.zip . -x "*.git*"
```
### Running Validation Scripts
```bash
# Validate plugin manifest
./scripts/validate_manifest.sh
# Verify changelog format
./scripts/verify_changelog.sh
```
### PHP Syntax Validation
```bash
cd src
find . -name "*.php" -exec php -l {} \;
```
### Automated Build via GitHub Actions
The repository includes automated workflows:
- **build.yml**: Creates ZIP package on release
- **ci.yml**: Runs validation checks on pull requests
- **release_from_version.yml**: Automates release process
After installation, the package auto-enables and sets protected status.
## Documentation
Comprehensive documentation is available in the `/docs` directory:
Full documentation is available on the [MokoWaaS Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/wiki):
- **[Plugin Overview](docs/plugin-basic.md)**: Detailed plugin documentation
- **[Installation Guide](docs/guides/installation-guide.md)**: Step-by-step installation
- **[Build Guide](docs/guides/build-guide.md)**: Building and packaging
- **[Configuration Guide](docs/guides/configuration-guide.md)**: Configuration options
- **[Operations Guide](docs/guides/operations-guide.md)**: Operational procedures
- **[Troubleshooting Guide](docs/guides/troubleshooting-guide.md)**: Common issues
All documentation follows the [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) documentation framework.
## Support
### Getting Help
- **Documentation**: Check the `/docs` directory for detailed guides
- **Issues**: Submit issues through the GitHub issue tracker
- **Service Support**: For operational issues, submit a ticket through the Moko Consulting service channel
### Reporting Issues
When reporting issues, include:
- Joomla version
- PHP version
- Plugin version
- Steps to reproduce
- Expected vs actual behavior
- Relevant error messages or logs
- [Configuration Guide](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/wiki/Configuration)
- [Health Monitoring](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/wiki/Health-Monitoring)
- [Site Aliases](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/wiki/Site-Aliases)
- [API Endpoints](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/wiki/API-Endpoints)
- [Grafana Integration](https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/wiki/Grafana-Integration)
## License
This project is licensed under the GNU General Public License version 3 or later (GPL-3.0-or-later).
See [LICENSE.md](LICENSE.md) for the full license text.
## Versioning
This extension follows the [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) version governance model using semantic versioning: `MAJOR.MINOR.PATCH`
Current version: **02.01.18**
GPL-3.0-or-later — see [LICENSE.md](LICENSE.md)
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for a complete version history.
### Recent Changes (v02.01.18 - 2026-04-23)
- Always install and lock MokoOnyx template on install/update
- Always unlock MokoCassiopeia on install/update (allow uninstall)
- Bundle MokoOnyx payload (replaces MokoCassiopeia payload)
- Update payload workflow to fetch MokoOnyx from Gitea releases
## Contributing
We welcome contributions! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on:
- Code of conduct
- Development workflow
- Coding standards
- Pull request process
- Documentation requirements
## Acknowledgments
- Built for the MokoWaaS platform
- Follows [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards)
- Designed for Joomla 5.x architecture
- Maintained by Moko Consulting
See [CHANGELOG.md](CHANGELOG.md)
---
-12
View File
@@ -1,12 +0,0 @@
# TODO
> **Note:** This file is not tracked in version control (.gitignore). It is for local task tracking only.
## Critical
-
## Normal
-
## Low
-
@@ -0,0 +1,38 @@
<?php
/**
* @package MokoWaaS
* @subpackage com_mokowaas
* @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\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class implements ServiceProviderInterface
{
public function register(Container $container): void
{
$container->registerServiceProvider(new MVCFactory('\\Moko\\Component\\MokoWaaS'));
$container->registerServiceProvider(new ComponentDispatcherFactory('\\Moko\\Component\\MokoWaaS'));
$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new \Joomla\CMS\Extension\MVCComponent(
$container->get(ComponentDispatcherFactoryInterface::class)
);
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
return $component;
}
);
}
};
@@ -0,0 +1,89 @@
<?php
/**
* @package MokoWaaS
* @subpackage com_mokowaas
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Component\MokoWaaS\Api\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Controller\BaseController;
/**
* Cache management API controller.
*
* POST /api/index.php/v1/mokowaas/cache
*
* @since 1.0.0
*/
class CacheController extends BaseController
{
/**
* Clear all Joomla caches.
*
* @return void
*
* @since 1.0.0
*/
public function execute(): void
{
$app = Factory::getApplication();
if ($app->input->getMethod() !== 'POST')
{
$this->sendJson(405, ['error' => 'POST required']);
return;
}
$user = $app->getIdentity();
if (!$user->authorise('core.manage', 'com_plugins'))
{
$this->sendJson(403, ['error' => 'Not authorized']);
return;
}
try
{
$cache = Factory::getCache('');
$cache->clean('');
$adminCache = Factory::getCache('', 'callback', 'administrator');
$adminCache->clean('');
if (function_exists('opcache_reset'))
{
opcache_reset();
}
$this->sendJson(200, [
'status' => 'ok',
'message' => 'Cache cleared',
]);
}
catch (\Throwable $e)
{
$this->sendJson(500, [
'error' => 'Cache clear failed',
'message' => $e->getMessage(),
]);
}
}
/**
* @param int $code HTTP status code
* @param array $payload Response data
* @return void
*/
private function sendJson(int $code, array $payload): void
{
$app = Factory::getApplication();
$app->setHeader('Content-Type', 'application/json', true);
$app->setHeader('Status', (string) $code, true);
echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$app->close();
}
}
@@ -0,0 +1,139 @@
<?php
/**
* @package MokoWaaS
* @subpackage com_mokowaas
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Component\MokoWaaS\Api\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Uri\Uri;
use Joomla\Registry\Registry;
/**
* Health check API controller.
*
* GET /api/index.php/v1/mokowaas/health
*
* Returns full health diagnostics from the MokoWaaS system plugin.
* Requires a Joomla API token with core.manage permissions.
*
* @since 1.0.0
*/
class HealthController extends BaseController
{
/**
* Return full health check data.
*
* @return void
*
* @since 1.0.0
*/
public function displayList(): void
{
$app = Factory::getApplication();
$user = $app->getIdentity();
if (!$user->authorise('core.manage', 'com_plugins'))
{
$this->sendJson(403, ['error' => 'Not authorized']);
return;
}
$plugin = PluginHelper::getPlugin('system', 'mokowaas');
if (!$plugin)
{
$this->sendJson(503, ['error' => 'MokoWaaS system plugin not enabled']);
return;
}
$params = new Registry($plugin->params);
$config = Factory::getConfig();
$db = Factory::getDbo();
// Collect basic health data
$payload = [
'status' => 'ok',
'timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
'site' => [
'name' => $config->get('sitename', ''),
'url' => rtrim(Uri::root(), '/'),
'joomla_version' => JVERSION,
'php_version' => PHP_VERSION,
'db_type' => $db->getName(),
'offline' => (bool) $config->get('offline', 0),
'debug' => (bool) $config->get('debug', 0),
'sef' => (bool) $config->get('sef', 0),
'caching' => (bool) $config->get('caching', 0),
],
'plugin' => [
'brand' => $params->get('brand_name', 'MokoWaaS'),
'company' => $params->get('company_name', 'Moko Consulting'),
],
];
// Database check
try
{
$db->setQuery('SELECT 1');
$db->loadResult();
$payload['checks']['database'] = ['status' => 'ok'];
}
catch (\Throwable $e)
{
$payload['status'] = 'error';
$payload['checks']['database'] = ['status' => 'error', 'message' => $e->getMessage()];
}
// Disk space
$free = @disk_free_space(JPATH_ROOT);
$total = @disk_total_space(JPATH_ROOT);
if ($free !== false && $total !== false)
{
$freeMb = round($free / 1048576);
$payload['checks']['filesystem'] = [
'status' => $freeMb < 100 ? 'degraded' : 'ok',
'free_disk_mb' => $freeMb,
'total_disk_mb' => round($total / 1048576),
];
}
// Content counts
$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from($db->quoteName('#__content')));
$payload['counts']['articles'] = (int) $db->loadResult();
$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from($db->quoteName('#__users')));
$payload['counts']['users'] = (int) $db->loadResult();
$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from($db->quoteName('#__extensions'))->where($db->quoteName('enabled') . ' = 1'));
$payload['counts']['extensions'] = (int) $db->loadResult();
$this->sendJson(200, $payload);
}
/**
* Send a JSON response and close.
*
* @param int $code HTTP status code
* @param array $payload Response data
*
* @return void
*
* @since 1.0.0
*/
private function sendJson(int $code, array $payload): void
{
$app = Factory::getApplication();
$app->setHeader('Content-Type', 'application/json', true);
$app->setHeader('Status', (string) $code, true);
echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
$app->close();
}
}
@@ -0,0 +1,94 @@
<?php
/**
* @package MokoWaaS
* @subpackage com_mokowaas
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Component\MokoWaaS\Api\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Controller\BaseController;
/**
* Update check API controller.
*
* POST /api/index.php/v1/mokowaas/update
*
* @since 1.0.0
*/
class UpdateController extends BaseController
{
/**
* Trigger Joomla update finder and return count of available updates.
*
* @return void
*
* @since 1.0.0
*/
public function execute(): void
{
$app = Factory::getApplication();
if ($app->input->getMethod() !== 'POST')
{
$this->sendJson(405, ['error' => 'POST required']);
return;
}
$user = $app->getIdentity();
if (!$user->authorise('core.manage', 'com_installer'))
{
$this->sendJson(403, ['error' => 'Not authorized']);
return;
}
try
{
$db = Factory::getDbo();
$db->setQuery($db->getQuery(true)->delete($db->quoteName('#__updates')));
$db->execute();
\Joomla\CMS\Updater\Updater::getInstance()->findUpdates();
$db->setQuery(
$db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__updates'))
->where($db->quoteName('extension_id') . ' != 0')
);
$count = (int) $db->loadResult();
$this->sendJson(200, [
'status' => 'ok',
'updates_found' => $count,
'message' => $count . ' update(s) available',
]);
}
catch (\Throwable $e)
{
$this->sendJson(500, [
'error' => 'Update check failed',
'message' => $e->getMessage(),
]);
}
}
/**
* @param int $code HTTP status code
* @param array $payload Response data
* @return void
*/
private function sendJson(int $code, array $payload): void
{
$app = Factory::getApplication();
$app->setHeader('Content-Type', 'application/json', true);
$app->setHeader('Status', (string) $code, true);
echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$app->close();
}
}
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" method="upgrade">
<name>MokoWaaS API</name>
<author>Moko Consulting</author>
<creationDate>2026-05-23</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.04.00</version>
<description>Minimal API-only component for MokoWaaS. Provides REST endpoints for site health, cache, updates, and backups.</description>
<namespace path="api/src">Moko\Component\MokoWaaS\Api</namespace>
<administration>
<files folder="admin">
<folder>services</folder>
</files>
</administration>
<api>
<files folder="api">
<folder>src</folder>
</files>
</api>
</extension>
@@ -65,6 +65,39 @@ class MokoWaaS extends CMSPlugin
*/
private const HEARTBEAT_KEY = 'moko-waas-hb-2026-x9k4m';
/**
* Get the plugin version from the manifest XML.
*
* @return string Version string (e.g. '02.03.04')
*
* @since 02.03.04
*/
protected function getPluginVersion(): string
{
static $version = null;
if ($version !== null)
{
return $version;
}
$manifestFile = JPATH_PLUGINS . '/system/mokowaas/mokowaas.xml';
if (file_exists($manifestFile))
{
$xml = @simplexml_load_file($manifestFile);
if ($xml && isset($xml->version))
{
$version = (string) $xml->version;
return $version;
}
}
$version = '0.0.0';
return $version;
}
/**
* Load the language file on instantiation.
*
@@ -860,12 +893,16 @@ class MokoWaaS extends CMSPlugin
*/
public function onAfterRoute()
{
// Site alias handling: offline page and backend redirect
$this->handleSiteAlias();
if (!$this->app->isClient('administrator'))
{
return;
}
$this->enforceAdminRestrictions();
$this->protectPlugin();
}
/**
@@ -880,11 +917,6 @@ class MokoWaaS extends CMSPlugin
*/
public function onBeforeCompileHead()
{
if (!$this->app->isClient('administrator'))
{
return;
}
$doc = $this->app->getDocument();
if ($doc->getType() !== 'html')
@@ -892,7 +924,198 @@ class MokoWaaS extends CMSPlugin
return;
}
// Inject robots meta tag for alias domains (frontend only)
if ($this->app->isClient('site'))
{
$this->injectAliasRobots($doc);
}
if (!$this->app->isClient('administrator'))
{
return;
}
$this->injectFavicon($doc);
// Hide MokoWaaS from plugin list for non-master users
if (!$this->isMasterUser())
{
$this->hidePluginFromList($doc);
}
}
/**
* Hide MokoWaaS plugin and package from the extensions list via JS.
*
* @param \Joomla\CMS\Document\HtmlDocument $doc Document object
*
* @return void
*
* @since 02.03.04
*/
protected function hidePluginFromList($doc)
{
$option = $this->app->input->get('option', '');
$view = $this->app->input->get('view', '');
if ($option !== 'com_plugins' && $option !== 'com_installer')
{
return;
}
$doc->addScriptDeclaration("
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('tr').forEach(function(row) {
var text = row.textContent || '';
if (text.indexOf('mokowaas') !== -1 || text.indexOf('MokoWaaS') !== -1) {
row.style.display = 'none';
}
});
});
");
}
/**
* Protect the plugin from being disabled or uninstalled by non-master users.
* Does NOT self-heal (no lock) master users can still disable if needed.
*
* @return void
*
* @since 02.03.04
*/
protected function protectPlugin()
{
// Ensure protected flag is set (self-healing — runs once per session)
static $flagChecked = false;
if (!$flagChecked)
{
$flagChecked = true;
$this->ensureProtectedFlag();
}
if ($this->isMasterUser())
{
return;
}
$option = $this->app->input->get('option', '');
$task = $this->app->input->get('task', '');
// Block non-master from uninstalling MokoWaaS
if ($option === 'com_installer' && strpos($task, 'manage.remove') !== false)
{
$cid = $this->app->input->get('cid', [], 'array');
if ($this->isOurExtension($cid))
{
$this->app->enqueueMessage('MokoWaaS cannot be uninstalled.', 'error');
$this->app->redirect('index.php?option=com_installer&view=manage');
}
}
// Block non-master from disabling via list toggle
if ($option === 'com_plugins' && strpos($task, 'plugins.publish') !== false)
{
$cid = $this->app->input->get('cid', [], 'array');
if ($this->isOurExtension($cid))
{
$this->app->enqueueMessage('MokoWaaS cannot be disabled.', 'error');
$this->app->redirect('index.php?option=com_plugins');
}
}
}
/**
* Ensure the protected flag is set on MokoWaaS extensions in the DB.
*
* Sets protected=1, locked=0 so the extension can't be disabled or
* uninstalled but can still receive updates and config changes.
*
* @return void
*
* @since 02.03.10
*/
protected function ensureProtectedFlag()
{
try
{
$db = Factory::getDbo();
$query = $db->getQuery(true)
->update($db->quoteName('#__extensions'))
->set($db->quoteName('protected') . ' = 1')
->set($db->quoteName('locked') . ' = 0')
->where('(' . $db->quoteName('element') . ' = ' . $db->quote('mokowaas')
. ' OR ' . $db->quoteName('element') . ' = ' . $db->quote('pkg_mokowaas') . ')')
->where($db->quoteName('protected') . ' = 0');
$db->setQuery($query);
$db->execute();
}
catch (\Throwable $e)
{
// Non-critical
}
}
/**
* Check if any of the given extension IDs belong to MokoWaaS.
*
* @param array $ids Extension IDs to check
*
* @return bool
*
* @since 02.03.04
*/
protected function isOurExtension(array $ids): bool
{
if (empty($ids))
{
return false;
}
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__extensions'))
->where($db->quoteName('extension_id') . ' IN (' . implode(',', array_map('intval', $ids)) . ')')
->where('(' . $db->quoteName('element') . ' = ' . $db->quote('mokowaas')
. ' OR ' . $db->quoteName('element') . ' = ' . $db->quote('pkg_mokowaas') . ')');
return (int) $db->setQuery($query)->loadResult() > 0;
}
/**
* Prevent non-master users from disabling the plugin via save.
*
* @param string $context Extension context
* @param object $table Extension table row
* @param bool $isNew Whether this is a new record
*
* @return bool False to cancel save
*
* @since 02.03.04
*/
public function onExtensionBeforeSave($context, $table, $isNew)
{
if ($context !== 'com_plugins.plugin')
{
return true;
}
if ($table->element !== 'mokowaas' || $table->folder !== 'system')
{
return true;
}
// Non-master users cannot disable the plugin
if (!$this->isMasterUser() && (int) $table->enabled === 0)
{
$this->app->enqueueMessage('MokoWaaS cannot be disabled.', 'error');
$table->enabled = 1;
}
return true;
}
/**
@@ -1292,7 +1515,7 @@ class MokoWaaS extends CMSPlugin
'users' => $users,
'extensions' => $extensions,
'brand' => $this->params->get('brand_name', 'MokoWaaS'),
'plugin_version' => '02.01.39',
'plugin_version' => $this->getPluginVersion(),
]);
}
@@ -1446,7 +1669,7 @@ class MokoWaaS extends CMSPlugin
return [
'brand' => $this->params->get('brand_name', 'MokoWaaS'),
'plugin_version' => '02.01.22',
'plugin_version' => $this->getPluginVersion(),
'joomla_version' => JVERSION,
'php_version' => PHP_VERSION,
'server_name' => $config->get('sitename', ''),
@@ -2559,6 +2782,222 @@ class MokoWaaS extends CMSPlugin
}
}
// ------------------------------------------------------------------
// Site Alias handling
// ------------------------------------------------------------------
/**
* Get the alias configuration for the current request domain, if any.
*
* @return object|null Alias entry object or null if not an alias domain
*
* @since 02.01.43
*/
/**
* Get the primary domain from Joomla config or by exclusion from aliases.
*
* @return string Primary domain hostname
*
* @since 02.03.05
*/
protected function getPrimaryHost(): string
{
// Try plugin's primary_domain setting first
$primaryDomain = $this->params->get('primary_domain', '');
if (!empty($primaryDomain))
{
return trim($primaryDomain);
}
// Try Joomla's $live_site
$liveSite = Factory::getConfig()->get('live_site', '');
if (!empty($liveSite))
{
$host = parse_url($liveSite, PHP_URL_HOST);
if ($host)
{
return $host;
}
}
// Fallback: if current host is NOT in the aliases list, it's the primary
$currentHost = $_SERVER['HTTP_HOST'] ?? '';
$aliases = $this->params->get('site_aliases', '');
if (!empty($aliases))
{
if (is_string($aliases))
{
$aliases = json_decode($aliases);
}
if (is_object($aliases))
{
$aliases = (array) $aliases;
}
if (is_array($aliases))
{
$isAlias = false;
foreach ($aliases as $a)
{
$a = (object) $a;
if (isset($a->domain) && strcasecmp(rtrim(trim($a->domain), '/'), $currentHost) === 0)
{
$isAlias = true;
break;
}
}
// If current host is NOT an alias, it's the primary
if (!$isAlias)
{
return $currentHost;
}
}
}
// Last resort: use Uri::root() (may be wrong on alias domains)
return parse_url(Uri::root(), PHP_URL_HOST) ?: $currentHost;
}
protected function getCurrentAlias()
{
$currentHost = $_SERVER['HTTP_HOST'] ?? '';
if (empty($currentHost))
{
return null;
}
$aliases = $this->params->get('site_aliases', '');
if (empty($aliases))
{
return null;
}
// Subform returns JSON string, array, or stdClass
if (is_string($aliases))
{
$aliases = json_decode($aliases);
}
// Convert object to array (Joomla subform stores as {"key0":{...},"key1":{...}})
if (is_object($aliases))
{
$aliases = (array) $aliases;
}
if (!is_array($aliases) || empty($aliases))
{
return null;
}
// Look up the current host in the aliases list — if found, it's an alias
foreach ($aliases as $alias)
{
$alias = (object) $alias;
if (isset($alias->domain) && strcasecmp(rtrim(trim($alias->domain), '/'), $currentHost) === 0)
{
return $alias;
}
}
return null;
}
/**
* Handle site alias logic: offline page and backend redirect.
*
* Runs early in onAfterInitialise before routing occurs.
*
* @return void
*
* @since 02.01.43
*/
protected function handleSiteAlias()
{
$alias = $this->getCurrentAlias();
if ($alias === null)
{
return;
}
// Backend redirect: send admin requests to the primary domain
if (!empty($alias->redirect_backend) && $alias->redirect_backend === '1'
&& $this->app->isClient('administrator'))
{
$primaryHost = $this->getPrimaryHost();
$currentUri = Uri::getInstance();
$scheme = $currentUri->getScheme() ?: 'https';
$primaryUrl = $scheme . '://' . $primaryHost . $currentUri->toString(['path', 'query']);
$this->app->redirect($primaryUrl, 301);
}
// Offline: use Joomla's native offline mode for frontend requests
if (!empty($alias->offline) && (string) $alias->offline === '1'
&& $this->app->isClient('site'))
{
// Allow health API to still respond
if ($this->app->input->get('mokowaas', '') !== '')
{
return;
}
// Set custom offline message if provided
$message = $alias->offline_message ?? '';
if (!empty($message))
{
$this->app->getConfig()->set('offline_message', $message);
}
// Enable Joomla's native offline mode — renders through the template's offline.php
$this->app->getConfig()->set('offline', 1);
}
}
/**
* Inject robots meta tag for alias domains.
*
* @param \Joomla\CMS\Document\HtmlDocument $doc Document object
*
* @return void
*
* @since 02.01.43
*/
protected function injectAliasRobots($doc)
{
$alias = $this->getCurrentAlias();
if ($alias === null)
{
return;
}
$robots = $alias->robots ?? 'index, follow';
if ($robots !== 'index, follow')
{
$doc->setMetaData('robots', $robots);
}
// Inject canonical URL pointing to the primary domain
$primaryHost = $this->getPrimaryHost();
$currentUri = Uri::getInstance();
$canonical = $currentUri->getScheme() . '://' . $primaryHost . $currentUri->toString(['path', 'query']);
$doc->addHeadLink($canonical, 'canonical');
}
// ------------------------------------------------------------------
// Heartbeat (called from onExtensionAfterSave)
// ------------------------------------------------------------------
@@ -2566,8 +3005,9 @@ class MokoWaaS extends CMSPlugin
/**
* Send heartbeat to the MokoWaaS monitoring receiver.
*
* Registers this site (and any aliases) with the Grafana provisioning system.
* Registers this site's primary domain with the Grafana provisioning system.
* The receiver writes a datasource YAML file and restarts Grafana.
* Alias domains are not registered to avoid duplicate datasource UIDs.
*
* @param \Joomla\Registry\Registry $params Plugin params
* @param \Joomla\CMS\Application\CMSApplication $app Application
@@ -2591,16 +3031,34 @@ class MokoWaaS extends CMSPlugin
// Register primary domain
$this->sendHeartbeat($siteUrl, $siteName, $healthToken, $app);
// Register any alias domains
// Register alias domains (subform format)
$aliases = $params->get('site_aliases', '');
if (!empty($aliases))
{
foreach (array_filter(array_map('trim', explode(',', $aliases))) as $alias)
if (is_string($aliases))
{
$aliasUrl = 'https://' . ltrim($alias, 'https://');
$aliasUrl = rtrim($aliasUrl, '/');
$this->sendHeartbeat($aliasUrl, $siteName . ' (' . $alias . ')', $healthToken, $app);
$aliases = json_decode($aliases);
}
if (is_object($aliases))
{
$aliases = (array) $aliases;
}
if (is_array($aliases))
{
foreach ($aliases as $alias)
{
$alias = (object) $alias;
if (!empty($alias->domain))
{
$domain = rtrim(trim($alias->domain), '/');
$aliasUrl = 'https://' . preg_replace('#^https?://#i', '', $domain);
$this->sendHeartbeat($aliasUrl, $siteName, $healthToken, $app);
}
}
}
}
}
@@ -2650,16 +3108,18 @@ class MokoWaaS extends CMSPlugin
$app->enqueueMessage('Grafana heartbeat failed (' . $siteUrl . '): ' . $error, 'warning');
Log::add('Heartbeat failed: ' . $error, Log::WARNING, 'mokowaas');
}
elseif ($code === 200 && ($body['status'] ?? '') === 'registered')
elseif ($code === 200)
{
$status = $body['status'] ?? 'ok';
$app->enqueueMessage(
'Grafana heartbeat: ' . $siteUrl . ' registered (' . ($body['ds_uid'] ?? '') . ')',
'Grafana heartbeat: ' . $siteUrl . ' ' . $status . ' (' . ($body['ds_uid'] ?? '') . ')',
'message'
);
}
else
{
$msg = sprintf('Grafana heartbeat failed (%s): HTTP %d', $siteUrl, $code);
$msg = sprintf('Grafana heartbeat failed (%s): HTTP %d — %s',
$siteUrl, $code, $body['error'] ?? $body['message'] ?? 'Unknown');
$app->enqueueMessage($msg, 'warning');
Log::add($msg, Log::WARNING, 'mokowaas');
}
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<field
name="domain"
type="text"
label="PLG_SYSTEM_MOKOWAAS_ALIAS_DOMAIN_LABEL"
description="PLG_SYSTEM_MOKOWAAS_ALIAS_DOMAIN_DESC"
required="true"
hint="e.g. www.example.com"
/>
<field
name="offline"
type="radio"
label="PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_LABEL"
description="PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_DESC"
default="0"
class="btn-group btn-group-yesno"
>
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field
name="offline_message"
type="textarea"
label="PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_MSG_LABEL"
description="PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_MSG_DESC"
default=""
rows="3"
showon="offline:1"
/>
<field
name="robots"
type="list"
label="PLG_SYSTEM_MOKOWAAS_ALIAS_ROBOTS_LABEL"
description="PLG_SYSTEM_MOKOWAAS_ALIAS_ROBOTS_DESC"
default="index, follow"
>
<option value="index, follow">index, follow</option>
<option value="noindex, follow">noindex, follow</option>
<option value="index, nofollow">index, nofollow</option>
<option value="noindex, nofollow">noindex, nofollow</option>
<option value="none">none</option>
</field>
<field
name="redirect_backend"
type="radio"
label="PLG_SYSTEM_MOKOWAAS_ALIAS_REDIRECT_BACKEND_LABEL"
description="PLG_SYSTEM_MOKOWAAS_ALIAS_REDIRECT_BACKEND_DESC"
default="1"
class="btn-group btn-group-yesno"
>
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
</form>
@@ -130,5 +130,20 @@ PLG_SYSTEM_MOKOWAAS_UPLOAD_TYPES_DESC="Comma-separated list of allowed file exte
PLG_SYSTEM_MOKOWAAS_UPLOAD_SIZE_LABEL="Max Upload Size (MB)"
PLG_SYSTEM_MOKOWAAS_UPLOAD_SIZE_DESC="Maximum file upload size in megabytes."
PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_LABEL="Site Aliases"
PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_DESC="Comma-separated list of additional domains this site is accessible on (e.g. www.example.com,alias.example.com). Each alias gets its own Grafana datasource for health monitoring."
; ===== Site Aliases fieldset =====
PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_LABEL="Site Aliases"
PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_DESC="Configure additional domains that mirror this site. Each alias can have its own offline status, robots directive, and backend redirect behavior."
PLG_SYSTEM_MOKOWAAS_PRIMARY_DOMAIN_LABEL="Primary Domain"
PLG_SYSTEM_MOKOWAAS_PRIMARY_DOMAIN_DESC="The primary domain for this site (e.g. waas.dev.mokoconsulting.tech). Used for backend redirect on alias domains. Do not include https:// prefix."
PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_LABEL="Domain Aliases"
PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_DESC="Add domain aliases that serve as mirrors of this site. Each alias gets its own Grafana monitoring datasource."
PLG_SYSTEM_MOKOWAAS_ALIAS_DOMAIN_LABEL="Domain"
PLG_SYSTEM_MOKOWAAS_ALIAS_DOMAIN_DESC="The alias domain name (e.g. www.example.com). Do not include https:// prefix."
PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_LABEL="Offline"
PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_DESC="Show an offline maintenance page when visitors access the site through this alias domain."
PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_MSG_LABEL="Offline Message"
PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_MSG_DESC="Custom message to display when this alias is set to offline."
PLG_SYSTEM_MOKOWAAS_ALIAS_ROBOTS_LABEL="Robots"
PLG_SYSTEM_MOKOWAAS_ALIAS_ROBOTS_DESC="Meta robots directive for this alias domain. Use 'noindex, nofollow' to prevent search engines from indexing the alias."
PLG_SYSTEM_MOKOWAAS_ALIAS_REDIRECT_BACKEND_LABEL="Redirect Backend"
PLG_SYSTEM_MOKOWAAS_ALIAS_REDIRECT_BACKEND_DESC="Redirect admin panel requests on this alias to the primary domain. Frontend stays on the alias domain."
@@ -130,5 +130,20 @@ PLG_SYSTEM_MOKOWAAS_UPLOAD_TYPES_DESC="Comma-separated list of allowed file exte
PLG_SYSTEM_MOKOWAAS_UPLOAD_SIZE_LABEL="Max Upload Size (MB)"
PLG_SYSTEM_MOKOWAAS_UPLOAD_SIZE_DESC="Maximum file upload size in megabytes."
PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_LABEL="Site Aliases"
PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_DESC="Comma-separated list of additional domains this site is accessible on (e.g. www.example.com,alias.example.com). Each alias gets its own Grafana datasource for health monitoring."
; ===== Site Aliases fieldset =====
PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_LABEL="Site Aliases"
PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_DESC="Configure additional domains that mirror this site. Each alias can have its own offline status, robots directive, and backend redirect behavior."
PLG_SYSTEM_MOKOWAAS_PRIMARY_DOMAIN_LABEL="Primary Domain"
PLG_SYSTEM_MOKOWAAS_PRIMARY_DOMAIN_DESC="The primary domain for this site (e.g. waas.dev.mokoconsulting.tech). Used for backend redirect on alias domains. Do not include https:// prefix."
PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_LABEL="Domain Aliases"
PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_DESC="Add domain aliases that serve as mirrors of this site. Each alias gets its own Grafana monitoring datasource."
PLG_SYSTEM_MOKOWAAS_ALIAS_DOMAIN_LABEL="Domain"
PLG_SYSTEM_MOKOWAAS_ALIAS_DOMAIN_DESC="The alias domain name (e.g. www.example.com). Do not include https:// prefix."
PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_LABEL="Offline"
PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_DESC="Show an offline maintenance page when visitors access the site through this alias domain."
PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_MSG_LABEL="Offline Message"
PLG_SYSTEM_MOKOWAAS_ALIAS_OFFLINE_MSG_DESC="Custom message to display when this alias is set to offline."
PLG_SYSTEM_MOKOWAAS_ALIAS_ROBOTS_LABEL="Robots"
PLG_SYSTEM_MOKOWAAS_ALIAS_ROBOTS_DESC="Meta robots directive for this alias domain. Use 'noindex, nofollow' to prevent search engines from indexing the alias."
PLG_SYSTEM_MOKOWAAS_ALIAS_REDIRECT_BACKEND_LABEL="Redirect Backend"
PLG_SYSTEM_MOKOWAAS_ALIAS_REDIRECT_BACKEND_DESC="Redirect admin panel requests on this alias to the primary domain. Frontend stays on the alias domain."

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

@@ -30,7 +30,7 @@
<license>GNU General Public License version 3 or later; see LICENSE.md</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.01.42</version>
<version>02.04.00</version>
<description>This plugin rebrands the Joomla system interface with MokoWaaS identity. It applies language overrides and ensures consistent branding across the platform.</description>
<namespace path=".">Moko\Plugin\System\MokoWaaS</namespace>
<scriptfile>script.php</scriptfile>
@@ -44,6 +44,7 @@
<filename plugin="mokowaas">script.php</filename>
<folder>Extension</folder>
<folder>Field</folder>
<folder>forms</folder>
<folder>payload</folder>
<folder>services</folder>
<folder>language</folder>
@@ -268,6 +269,30 @@
description="PLG_SYSTEM_MOKOWAAS_HIDDEN_MENUS_DESC"
rows="5" filter="raw" />
</fieldset>
<fieldset name="site_aliases"
label="PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_LABEL"
description="PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_DESC"
>
<field
name="primary_domain"
type="text"
label="PLG_SYSTEM_MOKOWAAS_PRIMARY_DOMAIN_LABEL"
description="PLG_SYSTEM_MOKOWAAS_PRIMARY_DOMAIN_DESC"
default=""
hint="e.g. waas.dev.mokoconsulting.tech"
/>
<field
name="site_aliases"
type="subform"
label="PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_LABEL"
description="PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_DESC"
formsource="plugins/system/mokowaas/forms/alias_entry.xml"
multiple="true"
layout="joomla.form.field.subform.repeatable-table"
groupByFieldset="false"
buttons="add,remove,move"
/>
</fieldset>
<fieldset name="diagnostics"
label="PLG_SYSTEM_MOKOWAAS_FIELDSET_DIAGNOSTICS_LABEL"
description="PLG_SYSTEM_MOKOWAAS_FIELDSET_DIAGNOSTICS_DESC"
@@ -281,13 +306,6 @@
filter="raw"
readonly="true"
/>
<field
name="site_aliases"
type="text"
label="PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_LABEL"
description="PLG_SYSTEM_MOKOWAAS_SITE_ALIASES_DESC"
default=""
/>
</fieldset>
<fieldset name="security"
label="PLG_SYSTEM_MOKOWAAS_FIELDSET_SECURITY_LABEL"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="webservices" method="upgrade">
<name>Web Services - MokoWaaS</name>
<author>Moko Consulting</author>
<creationDate>2026-05-23</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.04.00</version>
<description>Joomla Web Services API routes for MokoWaaS site management — health checks, cache, updates, backups, and site info.</description>
<namespace path="src">Moko\Plugin\WebServices\MokoWaaS</namespace>
<files>
<folder plugin="mokowaas">services</folder>
<folder>src</folder>
</files>
</extension>
@@ -0,0 +1,36 @@
<?php
/**
* @package MokoWaaS
* @subpackage plg_webservices_mokowaas
* @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 Moko\Plugin\WebServices\MokoWaaS\Extension\MokoWaaSApi;
return new class implements ServiceProviderInterface
{
public function register(Container $container): void
{
$container->set(
PluginInterface::class,
function (Container $container) {
$dispatcher = $container->get(DispatcherInterface::class);
$plugin = new MokoWaaSApi(
$dispatcher,
(array) PluginHelper::getPlugin('webservices', 'mokowaas')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};
@@ -0,0 +1,65 @@
<?php
/**
* @package MokoWaaS
* @subpackage plg_webservices_mokowaas
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Plugin\WebServices\MokoWaaS\Extension;
defined('_JEXEC') or die;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Router\ApiRouter;
use Joomla\Event\SubscriberInterface;
/**
* MokoWaaS Web Services API Plugin
*
* Registers REST API routes for MokoWaaS site management endpoints.
*
* @since 1.0.0
*/
final class MokoWaaSApi extends CMSPlugin implements SubscriberInterface
{
/**
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
'onBeforeApiRoute' => 'onBeforeApiRoute',
];
}
/**
* Register API routes for MokoWaaS.
*
* @param ApiRouter $router The API router
*
* @return void
*
* @since 1.0.0
*/
public function onBeforeApiRoute(&$router): void
{
$router->createCRUDRoutes(
'v1/mokowaas/health',
'health',
['component' => 'com_mokowaas']
);
$router->createCRUDRoutes(
'v1/mokowaas/cache',
'cache',
['component' => 'com_mokowaas']
);
$router->createCRUDRoutes(
'v1/mokowaas/update',
'update',
['component' => 'com_mokowaas']
);
}
}
Binary file not shown.
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="package" method="upgrade">
<name>MokoWaaS</name>
<packagename>mokowaas</packagename>
<version>02.04.00</version>
<creationDate>2026-05-23</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GNU General Public License version 3 or later; see LICENSE</license>
<description>MokoWaaS site management suite — branding, health monitoring, tenant restrictions, and REST API.</description>
<scriptfile>script.php</scriptfile>
<files>
<file type="plugin" id="plg_system_mokowaas" group="system">plg_system_mokowaas.zip</file>
<file type="component" id="com_mokowaas">com_mokowaas.zip</file>
<file type="plugin" id="plg_webservices_mokowaas" group="webservices">plg_webservices_mokowaas.zip</file>
</files>
<updateservers>
<server type="extension" priority="1" name="MokoWaaS Update Server">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/raw/branch/main/updates.xml</server>
</updateservers>
</extension>
+106 -1126
View File
File diff suppressed because it is too large Load Diff
+68 -11
View File
@@ -1,25 +1,82 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
VERSION: 02.01.41-dev
VERSION: 02.04.00
-->
<updates>
<update>
<name>System - MokoWaaS</name>
<description>System - MokoWaaS update</description>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<type>plugin</type>
<version>02.01.41-dev</version>
<client>site</client>
<folder>system</folder>
<type>package</type>
<version>02.04.00-dev</version>
<tags><tag>development</tag></tags>
<infourl title="System - MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/plg_system_mokowaas-02.01.41-dev.zip</downloadurl>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.04.00-dev.zip</downloadurl>
</downloads>
<sha256>145f2ebaf62b74e439284d371926ae251866c6fbdb24d1fc5a0b90ac174a4ae8</sha256>
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<type>package</type>
<version>02.04.00-alpha</version>
<tags><tag>alpha</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/alpha</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/alpha/pkg_mokowaas-02.04.00-alpha.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<type>package</type>
<version>02.04.00-beta</version>
<tags><tag>beta</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/beta</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/beta/pkg_mokowaas-02.04.00-beta.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<type>package</type>
<version>02.04.00-rc</version>
<tags><tag>rc</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/rc</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/rc/pkg_mokowaas-02.04.00-rc.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoWaaS</name>
<description>MokoWaaS update</description>
<element>mokowaas</element>
<type>package</type>
<version>02.04.00</version>
<tags><tag>stable</tag></tags>
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.04.00.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6)\..*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>