Compare commits

...

168 Commits

Author SHA1 Message Date
gitea-actions[bot] c4735a73e8 chore(version): pre-release bump to 02.47.03-dev [skip ci] 2026-06-21 23:32:26 +00:00
gitea-actions[bot] d430e083e4 chore(version): pre-release bump to 02.47.02-dev [skip ci] 2026-06-21 23:31:58 +00:00
Jonathan Miller 52ad2f2b37 docs: update changelog and README for v02.47 release cycle
Universal: Auto Version Bump / Version Bump (push) Successful in 18s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 16s
2026-06-21 18:27:24 -05:00
gitea-actions[bot] 0308df3b53 chore(version): pre-release bump to 02.47.01-dev [skip ci] 2026-06-21 23:26:05 +00:00
Jonathan Miller 21df82dc59 chore: remove ticket guided tour and support menu, rename aliases tab
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Universal: Auto Version Bump / Version Bump (push) Successful in 13s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 29s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 41s
- Removed helpdesk guided tour (tickets moved to MokoSuiteCRM)
- setupSupportMenuItem returns early (no ticket menu)
- Renamed tab: Mirror Domains & Staging Environments
2026-06-21 18:15:59 -05:00
gitea-actions[bot] 9ad828c248 chore(version): pre-release bump to 02.47.00-dev [skip ci] 2026-06-21 23:13:05 +00:00
gitea-actions[bot] 15e891dca2 chore(version): auto-bump patch 02.46.99-dev [skip ci] 2026-06-21 23:12:45 +00:00
Jonathan Miller 6ee0f08f42 fix: undefined $pluginDir in reinstallBrokenPlugins Step 1
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 52s
Universal: Auto Version Bump / Version Bump (push) Successful in 16s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 25s
2026-06-21 18:04:54 -05:00
gitea-actions[bot] d0ff29aed2 chore(version): pre-release bump to 02.46.98-dev [skip ci] 2026-06-21 23:02:06 +00:00
gitea-actions[bot] 6186eec2ca chore(version): pre-release bump to 02.46.97-dev [skip ci] 2026-06-21 23:01:25 +00:00
gitea-actions[bot] 4566071d74 chore(version): auto-bump patch 02.46.96-dev [skip ci] 2026-06-21 23:01:03 +00:00
Jonathan Miller 4a8570f7a3 feat(devtools): Domain Aliases & Staging — repeatable subform table
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 9s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 14s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 55s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 1m7s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 1m8s
Universal: Auto Version Bump / Version Bump (push) Successful in 22s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 1m3s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 26s
Renamed from "Dev Domain" to "Domain Aliases & Staging". Now a
repeatable table (subform) supporting multiple domain aliases, each
with: domain, offline bypass, robots directive, and label.

Core plugin reads all aliases and matches current host against the
list. Auto-includes dev.{primary_domain} when no aliases configured.
2026-06-21 17:59:21 -05:00
gitea-actions[bot] 756e8b664b chore(version): pre-release bump to 02.46.95-dev [skip ci] 2026-06-21 22:51:49 +00:00
gitea-actions[bot] a8224bce93 chore(version): auto-bump patch 02.46.94-dev [skip ci] 2026-06-21 22:51:13 +00:00
Jonathan Miller 711b89ea03 fix(install): two-step plugin reinstall — extract files then create DB records
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
Generic: Project CI / Lint & Validate (pull_request) Successful in 12s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 34s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 34s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 39s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 35s
Step 1: Extract plugin zips to correct directories (always re-extract)
Step 2: For each expected plugin with files but no DB record, INSERT
with all required fields. Set namespace via separate UPDATE (handles
Joomla versions without namespace column).
2026-06-21 17:51:01 -05:00
gitea-actions[bot] 98d7ab3bb3 chore(version): pre-release bump to 02.46.93-dev [skip ci] 2026-06-21 22:43:47 +00:00
gitea-actions[bot] ee4746e8ff chore(version): pre-release bump to 02.46.92-dev [skip ci] 2026-06-21 22:41:46 +00:00
gitea-actions[bot] 6fa3cbbaea chore(version): auto-bump patch 02.46.91-dev [skip ci] 2026-06-21 22:41:31 +00:00
Jonathan Miller 0e2433cf5c fix(install): use Joomla Installer API for plugin reinstall
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 14s
Universal: Auto Version Bump / Version Bump (push) Successful in 23s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 16s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 20s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 52s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 56s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 58s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 1m5s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 48s
Replace raw ZipArchive extract with Joomla\CMS\Installer\Installer::install()
which properly handles namespace registration, extension record creation,
and all internal bookkeeping. Removes manual INSERT from enablePlugin()
since the Installer handles it correctly.
2026-06-21 17:41:09 -05:00
gitea-actions[bot] 59f50867c0 chore(version): pre-release bump to 02.46.90-dev [skip ci] 2026-06-21 22:36:01 +00:00
gitea-actions[bot] 941d49e0ce chore(version): pre-release bump to 02.46.89-dev [skip ci] 2026-06-21 22:33:26 +00:00
gitea-actions[bot] ad7af89228 chore(version): pre-release bump to 02.46.88-dev [skip ci] 2026-06-21 22:30:04 +00:00
gitea-actions[bot] 078caa423b chore(version): auto-bump patch 02.46.87-dev [skip ci] 2026-06-21 22:29:49 +00:00
Jonathan Miller 1583e98cea fix(install): always re-extract plugin zips on update, pretty cpanel labels
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 10s
Generic: Project CI / Lint & Validate (pull_request) Successful in 18s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 19s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 15s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 49s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 1m0s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 1m5s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
- reinstallBrokenPlugins now re-extracts ALL expected plugins on every
  update (removes old dir first) to ensure files stay current
- Cpanel plugin labels: Core Engine, Web Firewall, Tenant Guard,
  Dev Tools, Offline Bypass, GeoIP Lookup, License Manager
2026-06-21 17:26:54 -05:00
gitea-actions[bot] a737ac9106 chore(version): pre-release bump to 02.46.86-dev [skip ci] 2026-06-21 22:22:00 +00:00
gitea-actions[bot] d03269c1ca chore(version): auto-bump patch 02.46.85-dev [skip ci] 2026-06-21 22:21:46 +00:00
Jonathan Miller 29079c10e2 feat(devtools): implement dev domain offline bypass and robots logic
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Project CI / Lint & Validate (pull_request) Successful in 16s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 16s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 12s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 42s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 47s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 48s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
handleSiteAlias() and injectAliasRobots() now read from DevTools
plugin params instead of hardcoding dev.{primary_domain}. Supports
custom domain, configurable offline bypass, and robots directive.
Uses static cache to avoid repeated DB queries per request.
2026-06-21 17:20:19 -05:00
gitea-actions[bot] b433865a6d chore(version): pre-release bump to 02.46.84-dev [skip ci] 2026-06-21 21:41:00 +00:00
gitea-actions[bot] b3cba3ea78 chore(version): pre-release bump to 02.46.83-dev [skip ci] 2026-06-21 21:26:22 +00:00
gitea-actions[bot] 3e79014b97 chore(version): auto-bump patch 02.46.82-dev [skip ci] 2026-06-21 21:26:08 +00:00
Jonathan Miller 62d213027f Merge remote-tracking branch 'origin/dev' into dev
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 8s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: Auto Version Bump / Version Bump (push) Successful in 16s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 13s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 19s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 39s
Generic: Project CI / Lint & Validate (pull_request) Successful in 49s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 51s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 53s
# Conflicts:
#	source/packages/plg_task_mokosuiteclient_tickets/mokosuiteclient_tickets.xml
2026-06-21 16:25:48 -05:00
Jonathan Miller b2d1e4ba23 chore: remove helpdesk/ticket system (migrating to MokoSuiteCRM #67)
- Removed ticket views, models, controllers (ticket, tickets, ticketsettings)
- Removed plg_task_mokosuiteclient_tickets plugin and source
- Removed from package manifest, enablePlugin, protectExtensions
- Removed Helpdesk entry from sidebar menu
- Ticket tables kept in SQL for migration compatibility
- See MokoSuiteClient#233, MokoSuiteCRM#67
2026-06-21 15:50:29 -05:00
gitea-actions[bot] d4c4d1be2d chore(version): pre-release bump to 02.46.81-dev [skip ci] 2026-06-21 20:35:25 +00:00
gitea-actions[bot] 5ec943b90e chore(version): pre-release bump to 02.46.80-dev [skip ci] 2026-06-21 16:59:49 +00:00
gitea-actions[bot] 09553be73b chore(version): auto-bump patch 02.46.79-dev [skip ci] 2026-06-21 16:59:38 +00:00
Jonathan Miller a5dd8e395d style(menu): remove child/parent margin — just pad the wrapper
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Project CI / Lint & Validate (pull_request) Successful in 13s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 12s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 34s
Universal: Auto Version Bump / Version Bump (push) Successful in 10s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 35s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 38s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 13s
2026-06-21 11:59:00 -05:00
gitea-actions[bot] fad314eef7 chore(version): pre-release bump to 02.46.78-dev [skip ci] 2026-06-21 16:53:10 +00:00
gitea-actions[bot] 7472866e14 chore(version): auto-bump patch 02.46.77-dev [skip ci] 2026-06-21 16:52:56 +00:00
Jonathan Miller 5ef3279e29 style(menu): add padding-right to sidebar wrapper
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
Generic: Project CI / Lint & Validate (pull_request) Successful in 13s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 31s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 32s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 36s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 32s
2026-06-21 11:52:42 -05:00
gitea-actions[bot] 0f2679d6f1 chore(version): pre-release bump to 02.46.76-dev [skip ci] 2026-06-21 16:50:54 +00:00
gitea-actions[bot] 8746e56860 chore(version): auto-bump patch 02.46.75-dev [skip ci] 2026-06-21 16:50:39 +00:00
Jonathan Miller a198990982 feat(devtools): configurable dev domain with offline bypass
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 3s
Universal: Auto Version Bump / Version Bump (push) Successful in 17s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 13s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
Generic: Project CI / Lint & Validate (pull_request) Successful in 36s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 36s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 39s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 40s
- Dev Domain fieldset in DevTools plugin settings
- Custom dev domain or auto-detect as dev.{primary_domain}
- Offline bypass, robots directive per dev domain
- Core plugin reads dev domain from devtools params
- Sidebar CSS: margin child ul instead of individual icons
2026-06-21 11:50:21 -05:00
gitea-actions[bot] 8632068357 chore(version): pre-release bump to 02.46.74-dev [skip ci] 2026-06-21 16:43:58 +00:00
gitea-actions[bot] ac0f4b30bd chore(version): pre-release bump to 02.46.73-dev [skip ci] 2026-06-21 16:43:39 +00:00
gitea-actions[bot] 66c8d9c574 chore(version): auto-bump patch 02.46.72-dev [skip ci] 2026-06-21 16:43:25 +00:00
Jonathan Miller b62fe22244 fix(health): return error on exceptions, fix MokoSuiteBackup detection
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 19s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 37s
Generic: Project CI / Lint & Validate (pull_request) Successful in 38s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 41s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 41s
- Health check catch blocks now return status:error with message
- MokoSuiteBackup detection returns early and queries correct table
- Added checkMokoSuiteBackup() method for mokosuitebackup_records
2026-06-21 11:43:11 -05:00
gitea-actions[bot] 2d52cd9b05 chore(version): pre-release bump to 02.46.71-dev [skip ci] 2026-06-21 16:33:10 +00:00
gitea-actions[bot] 505da43fc9 chore(version): auto-bump patch 02.46.70-dev [skip ci] 2026-06-21 16:32:47 +00:00
Jonathan Miller c6b1e3dc7b fix: sidebar icon margin, scope orphan deletion to MokoSuiteClient
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Universal: Auto Version Bump / Version Bump (push) Successful in 19s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 30s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 52s
Generic: Project CI / Lint & Validate (pull_request) Successful in 58s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 58s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 52s
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 51s
- Zero left margin on sidebar menu icons
- cleanupEmptyElements only deletes MokoSuiteClient orphan rows
2026-06-21 11:32:24 -05:00
gitea-actions[bot] 111e7c3bf3 chore(version): pre-release bump to 02.46.69-dev [skip ci] 2026-06-21 16:20:11 +00:00
gitea-actions[bot] bfe1456208 chore(version): auto-bump patch 02.46.68-dev [skip ci] 2026-06-21 16:19:59 +00:00
Jonathan Miller 83b6933ff5 feat(heartbeat): include dev_domain in heartbeat payload
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Successful in 13s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 14s
Generic: Project CI / Lint & Validate (pull_request) Successful in 34s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 36s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 40s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 38s
2026-06-21 11:19:46 -05:00
gitea-actions[bot] 6db037f76c chore(version): pre-release bump to 02.46.67-dev [skip ci] 2026-06-21 16:06:41 +00:00
gitea-actions[bot] 68fa3c6c75 chore(version): pre-release bump to 02.46.66-dev [skip ci] 2026-06-21 16:05:42 +00:00
gitea-actions[bot] 53e9037059 chore(version): auto-bump patch 02.46.65-dev [skip ci] 2026-06-21 16:05:29 +00:00
Jonathan Miller 820aca7124 fix(modules): namespace mismatch — cpanel and cache modules not loading
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 12s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 39s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Auto Version Bump / Version Bump (push) Successful in 13s
Generic: Project CI / Lint & Validate (pull_request) Successful in 45s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 47s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 48s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 16s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 35s
Manifest XML namespace didn't match PHP namespace:
- cpanel: MokoSuiteCpanel → MokoSuiteClientCpanel
- cache: MokoSuiteCache → MokoSuiteClientCache

Joomla couldn't find the Dispatcher class so modules silently failed.
2026-06-21 11:04:42 -05:00
gitea-actions[bot] 40b244e6af chore(version): pre-release bump to 02.46.64-dev [skip ci] 2026-06-21 15:58:13 +00:00
gitea-actions[bot] 832b3022ff chore(version): pre-release bump to 02.46.63-dev [skip ci] 2026-06-21 15:56:46 +00:00
gitea-actions[bot] fbe383d543 chore(version): auto-bump patch 02.46.62-dev [skip ci] 2026-06-21 15:56:32 +00:00
Jonathan Miller acef7ac02f feat(firewall): show current IP address in plugin settings
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 12s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
Generic: Project CI / Lint & Validate (pull_request) Successful in 34s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 33s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 36s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 38s
Adds CurrentIpField that displays the admin's IP with a copy button,
making it easy to add to the Trusted IPs list. Shown at the top of
the firewall plugin's Network & Session fieldset.
2026-06-21 10:56:14 -05:00
gitea-actions[bot] 772b549423 chore(version): pre-release bump to 02.46.61-dev [skip ci] 2026-06-21 15:16:33 +00:00
gitea-actions[bot] f7b8bd4fab chore(version): pre-release bump to 02.46.60-dev [skip ci] 2026-06-21 15:16:05 +00:00
gitea-actions[bot] c861017ee0 chore(version): auto-bump patch 02.46.59-dev [skip ci] 2026-06-21 15:15:52 +00:00
Jonathan Miller b916e5a254 fix(install): prevent duplicate extension rows, add dedup cleanup
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 34s
Generic: Project CI / Lint & Validate (pull_request) Successful in 32s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 34s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 41s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- Check row exists before INSERT to prevent duplicates
- Deduplicate existing rows (keep lowest extension_id per element)
2026-06-21 10:15:39 -05:00
gitea-actions[bot] d6a7572067 chore(version): pre-release bump to 02.46.58-dev [skip ci] 2026-06-21 15:11:29 +00:00
gitea-actions[bot] 1f246c3654 chore(version): pre-release bump to 02.46.57-dev [skip ci] 2026-06-21 15:10:53 +00:00
gitea-actions[bot] eaad6f1d9f chore(version): auto-bump patch 02.46.56-dev [skip ci] 2026-06-21 15:10:43 +00:00
Jonathan Miller 0c9af815a6 fix(install): include custom_data and other required fields in extension INSERT
Universal: Auto Version Bump / Version Bump (push) Successful in 9s
Generic: Project CI / Lint & Validate (pull_request) Successful in 8s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 3s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 3s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 13s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 3s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 38s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 29s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 30s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-21 10:10:31 -05:00
gitea-actions[bot] ce80f9d195 chore(version): pre-release bump to 02.46.55-dev [skip ci] 2026-06-21 15:02:20 +00:00
gitea-actions[bot] b0b5f700ca chore(version): pre-release bump to 02.46.54-dev [skip ci] 2026-06-21 15:01:49 +00:00
gitea-actions[bot] 8bba6a7214 chore(version): auto-bump patch 02.46.53-dev [skip ci] 2026-06-21 15:01:36 +00:00
Jonathan Miller ac0c4cab81 fix(install): create extension records for plugins missing from DB
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 10s
Generic: Project CI / Lint & Validate (pull_request) Successful in 35s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 37s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 37s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 33s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
enablePlugin now INSERTs a new extension row from the manifest XML
when no row exists (neither by element nor by empty element). This
handles the case where cleanupEmptyElements deleted the orphan rows
but the files were reinstalled from the package zip.
2026-06-21 10:01:23 -05:00
gitea-actions[bot] 9849338857 chore(version): pre-release bump to 02.46.52-dev [skip ci] 2026-06-21 14:55:45 +00:00
gitea-actions[bot] 01e5825ff2 chore(version): pre-release bump to 02.46.51-dev [skip ci] 2026-06-21 14:55:00 +00:00
gitea-actions[bot] 0aab262d7c chore(version): auto-bump patch 02.46.50-dev [skip ci] 2026-06-21 14:54:46 +00:00
Jonathan Miller 978dfdfcf3 fix(install): reinstall broken plugins from package zip in postflight
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Generic: Project CI / Lint & Validate (pull_request) Successful in 14s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: Auto Version Bump / Version Bump (push) Successful in 13s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 32s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 35s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 37s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 37s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
When Joomla installs plugins with empty element, files go to the
group root. Postflight now:
1. Deletes orphan rows (empty element or display-name-as-element)
2. Cleans stale files from group roots
3. Checks each expected plugin directory exists
4. If missing, extracts the plugin zip from the package source dir

This guarantees all core plugins are correctly installed after every
update, regardless of the MySQL DEFAULT '' issue.
2026-06-21 09:54:28 -05:00
gitea-actions[bot] a639d1f8fb chore(version): pre-release bump to 02.46.49-dev [skip ci] 2026-06-21 14:49:23 +00:00
gitea-actions[bot] 079aa281d8 chore(version): pre-release bump to 02.46.48-dev [skip ci] 2026-06-21 14:19:47 +00:00
gitea-actions[bot] 1765e9f4b3 chore(version): pre-release bump to 02.46.47-dev [skip ci] 2026-06-21 14:06:36 +00:00
gitea-actions[bot] 84705bd2f7 chore(version): pre-release bump to 02.46.46-dev [skip ci] 2026-06-21 14:05:26 +00:00
gitea-actions[bot] ea83c75396 chore(version): auto-bump patch 02.46.45-dev [skip ci] 2026-06-21 14:05:12 +00:00
Jonathan Miller c28ca37c3c fix(install): delete empty-element rows and all stale files cleanly
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 5s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 16s
Generic: Project CI / Lint & Validate (pull_request) Successful in 31s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 41s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 39s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Relocation approach failed — multiple plugins overwrite each other's
files at the group root. Instead, delete empty-element rows and all
stale files/dirs. On the NEXT update, Joomla creates fresh rows and
installs correctly since the orphan rows are gone.
2026-06-21 09:04:57 -05:00
gitea-actions[bot] 14708637b7 chore(version): pre-release bump to 02.46.44-dev [skip ci] 2026-06-21 13:56:15 +00:00
gitea-actions[bot] 59bb6337c9 chore(version): pre-release bump to 02.46.43-dev [skip ci] 2026-06-21 13:55:59 +00:00
gitea-actions[bot] b8abe2569c chore(version): auto-bump patch 02.46.42-dev [skip ci] 2026-06-21 13:55:45 +00:00
Jonathan Miller 679789e267 fix(install): relocate stale plugin files instead of deleting them
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: Auto Version Bump / Version Bump (push) Successful in 13s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 12s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 18s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 35s
Generic: Project CI / Lint & Validate (pull_request) Successful in 39s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 41s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 42s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
When plugins install to the group root due to empty element, postflight
now moves the files to the correct plugin subdirectory and fixes the
element value in the DB. This preserves the plugin files instead of
losing them on every update.
2026-06-21 08:55:30 -05:00
gitea-actions[bot] cb3628b682 chore(version): pre-release bump to 02.46.41-dev [skip ci] 2026-06-21 13:49:07 +00:00
gitea-actions[bot] 9524d1152b chore(version): pre-release bump to 02.46.40-dev [skip ci] 2026-06-21 13:48:14 +00:00
gitea-actions[bot] 568af6905e chore(version): auto-bump patch 02.46.39-dev [skip ci] 2026-06-21 13:48:00 +00:00
Jonathan Miller 5b67751858 fix(install): restore DEFAULT '' in preflight for MySQL strict mode
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
Generic: Project CI / Lint & Validate (pull_request) Successful in 13s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 18s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 33s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 33s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 36s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 31s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Joomla INSERTs extension rows without element before setting it.
MySQL strict mode requires a default value. Preflight sets DEFAULT ''
so the INSERT succeeds, then postflight cleans up the empty rows
and stale files.
2026-06-21 08:47:46 -05:00
gitea-actions[bot] c2cc483eff chore(version): pre-release bump to 02.46.38-dev [skip ci] 2026-06-21 13:46:09 +00:00
gitea-actions[bot] dab90d2633 chore(version): pre-release bump to 02.46.37-dev [skip ci] 2026-06-21 13:45:52 +00:00
gitea-actions[bot] 57906c3aee chore(version): auto-bump patch 02.46.36-dev [skip ci] 2026-06-21 13:45:34 +00:00
Jonathan Miller e9bcee71be fix(install): move empty-element cleanup to postflight, drop ALTER
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 37s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 21s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 37s
Generic: Project CI / Lint & Validate (pull_request) Successful in 38s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 41s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 40s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
The ALTER TABLE NOT NULL breaks Joomla's package installer on MySQL
strict mode. Instead, clean up empty-element rows and stale files in
postflight AFTER Joomla finishes installing sub-extensions.
2026-06-21 08:45:17 -05:00
gitea-actions[bot] 2348311528 chore(version): pre-release bump to 02.46.35-dev [skip ci] 2026-06-21 13:41:54 +00:00
gitea-actions[bot] 3660835b4f chore(version): pre-release bump to 02.46.34-dev [skip ci] 2026-06-21 13:41:37 +00:00
gitea-actions[bot] 1139cd91d9 chore(version): auto-bump patch 02.46.33-dev [skip ci] 2026-06-21 13:41:23 +00:00
Jonathan Miller 2733508045 fix(install): delete empty-element rows and stale files in preflight
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Generic: Project CI / Lint & Validate (pull_request) Successful in 13s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 16s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 35s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 40s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 40s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 33s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Previous ALTER TABLE DEFAULT '' created orphaned extension rows with
empty element, causing plugins to install to the group root. Preflight
now deletes these rows and cleans stale services/src/language dirs.
2026-06-21 08:41:07 -05:00
gitea-actions[bot] b8a1b9769a chore(version): pre-release bump to 02.46.32-dev [skip ci] 2026-06-21 06:32:06 +00:00
gitea-actions[bot] 90410c3add chore(version): pre-release bump to 02.46.31-dev [skip ci] 2026-06-21 06:08:43 +00:00
gitea-actions[bot] 28bcf014bc chore(version): pre-release bump to 02.46.30-dev [skip ci] 2026-06-21 05:55:51 +00:00
gitea-actions[bot] 70fb6e04b2 chore(version): pre-release bump to 02.46.29-dev [skip ci] 2026-06-21 05:55:25 +00:00
gitea-actions[bot] f1f9b7eb73 chore(version): auto-bump patch 02.46.28-dev [skip ci] 2026-06-21 05:55:07 +00:00
Jonathan Miller 6af960d1f0 fix(install): revert element column DEFAULT '' on existing sites
Universal: Auto Version Bump / Version Bump (push) Successful in 17s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Project CI / Lint & Validate (pull_request) Successful in 41s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 29s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 46s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 42s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 47s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 42s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-21 00:54:49 -05:00
gitea-actions[bot] f7311a8583 chore(version): pre-release bump to 02.46.27-dev [skip ci] 2026-06-21 05:49:47 +00:00
gitea-actions[bot] dcaee3e9ca chore(version): pre-release bump to 02.46.26-dev [skip ci] 2026-06-21 05:48:03 +00:00
gitea-actions[bot] 6704138998 chore(version): auto-bump patch 02.46.25-dev [skip ci] 2026-06-21 05:47:45 +00:00
Jonathan Miller 8bbae58d83 chore(install): remove update server URL migration and ALTER TABLE
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 8s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: Auto Version Bump / Version Bump (push) Successful in 16s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 14s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 43s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 29s
Generic: Project CI / Lint & Validate (pull_request) Successful in 49s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 50s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 53s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- Remove migrateUpdateServerUrls, cleanupStaleUpdateSites,
  fixUpdateRecords, enableUpdateServer calls from postflight
- Remove ALTER TABLE DEFAULT '' from preflight (caused plugin files
  to install to wrong directory)
- Dead method bodies left in file (harmless, never called)
2026-06-21 00:47:26 -05:00
gitea-actions[bot] e3bc5a0f42 chore(version): pre-release bump to 02.46.24-dev [skip ci] 2026-06-21 05:36:29 +00:00
gitea-actions[bot] c0b03ac5f4 chore(version): auto-bump patch 02.46.23-dev [skip ci] 2026-06-21 05:36:15 +00:00
Jonathan Miller 9a8d395cc8 fix(install): remove ALTER TABLE DEFAULT '' that breaks plugin installs
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 35s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 23s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 38s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 40s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 43s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 33s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
The preflight ALTER TABLE set element column to DEFAULT '', causing
Joomla to install sub-extension files to plugins/{group}/ root instead
of plugins/{group}/{element}/. This overwrote services/provider.php
across plugins, causing fatal class-not-found errors on every page.
2026-06-21 00:36:00 -05:00
gitea-actions[bot] 2927e7420a chore(version): pre-release bump to 02.46.22-dev [skip ci] 2026-06-21 05:30:44 +00:00
gitea-actions[bot] 1811e080ad chore(version): auto-bump patch 02.46.21-dev [skip ci] 2026-06-21 05:30:29 +00:00
Jonathan Miller d1cc81624f fix(health): add verbose messages to all health checks
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 26s
Generic: Project CI / Lint & Validate (pull_request) Successful in 33s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 41s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 36s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 36s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- Config check: message lists specific issues (Debug ON, Force SSL OFF)
- Security/backup/cron: message explains "Not installed" clearly
- Backup: also checks for MokoSuiteBackup tables, not just Akeeba
- Removed issues array from config — message field replaces it
2026-06-21 00:30:11 -05:00
gitea-actions[bot] 1320a567f8 chore(version): pre-release bump to 02.46.20-dev [skip ci] 2026-06-21 05:07:31 +00:00
gitea-actions[bot] 8d77ca9dd4 chore(version): pre-release bump to 02.46.19-dev [skip ci] 2026-06-21 05:07:09 +00:00
gitea-actions[bot] c31ac0f4bf chore(version): auto-bump patch 02.46.18-dev [skip ci] 2026-06-21 05:06:53 +00:00
Jonathan Miller d97695a858 fix(install): remove backup bridge from package until build is fixed
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 8s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 14s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 46s
Generic: Project CI / Lint & Validate (pull_request) Successful in 50s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 51s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 53s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 24s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
The backup bridge plugin deploys files to plugins/system/ root instead
of plugins/system/mokosuiteclient_backup/, causing a fatal error on
every page load. Removed from package manifest, enablePlugin, and
protectExtensions until the build pipeline correctly packages it.
2026-06-21 00:06:13 -05:00
gitea-actions[bot] ffbb46f5e8 chore(version): pre-release bump to 02.46.17-dev [skip ci] 2026-06-21 05:01:10 +00:00
gitea-actions[bot] e8dad8541e chore(version): pre-release bump to 02.46.16-dev [skip ci] 2026-06-21 04:59:06 +00:00
gitea-actions[bot] d5cd995a79 chore(version): auto-bump patch 02.46.15-dev [skip ci] 2026-06-21 04:58:53 +00:00
Jonathan Miller ab21d17563 fix(install): skip enablePlugin when plugin files not on disk
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 22s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 35s
Generic: Project CI / Lint & Validate (pull_request) Successful in 35s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 38s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 40s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 33s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Prevents enabling plugins whose extension record exists in the DB
but whose files were never deployed (e.g. backup bridge when package
build fails). Avoids fatal class-not-found errors on next page load.
2026-06-20 23:58:41 -05:00
gitea-actions[bot] 560035655a chore(version): pre-release bump to 02.46.14-dev [skip ci] 2026-06-21 04:49:35 +00:00
gitea-actions[bot] 3858869b24 chore(version): pre-release bump to 02.46.13-dev [skip ci] 2026-06-21 04:31:52 +00:00
gitea-actions[bot] a053cc8631 chore(version): pre-release bump to 02.46.12-dev [skip ci] 2026-06-21 04:28:22 +00:00
gitea-actions[bot] bfb28a2050 chore(version): auto-bump patch 02.46.11-dev [skip ci] 2026-06-21 04:28:09 +00:00
Jonathan Miller 0067835a17 Merge remote-tracking branch 'origin/dev' into dev
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: Auto Version Bump / Version Bump (push) Successful in 13s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 34s
Generic: Project CI / Lint & Validate (pull_request) Successful in 36s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 21s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 39s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 41s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
# Conflicts:
#	source/packages/plg_system_mokosuiteclient_monitor/mokosuiteclient_monitor.xml
2026-06-20 23:27:52 -05:00
Jonathan Miller 62e5cf6fb5 chore: remove retired monitor plugin source files 2026-06-20 23:26:56 -05:00
gitea-actions[bot] e34f52fb23 chore(version): pre-release bump to 02.46.10-dev [skip ci] 2026-06-21 03:15:53 +00:00
gitea-actions[bot] 348aaf1d8b chore(version): pre-release bump to 02.46.09-dev [skip ci] 2026-06-21 03:11:32 +00:00
gitea-actions[bot] b63779a675 chore(version): auto-bump patch 02.46.08-dev [skip ci] 2026-06-21 03:11:19 +00:00
Jonathan Miller aa61834e61 feat(support): show domain as support key in status bar with click-to-copy
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 9s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 13s
Generic: Project CI / Lint & Validate (pull_request) Successful in 16s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Auto Version Bump / Version Bump (push) Successful in 16s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 37s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 37s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 19s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 42s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 34s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Domain is displayed in the status bar next to cache buttons. Click to
copy to clipboard for easy sharing with support agents. HQ looks up
the site by domain.
2026-06-20 22:10:58 -05:00
gitea-actions[bot] 206239ab7d chore(version): pre-release bump to 02.46.07-dev [skip ci] 2026-06-21 03:03:19 +00:00
gitea-actions[bot] d273f2e615 chore(version): auto-bump patch 02.46.06-dev [skip ci] 2026-06-21 03:03:05 +00:00
Jonathan Miller 520422db8c fix(menu): MokoSuiteClient displays as MokoSuite in menu
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: Auto Version Bump / Version Bump (push) Successful in 13s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 22s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 36s
Generic: Project CI / Lint & Validate (pull_request) Successful in 39s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 40s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 42s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-20 22:02:48 -05:00
gitea-actions[bot] 035fe6d619 chore(version): pre-release bump to 02.46.05-dev [skip ci] 2026-06-21 02:56:58 +00:00
gitea-actions[bot] 1b316ecc40 chore(version): auto-bump patch 02.46.04-dev [skip ci] 2026-06-21 02:56:39 +00:00
Jonathan Miller 8c21553137 fix(menu): MokoSuiteClient→MokoClient, strip MokoSuite from others
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 8s
Generic: Project CI / Lint & Validate (pull_request) Successful in 15s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Successful in 15s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 14s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 28s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 45s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 47s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 49s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 44s
Generic: Project CI / Tests (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- HQ and Client keep "Moko" prefix: MokoHQ, MokoClient
- All other extensions drop MokoSuite entirely: Backup, OpenGraph, etc.
- Component menu title changed to MokoClient
2026-06-20 21:56:20 -05:00
Jonathan Miller 76d9fdd944 fix(menu): pin Client below HQ, strip MokoSuite prefix from titles
Menu order: HQ first, Client second, then alphabetical.
Display titles have "MokoSuite" prefix removed for cleaner sidebar.
2026-06-20 21:56:19 -05:00
gitea-actions[bot] d03c479aa4 chore(version): pre-release bump to 02.46.03-dev [skip ci] 2026-06-21 02:19:01 +00:00
gitea-actions[bot] 09153a04c4 chore(version): pre-release bump to 02.46.02-dev [skip ci] 2026-06-21 02:17:55 +00:00
gitea-actions[bot] b6f4b1f505 chore(version): auto-bump patch 02.46.01-dev [skip ci] 2026-06-21 02:17:41 +00:00
Jonathan Miller 7b5231d7f7 Merge remote-tracking branch 'origin/dev' into dev
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 12s
Generic: Project CI / Lint & Validate (pull_request) Successful in 37s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 37s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 21s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 38s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 41s
Generic: Project CI / Tests (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-20 21:17:22 -05:00
Jonathan Miller 56bdcf66ec chore(version): bump to 02.46.00 (above stable 02.45.00)
Dev version must be higher than stable for Joomla updater to show it.
2026-06-20 21:14:49 -05:00
gitea-actions[bot] 29d17dee8f chore(version): pre-release bump to 02.44.09-dev [skip ci] 2026-06-21 02:01:55 +00:00
gitea-actions[bot] d030a5d886 chore(version): pre-release bump to 02.44.08-dev [skip ci] 2026-06-21 01:39:20 +00:00
gitea-actions[bot] 3f0d45438c chore(version): auto-bump patch 02.44.07-dev [skip ci] 2026-06-21 01:39:05 +00:00
Jonathan Miller 64a8504794 feat(support-pin): daily-rotating PIN via HMAC(token, date)
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 11s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Generic: Project CI / Lint & Validate (pull_request) Successful in 14s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 22s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 36s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 36s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 39s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 35s
Generic: Project CI / Tests (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
PIN is derived from HMAC-SHA256(health_api_token, YYYY-MM-DD) so both
client and HQ compute the same value independently. Rotates at UTC
midnight. Shown on cpanel module header and component dashboard.
2026-06-20 20:38:51 -05:00
gitea-actions[bot] 9e22a4c49c chore(version): pre-release bump to 02.44.06-dev [skip ci] 2026-06-21 01:31:32 +00:00
gitea-actions[bot] 1ab721cbe3 chore(version): auto-bump patch 02.44.05-dev [skip ci] 2026-06-21 01:31:15 +00:00
Jonathan Miller 6bb1b43195 feat: MokoGitea LicenseValidator — core DRM enforcement, cache table, task scheduler
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 13s
Universal: Auto Version Bump / Version Bump (push) Successful in 16s
Generic: Project CI / Lint & Validate (pull_request) Successful in 16s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 25s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 42s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 46s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 50s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 41s
Generic: Project CI / Tests (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-20 20:30:57 -05:00
gitea-actions[bot] 5a5a5713d6 chore(version): auto-bump patch 02.44.04-dev [skip ci] 2026-06-21 00:39:06 +00:00
Jonathan Miller e55acc464a Merge remote-tracking branch 'origin/dev' into rc
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 35s
Generic: Project CI / Lint & Validate (pull_request) Successful in 35s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 36s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 38s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 9s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 32s
Generic: Project CI / Tests (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-20 19:38:22 -05:00
Jonathan Miller 6ed0a9f221 Merge remote-tracking branch 'origin/rc' into rc 2026-06-20 19:38:21 -05:00
Jonathan Miller 072297a75d fix(cpanel): always start collapsed, remove setting
Cpanel module is always collapsed by default. Removed the collapsed
toggle from module settings since it should not be configurable.
2026-06-20 19:37:36 -05:00
gitea-actions[bot] c0adfe41dd chore(version): auto-bump patch 02.44.03-dev [skip ci] 2026-06-21 00:15:31 +00:00
Jonathan Miller 579b0f13d8 Merge remote-tracking branch 'origin/dev' into rc
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Auto Version Bump / Version Bump (push) Successful in 13s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 12s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 13s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 40s
Generic: Project CI / Lint & Validate (pull_request) Successful in 42s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 45s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 46s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-20 19:15:05 -05:00
gitea-actions[bot] 2b7913f6e1 chore(version): auto-bump patch 02.44.03-rc [skip ci] 2026-06-21 00:14:51 +00:00
Jonathan Miller 07482df7b9 Merge remote-tracking branch 'origin/rc' into rc
Universal: Auto Version Bump / Version Bump (push) Successful in 8s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 9s
2026-06-20 19:14:37 -05:00
Jonathan Miller 61f96cba64 fix(menu): pin com_mokosuitehq at top of sidebar menu
The element name is com_mokosuitehq not com_mokosuiteclienthq.
The sort logic was checking the wrong name so HQ was never pinned.
2026-06-20 19:12:59 -05:00
Jonathan Miller 71f5d161e9 style(menu): simplify sidebar child item indent 2026-06-20 19:05:38 -05:00
gitea-actions[bot] 2db043412f chore(version): auto-bump patch 02.44.02-dev [skip ci] 2026-06-20 23:55:31 +00:00
Jonathan Miller c913f12621 Merge remote-tracking branch 'origin/dev' into rc
Generic: Project CI / Lint & Validate (pull_request) Failing after 3s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 11s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 35s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 37s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 42s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 39s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-20 18:55:13 -05:00
gitea-actions[bot] 772c40bb56 chore(version): auto-bump patch 02.44.02-rc [skip ci] 2026-06-20 23:54:29 +00:00
Jonathan Miller 49cc5becf6 fix(heartbeat): show success/error notification to admin on install/update
Universal: Auto Version Bump / Version Bump (push) Successful in 8s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 8s
Both install-time and runtime heartbeat now enqueue visible messages
for all outcomes — success, HTTP error, and connection failure — so
the admin always knows whether the heartbeat registered.
2026-06-20 18:54:17 -05:00
gitea-actions[bot] 738c878067 chore(version): auto-bump patch 02.44.01-dev [skip ci] 2026-06-20 23:37:52 +00:00
gitea-actions[bot] aa5f3ab06a chore(version): auto-bump patch 02.44.01-rc [skip ci] 2026-06-20 23:34:46 +00:00
Jonathan Miller 9326e2d11a fix(heartbeat): rotate RSA keys, fix backup bridge StatusHelper path
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: Auto Version Bump / Version Bump (push) Successful in 13s
Generic: Project CI / Lint & Validate (pull_request) Successful in 13s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 10s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 31s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 30s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 33s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 29s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- Replace broken RSA key pair (had Windows \r\n line endings in PEM)
  with fresh Unix-style keys that pass OpenSSL verify
- Fix backup bridge: use correct Helper\BackupStatusHelper path and
  getStatusSummary() method per MokoSuite convention
- Align fallback query output with StatusHelper return format
  (latest/totals structure instead of flat keys)
2026-06-20 18:34:13 -05:00
78 changed files with 1258 additions and 3519 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation
# VERSION: 01.00.00
# VERSION: 02.47.03
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
+34 -6
View File
@@ -14,21 +14,49 @@
INGROUP: MokoSuiteClient.Documentation
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
PATH: ./CHANGELOG.md
VERSION: 02.44.00
VERSION: 02.47.03
BRIEF: Version history using `Keep a Changelog`
-->
# Changelog
## [Unreleased]
## [02.44.00] --- 2026-06-20
### Added
- **Mirror Domains & Staging** — repeatable subform table in DevTools plugin for configuring domain aliases with per-alias offline bypass, robots directive, and labels
- **Daily Support PIN** — HMAC-SHA256 rotating PIN shown on cpanel module, component dashboard, and HQ site cards
- **Domain as support key** — click-to-copy domain in admin status bar
- **Current IP display** — firewall plugin settings show admin's IP with copy button
- **Heartbeat monitor** — consolidated into core plugin from retired monitor plugin, with diagnostic logging on all bail-out points
- **Backup bridge plugin** — discovers MokoSuiteBackup's BackupStatusHelper and sends status in heartbeat payloads
- **Activity log** — blockchain-style hash chain for tamper detection in MokoSuiteHQ
- **Dev domain in heartbeat** — client sends dev alias to HQ for display on dashboard
### Changed
- **Plugin install** — self-healing: extracts plugin zips from package on every update, creates missing extension records with namespace
- **Menu naming** — MokoSuiteClient displays as "MokoSuite", MokoSuiteHQ as "MokoHQ", others stripped of prefix
- **Menu ordering** — HQ first, MokoSuite second, others alphabetical
- **Cpanel module** — always starts collapsed, access level 3 (Special), pretty plugin badge labels
- **Module namespaces** — fixed cpanel (MokoSuiteCpanel → MokoSuiteClientCpanel) and cache (MokoSuiteCache → MokoSuiteClientCache)
- **Health checks** — return status:error on exceptions instead of false status:ok; MokoSuiteBackup detection queries correct table
- **Heartbeat** — correct URL (suite.dev), correct API route (mokosuitehq), correct headers (X-MokoSuite-*), fresh RSA key pair
- **Date formats** — all templates use Joomla locale-aware DATE_FORMAT_LC2/LC4
- **Domains** — updated from waas.dev to suite.dev.mokoconsulting.tech throughout
### Removed
- **Helpdesk/tickets** — migrated to MokoSuiteCRM (issue #67)
- **Monitor plugin** — retired, config consolidated into core plugin
- **Backup bridge** — temporarily removed from package manifest (build pipeline issue)
- **Update server migration** — removed migrateUpdateServerUrls, cleanupStaleUpdateSites, fixUpdateRecords, enableUpdateServer calls
### Fixed
- Plugin files installing to group root instead of element subdirectory (ALTER TABLE DEFAULT '' + empty element cleanup)
- Orphan extension rows with empty element or display-name-as-element
- Module not publishing (ensureAdminModule direct DB update bypasses checked_out)
- RSA key pair had Windows line endings causing signature verification failure
- Heartbeat connection failing due to wrong domain, route, and header names
## [02.44.00] --- 2026-06-20
## [02.43.00] --- 2026-06-20
## [02.43.00] --- 2026-06-20
## [02.42.00] --- 2026-06-20
## [02.42.00] --- 2026-06-20
+1 -1
View File
@@ -14,7 +14,7 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient.Documentation
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: ./CODE_OF_CONDUCT.md
BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default
-->
+1 -1
View File
@@ -19,7 +19,7 @@
DEFGROUP: mokoconsulting-tech.MokoSuiteClientBrand
INGROUP: MokoStandards.Governance
REPO: https://github.com/mokoconsulting-tech/MokoSuiteClientBrand
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /GOVERNANCE.md
BRIEF: Project governance rules, roles, and decision process for MokoSuiteClientBrand
-->
+1 -1
View File
@@ -15,7 +15,7 @@
INGROUP: MokoSuiteClient.Documentation
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
PATH: ./LICENSE.md
VERSION: 02.44.00
VERSION: 02.47.03
BRIEF: Project license (GPL-3.0-or-later)
-->
GNU GENERAL PUBLIC LICENSE
+32 -9
View File
@@ -9,7 +9,7 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteClient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /README.md
BRIEF: MokoSuiteClient platform plugin for Joomla
-->
@@ -21,17 +21,40 @@
[![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)
MokoSuiteClient 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 MokoSuiteClient platform.
MokoSuiteClient is the Joomla 5.x / 6.x client-facing tracker and identity layer for the MokoSuite platform. It provides security hardening, health monitoring, privacy compliance, multi-domain management, and integration with MokoSuiteHQ for centralized site management.
## Features
- **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 `/?mokosuiteclient=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
### Core
- **Admin Dashboard** — site info, plugin status, quick actions, support PIN
- **Health Monitoring** — 15 diagnostic checks via `/?mokosuiteclient=health`
- **Heartbeat** — RSA-signed registration with MokoSuiteHQ, daily support PIN rotation
- **Extension Catalog** — browse and install Moko Consulting extensions
### Security (Firewall Plugin)
- **Web Application Firewall** — SQL injection, XSS, RFI, directory traversal shields
- **Security Headers** — X-Frame-Options, CSP, HSTS, Referrer-Policy, Permissions-Policy
- **IP Management** — trusted IPs, blocklist, auto-ban on WAF threshold
- **Password Policy** — min length, uppercase, number, special character requirements
- **Access Control** — admin secret URL, frontend super user block, upload restrictions
### Privacy Guard
- **GDPR Compliance** — data subject requests, consent logging, retention policies
- **User Data** — export, anonymize, or delete user data on request
### DevTools
- **Development Mode** — debug, cache disable, hit suppression
- **Mirror Domains & Staging** — repeatable table of domain aliases with offline bypass and robots directives
- **Maintenance** — reset hits, delete versions, reset download keys
### Multi-Domain
- **Site Aliases** — per-alias offline mode, robots directives, canonical URLs
- **Offline Bypass** — TOS, privacy policy, and support pages remain accessible when site is offline
### Integration
- **MokoSuiteHQ** — heartbeat, health data, backup status, activity logging
- **MokoSuiteBackup** — bridge plugin discovers BackupStatusHelper for heartbeat payloads
- **Joomla** — guided tours, action logging, custom fields, scheduled tasks
## Requirements
+1 -1
View File
@@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME]
INGROUP: [PROJECT_NAME].Documentation
REPO: [REPOSITORY_URL]
PATH: /SECURITY.md
VERSION: 02.44.00
VERSION: 02.47.03
BRIEF: Security vulnerability reporting and handling policy
-->
+2 -2
View File
@@ -11,13 +11,13 @@
INGROUP: MokoSuiteClient.Build
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
FILE: build-guide.md
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /docs/guides/
BRIEF: Build and packaging guide for the MokoSuiteClient system plugin
NOTE: Defines environment setup, repository layout, packaging rules, and release preparation
-->
# MokoSuiteClient Build Guide (VERSION: 02.44.00)
# MokoSuiteClient Build Guide (VERSION: 02.47.03)
## 1. Purpose
+2 -2
View File
@@ -10,13 +10,13 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient.Guides
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /docs/guides/configuration-guide.md
BRIEF: Configuration guide for the MokoSuiteClient system plugin
NOTE: Defines plugin parameters, expected behaviors, and recommended defaults
-->
# MokoSuiteClient Configuration Guide (VERSION: 02.44.00)
# MokoSuiteClient Configuration Guide (VERSION: 02.47.03)
## 1. Objective
+2 -2
View File
@@ -10,13 +10,13 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient.Guides
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /docs/guides/installation-guide.md
BRIEF: Installation guide for the MokoSuiteClient system plugin
NOTE: First document in the guide set
-->
# MokoSuiteClient Installation Guide (VERSION: 02.44.00)
# MokoSuiteClient Installation Guide (VERSION: 02.47.03)
## Introduction
+2 -2
View File
@@ -10,13 +10,13 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient.Guides
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /docs/guides/operations-guide.md
BRIEF: Operational guide for administering and managing the MokoSuiteClient system plugin
NOTE: Defines lifecycle, responsibilities, and operational behaviors
-->
# MokoSuiteClient Operations Guide (VERSION: 02.44.00)
# MokoSuiteClient Operations Guide (VERSION: 02.47.03)
## Introduction
+2 -2
View File
@@ -10,13 +10,13 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient.Guides
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /docs/guides/rollback-and-recovery-guide.md
BRIEF: Rollback and recovery guide for restoring stable operation after plugin related incidents
NOTE: Completes the core guide set for Suite plugin governance
-->
# MokoSuiteClient Rollback and Recovery Guide (VERSION: 02.44.00)
# MokoSuiteClient Rollback and Recovery Guide (VERSION: 02.47.03)
## Introduction
+2 -2
View File
@@ -7,13 +7,13 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient.Guides
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /docs/guides/testing-guide.md
BRIEF: Testing guide for MokoSuiteClient v02.01.08
NOTE: Covers manual test procedures for language overrides, install/uninstall, and configuration
-->
# MokoSuiteClient Testing Guide (VERSION: 02.44.00)
# MokoSuiteClient Testing Guide (VERSION: 02.47.03)
## 1. Prerequisites
+2 -2
View File
@@ -10,13 +10,13 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient.Guides
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /docs/guides/troubleshooting-guide.md
BRIEF: Troubleshooting guide for diagnosing and resolving issues related to the MokoSuiteClient plugin
NOTE: Designed for administrators and Suite operations teams
-->
# MokoSuiteClient Troubleshooting Guide (VERSION: 02.44.00)
# MokoSuiteClient Troubleshooting Guide (VERSION: 02.47.03)
## Introduction
+2 -2
View File
@@ -10,13 +10,13 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient.Guides
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /docs/guides/upgrade-and-versioning-guide.md
BRIEF: Guide for updating, versioning, and maintaining the MokoSuiteClient plugin
NOTE: Defines release flow, version rules, and upgrade validation
-->
# MokoSuiteClient Upgrade and Versioning Guide (VERSION: 02.44.00)
# MokoSuiteClient Upgrade and Versioning Guide (VERSION: 02.47.03)
## Introduction
+2 -2
View File
@@ -10,13 +10,13 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoSuiteClient.Documentation
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
VERSION: 02.44.00
VERSION: 02.47.03
PATH: /docs/index.md
BRIEF: Master index of all documentation for the MokoSuiteClient plugin
NOTE: Automatically maintained index for all guide canvases
-->
# MokoSuiteClient Documentation Index (VERSION: 02.44.00)
# MokoSuiteClient Documentation Index (VERSION: 02.47.03)
## Introduction
+2 -2
View File
@@ -11,12 +11,12 @@
INGROUP: MokoSuiteClient
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
PATH: /docs/plugin-basic.md
VERSION: 02.44.00
VERSION: 02.47.03
BRIEF: Baseline documentation for the MokoSuiteClient system plugin
NOTE: Foundational reference for internal and external stakeholders
-->
# MokoSuiteClient Plugin Overview (VERSION: 02.44.00)
# MokoSuiteClient Plugin Overview (VERSION: 02.47.03)
## Introduction
+1 -1
View File
@@ -10,7 +10,7 @@ DEFGROUP: MokoSuiteClient.Documentation
INGROUP: MokoStandards.Templates
REPO: https://github.com/mokoconsulting-tech/MokoSuiteClient
PATH: /docs/update-server.md
VERSION: 02.44.00
VERSION: 02.47.03
BRIEF: How this extension's Joomla update server file (update.xml) is managed
-->
@@ -2,7 +2,7 @@
; Copyright (C) 2026 Moko Consulting. All rights reserved.
; License: GPL-3.0-or-later
COM_MOKOSUITECLIENT="MokoSuiteClient"
COM_MOKOSUITECLIENT="MokoSuite"
COM_MOKOSUITECLIENT_DESCRIPTION="MokoSuiteClient admin dashboard and REST API. Control panel for managing site features, health monitoring, and remote management."
COM_MOKOSUITECLIENT_DASHBOARD_TITLE="MokoSuiteClient Control Panel"
COM_MOKOSUITECLIENT_MENU_DASHBOARD="Dashboard"
@@ -215,3 +215,15 @@ INSERT IGNORE INTO `#__mokosuiteclient_retention_policies` (`id`, `content_type`
(4, 'inactive_users', 730, 'anonymize', 0, 'Anonymize users inactive for 2 years (disabled by default)'),
(5, 'closed_tickets', 365, 'anonymize', 0, 'Anonymize closed tickets older than 1 year (disabled by default)');
-- ============================================================
-- License Cache — stores MokoGitea validation results
-- ============================================================
CREATE TABLE IF NOT EXISTS `#__mokosuite_license_cache` (
`dlid_hash` CHAR(64) NOT NULL COMMENT 'SHA-256 of DLID (never store raw DLID)',
`response_data` TEXT NOT NULL COMMENT 'JSON validation response from MokoGitea',
`checked_at` DATETIME NOT NULL,
PRIMARY KEY (`dlid_hash`),
KEY `idx_checked` (`checked_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
File diff suppressed because it is too large Load Diff
@@ -26,6 +26,7 @@ class HtmlView extends BaseHtmlView
protected $wafChartData = [];
protected $loginChartData = [];
protected $mokoExtensions = [];
public $supportPin = '';
public function display($tpl = null)
{
@@ -33,6 +34,28 @@ class HtmlView extends BaseHtmlView
$this->plugins = $model->getFeaturePlugins();
$this->siteInfo = $model->getSiteInfo();
// Daily support PIN from health token
try
{
$db = \Joomla\CMS\Factory::getDbo();
$db->setQuery(
$db->getQuery(true)
->select($db->quoteName('params'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('element') . ' = ' . $db->quote('mokosuiteclient'))
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
);
$token = (json_decode((string) $db->loadResult()))->health_api_token ?? '';
if (!empty($token))
{
$hash = hash_hmac('sha256', gmdate('Y-m-d'), $token);
$this->supportPin = 'MOKO-' . strtoupper(substr($hash, 0, 4)) . '-' . strtoupper(substr($hash, 4, 4));
}
}
catch (\Throwable $e) {}
$this->recentLogins = $model->getRecentLogins(5);
$this->pendingUpdates = $model->getPendingUpdates();
$this->checkedOutItems = $model->getCheckedOutItems();
@@ -1,72 +0,0 @@
<?php
/**
* @package MokoSuiteClient
* @subpackage com_mokosuiteclient
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Component\MokoSuiteClient\Administrator\View\Ticket;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
protected $ticket;
protected $cannedResponses = [];
protected $statuses = [];
protected $priorities = [];
protected $customFields = [];
protected $fieldValues = [];
protected $attachments = [];
public function display($tpl = null)
{
$model = $this->getModel('Tickets');
$id = Factory::getApplication()->getInput()->getInt('id', 0);
$this->ticket = $model->getTicket($id);
$this->cannedResponses = $model->getCannedResponses((int) ($this->ticket->category_id ?? 0));
$this->statuses = $model->getStatuses();
$this->priorities = $model->getPriorities();
// Load custom fields for this ticket's category
if ($this->ticket && $this->ticket->category_id)
{
$groups = $model->getFieldGroupsForCategory((int) $this->ticket->category_id);
$groupIds = array_column($groups, 'id');
$this->customFields = $model->getFieldsForGroups($groupIds);
$this->fieldValues = $model->getFieldValues($id);
}
// Load attachments
$this->attachments = \Moko\Component\MokoSuiteClient\Administrator\Service\AttachmentService::getForTicket($id);
if (!$this->ticket)
{
Factory::getApplication()->enqueueMessage('Ticket not found.', 'error');
Factory::getApplication()->redirect('index.php?option=com_mokosuiteclient&view=tickets');
return;
}
$this->addToolbar();
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->registerAndUseStyle('com_mokosuiteclient.dashboard', 'com_mokosuiteclient/dashboard.css');
parent::display($tpl);
}
protected function addToolbar(): void
{
$title = $this->ticket ? 'Ticket #' . $this->ticket->id . ' — ' . $this->ticket->subject : 'Ticket';
ToolbarHelper::title($title, 'headphones');
ToolbarHelper::back('JTOOLBAR_BACK', 'index.php?option=com_mokosuiteclient&view=tickets');
}
}
@@ -1,67 +0,0 @@
<?php
/**
* @package MokoSuiteClient
* @subpackage com_mokosuiteclient
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Component\MokoSuiteClient\Administrator\View\Tickets;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
protected $tickets = [];
protected $categories = [];
protected $statusCounts;
protected $overdue = [];
protected $atsAvailable = null;
protected $contacts = [];
protected $statuses = [];
protected $priorities = [];
protected $backendUsers = [];
protected $userGroups = [];
public function display($tpl = null)
{
$model = $this->getModel();
$app = Factory::getApplication();
$filters = [
'status_id' => $app->getInput()->getInt('filter_status', 0),
'priority_id' => $app->getInput()->getInt('filter_priority', 0),
'category_id' => $app->getInput()->getInt('filter_category', 0),
'contact_id' => $app->getInput()->getInt('filter_contact', 0),
];
$this->tickets = $model->getTickets($filters);
$this->categories = $model->getCategories();
$this->statuses = $model->getStatuses();
$this->priorities = $model->getPriorities();
$this->statusCounts = $model->getStatusCounts();
$this->overdue = $model->getOverdueTickets();
$this->atsAvailable = $model->checkAtsAvailable();
$this->contacts = $model->getContacts();
$this->backendUsers = $model->getBackendUsers();
$this->userGroups = $model->getUserGroups();
$this->addToolbar();
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->registerAndUseStyle('com_mokosuiteclient.dashboard', 'com_mokosuiteclient/dashboard.css');
parent::display($tpl);
}
protected function addToolbar(): void
{
ToolbarHelper::title(Text::_('COM_MOKOSUITECLIENT_TICKETS_TITLE'), 'headphones');
ToolbarHelper::back('JTOOLBAR_BACK', 'index.php?option=com_mokosuiteclient');
}
}
@@ -1,41 +0,0 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-LICENSE-IDENTIFIER: GPL-3.0-or-later
*
* @package MokoSuiteClient
* @subpackage Component
*/
namespace Moko\Component\MokoSuiteClient\Administrator\View\Ticketsettings;
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
protected $statuses = [];
protected $priorities = [];
public function display($tpl = null)
{
$model = $this->getModel('Tickets');
$this->statuses = $model->getStatuses();
$this->priorities = $model->getPriorities();
$this->addToolbar();
parent::display($tpl);
}
protected function addToolbar(): void
{
ToolbarHelper::title(Text::_('COM_MOKOSUITECLIENT_TICKET_SETTINGS'), 'cog');
ToolbarHelper::back('JTOOLBAR_BACK', 'index.php?option=com_mokosuiteclient&view=tickets');
}
}
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -48,6 +48,12 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
<span class="mokosuiteclient-info-label">MokoSuiteClient</span>
<span class="mokosuiteclient-info-value"><span class="badge bg-primary"><?php echo $this->escape($siteInfo->mokosuiteclient_version); ?></span></span>
</div>
<?php if (!empty($this->supportPin)): ?>
<div class="mokosuiteclient-info-item">
<span class="mokosuiteclient-info-label">Support PIN</span>
<span class="mokosuiteclient-info-value"><span class="badge bg-dark" style="font-family:monospace;letter-spacing:0.08em;cursor:help;" title="Daily verification PIN — rotates at midnight UTC. Ask your provider for this code to verify identity."><span class="icon-key small me-1" aria-hidden="true"></span><?php echo $this->escape($this->supportPin); ?></span></span>
</div>
<?php endif; ?>
<div class="mokosuiteclient-info-item">
<span class="mokosuiteclient-info-label">Joomla</span>
<span class="mokosuiteclient-info-value"><span class="badge bg-secondary"><?php echo $this->escape($siteInfo->joomla_version); ?></span></span>
@@ -311,7 +317,7 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
<tr>
<td class="text-muted"><?php echo $this->escape(mb_substr($item->title, 0, 30)); ?></td>
<td class="text-muted"><?php echo $this->escape($item->username ?? ''); ?></td>
<td class="text-muted"><?php echo HTMLHelper::_('date', $item->checked_out_time, 'M d H:i'); ?></td>
<td class="text-muted"><?php echo HTMLHelper::_('date', $item->checked_out_time, Text::_('DATE_FORMAT_LC4')); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
@@ -342,7 +348,7 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
<tr>
<td class="text-muted"><code><?php echo $this->escape($block->ip); ?></code></td>
<td class="text-muted"><span class="badge bg-danger"><?php echo $this->escape($block->rule); ?></span></td>
<td class="text-muted"><?php echo HTMLHelper::_('date', $block->created, 'M d H:i'); ?></td>
<td class="text-muted"><?php echo HTMLHelper::_('date', $block->created, Text::_('DATE_FORMAT_LC4')); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
@@ -369,7 +375,7 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api'];
<tr>
<td class="text-muted"><?php echo $this->escape($login->username ?? ''); ?></td>
<td class="text-muted"><code><?php echo $this->escape($login->ip_address ?? ''); ?></code></td>
<td class="text-muted"><?php echo HTMLHelper::_('date', $login->log_date, 'M d H:i'); ?></td>
<td class="text-muted"><?php echo HTMLHelper::_('date', $login->log_date, Text::_('DATE_FORMAT_LC4')); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
@@ -3,6 +3,7 @@ defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
@@ -140,8 +141,8 @@ $typeBadge = [
<td><?php echo $this->escape($r->user_name ?? ''); ?><br><small class="text-muted"><?php echo $this->escape($r->user_email ?? ''); ?></small></td>
<td><span class="badge <?php echo $typeBadge[$r->type] ?? 'bg-secondary'; ?>"><?php echo ucfirst($r->type); ?></span></td>
<td><span class="badge <?php echo $statusBadge[$r->status] ?? 'bg-secondary'; ?>"><?php echo ucfirst($r->status); ?></span></td>
<td class="text-nowrap small"><?php echo HTMLHelper::_('date', $r->created, 'M d, Y H:i'); ?></td>
<td class="text-nowrap small"><?php echo $r->processed ? HTMLHelper::_('date', $r->processed, 'M d, Y H:i') : '—'; ?></td>
<td class="text-nowrap small"><?php echo HTMLHelper::_('date', $r->created, Text::_('DATE_FORMAT_LC2')); ?></td>
<td class="text-nowrap small"><?php echo $r->processed ? HTMLHelper::_('date', $r->processed, Text::_('DATE_FORMAT_LC2')) : '—'; ?></td>
<td>
<?php if ($r->status === 'pending'): ?>
<div class="btn-group btn-group-sm">
@@ -1,364 +0,0 @@
<?php
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
$t = $this->ticket;
$canned = $this->cannedResponses;
$token = Session::getFormToken();
$attachments = $this->attachments;
$downloadUrl = Route::_('index.php?option=com_mokosuiteclient&task=display.downloadAttachment');
$uploadUrl = Route::_('index.php?option=com_mokosuiteclient&task=display.uploadAttachment&format=json');
$deleteAttUrl = Route::_('index.php?option=com_mokosuiteclient&task=display.deleteAttachment&format=json');
// Group attachments by reply_id (null = ticket-level)
$attByReply = [];
foreach ($attachments as $att) {
$key = $att->reply_id ?? 0;
$attByReply[$key][] = $att;
}
$statuses = $this->statuses ?? [];
$priorities = $this->priorities ?? [];
?>
<div id="mokosuiteclient-ticket" class="row">
<!-- Left: conversation thread -->
<div class="col-12 col-xl-8">
<!-- Original ticket -->
<div class="card mb-3">
<div class="card-header d-flex justify-content-between align-items-center">
<div>
<strong><?php echo $this->escape($t->created_by_name); ?></strong>
<small class="text-muted ms-2"><?php echo HTMLHelper::_('date', $t->created, 'M d, Y H:i'); ?></small>
</div>
<span class="badge bg-dark">Original</span>
</div>
<div class="card-body">
<?php echo nl2br($this->escape($t->body)); ?>
<?php if (!empty($attByReply[0])): ?>
<hr>
<div class="small">
<strong>Attachments:</strong>
<?php foreach ($attByReply[0] as $att): ?>
<a href="<?php echo $downloadUrl . '&id=' . $att->id; ?>" class="d-inline-block me-3">
<span class="icon-download"></span> <?php echo $this->escape($att->filename); ?>
<span class="text-muted">(<?php echo \Moko\Component\MokoSuiteClient\Administrator\Service\AttachmentService::formatSize($att->filesize); ?>)</span>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<!-- Replies -->
<?php foreach ($t->replies as $reply): ?>
<div class="card mb-3 <?php echo $reply->is_internal ? 'border-warning' : ''; ?>">
<div class="card-header d-flex justify-content-between align-items-center">
<div>
<strong><?php echo $this->escape($reply->user_name ?? 'System'); ?></strong>
<small class="text-muted ms-2"><?php echo HTMLHelper::_('date', $reply->created, 'M d, Y H:i'); ?></small>
</div>
<?php if ($reply->is_internal): ?>
<span class="badge bg-warning text-dark">Internal Note</span>
<?php endif; ?>
</div>
<div class="card-body">
<?php echo nl2br($this->escape($reply->body)); ?>
<?php if (!empty($attByReply[$reply->id])): ?>
<hr>
<div class="small">
<strong>Attachments:</strong>
<?php foreach ($attByReply[$reply->id] as $att): ?>
<a href="<?php echo $downloadUrl . '&id=' . $att->id; ?>" class="d-inline-block me-3">
<span class="icon-download"></span> <?php echo $this->escape($att->filename); ?>
<span class="text-muted">(<?php echo \Moko\Component\MokoSuiteClient\Administrator\Service\AttachmentService::formatSize($att->filesize); ?>)</span>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<!-- Reply form -->
<div class="card mb-3">
<div class="card-header"><strong>Reply</strong></div>
<div class="card-body">
<?php if (!empty($canned)): ?>
<div class="mb-2">
<select class="form-select form-select-sm" id="canned-select">
<option value="">Insert canned response...</option>
<?php foreach ($canned as $c): ?>
<option value="<?php echo $this->escape($c->body); ?>"><?php echo $this->escape($c->title); ?></option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<textarea id="reply-body" class="form-control mb-2" rows="5" placeholder="Type your reply..."></textarea>
<div class="mb-2">
<input type="file" id="reply-attachments" class="form-control form-control-sm" multiple
accept=".jpg,.jpeg,.png,.gif,.webp,.pdf,.doc,.docx,.xls,.xlsx,.csv,.txt,.zip">
</div>
<div class="d-flex gap-2">
<button type="button" class="btn btn-primary" id="btn-reply"
data-url="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.addTicketReply&format=json'); ?>"
data-ticket="<?php echo $t->id; ?>" data-token="<?php echo $token; ?>">
<span class="icon-reply"></span> Send Reply
</button>
<button type="button" class="btn btn-outline-warning" id="btn-internal"
data-url="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.addTicketReply&format=json'); ?>"
data-ticket="<?php echo $t->id; ?>" data-token="<?php echo $token; ?>" data-internal="1">
<span class="icon-eye-slash"></span> Internal Note
</button>
</div>
</div>
</div>
</div>
<!-- Right: ticket metadata -->
<div class="col-12 col-xl-4">
<div class="card mb-3">
<div class="card-header"><strong>Details</strong></div>
<div class="card-body">
<table class="table table-sm mb-0">
<tr><td class="text-muted">Status</td><td><span class="badge <?php echo $this->escape($t->status_color ?? 'bg-secondary'); ?>"><?php echo $this->escape($t->status_title ?? $t->status); ?></span></td></tr>
<tr><td class="text-muted">Priority</td><td><span class="badge <?php echo $this->escape($t->priority_color ?? 'bg-secondary'); ?>"><?php echo $this->escape($t->priority_title ?? $t->priority); ?></span></td></tr>
<tr><td class="text-muted">Category</td><td><?php echo $this->escape($t->category_title ?? '—'); ?></td></tr>
<tr><td class="text-muted">Created By</td><td><?php echo $this->escape($t->created_by_name); ?><br><small><?php echo $this->escape($t->created_by_email ?? ''); ?></small></td></tr>
<tr><td class="text-muted">Assigned To</td><td><?php
if (!empty($t->assignees)) {
foreach ($t->assignees as $a) {
$icon = $a->assignee_type === 'group' ? '<span class="icon-users"></span> ' : '<span class="icon-user"></span> ';
echo '<div>' . $icon . $this->escape($a->name) . '</div>';
}
} else {
echo '<em>Unassigned</em>';
}
?></td></tr>
<?php if ($t->contact_id): ?>
<tr><td class="text-muted">Contact</td><td>
<a href="<?php echo Route::_('index.php?option=com_contact&task=contact.edit&id=' . (int) $t->contact_id); ?>">
<?php echo $this->escape($t->contact_name ?? 'Contact #' . $t->contact_id); ?>
</a>
<?php if (!empty($t->contact_email)): ?><br><small><?php echo $this->escape($t->contact_email); ?></small><?php endif; ?>
<?php if (!empty($t->contact_phone)): ?><br><small><?php echo $this->escape($t->contact_phone); ?></small><?php endif; ?>
</td></tr>
<?php endif; ?>
<tr><td class="text-muted">Created</td><td><?php echo HTMLHelper::_('date', $t->created, 'M d, Y H:i'); ?></td></tr>
<?php if ($t->resolved): ?><tr><td class="text-muted">Resolved</td><td><?php echo HTMLHelper::_('date', $t->resolved, 'M d, Y H:i'); ?></td></tr><?php endif; ?>
<?php if ($t->closed): ?><tr><td class="text-muted">Closed</td><td><?php echo HTMLHelper::_('date', $t->closed, 'M d, Y H:i'); ?></td></tr><?php endif; ?>
<tr><td class="text-muted">Replies</td><td><?php echo $t->reply_count; ?></td></tr>
</table>
</div>
</div>
<!-- SLA -->
<?php if ($t->sla_response_due || $t->sla_resolution_due): ?>
<div class="card mb-3">
<div class="card-header"><strong>SLA</strong></div>
<div class="card-body">
<?php if ($t->sla_response_due): ?>
<div class="mb-2">
<small class="text-muted">Response Due</small><br>
<?php
$responseOverdue = !$t->sla_responded && strtotime($t->sla_response_due) < time();
?>
<span class="<?php echo $t->sla_responded ? 'text-success' : ($responseOverdue ? 'text-danger fw-bold' : ''); ?>">
<?php echo $t->sla_responded ? 'Responded' : HTMLHelper::_('date', $t->sla_response_due, 'M d H:i'); ?>
<?php echo $responseOverdue ? ' OVERDUE' : ''; ?>
</span>
</div>
<?php endif; ?>
<?php if ($t->sla_resolution_due): ?>
<div>
<small class="text-muted">Resolution Due</small><br>
<?php
$resolutionOverdue = !!empty($t->status_is_closed) && strtotime($t->sla_resolution_due) < time();
?>
<span class="<?php echo !empty($t->status_is_closed) ? 'text-success' : ($resolutionOverdue ? 'text-danger fw-bold' : ''); ?>">
<?php echo !empty($t->status_is_closed) ? 'Met' : HTMLHelper::_('date', $t->sla_resolution_due, 'M d H:i'); ?>
<?php echo $resolutionOverdue ? ' OVERDUE' : ''; ?>
</span>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<!-- Satisfaction Rating -->
<?php
$isClosed = in_array($t->status, ['resolved', 'closed'], true);
$hasRating = !empty($t->satisfaction_rating);
?>
<?php if ($hasRating): ?>
<div class="card mb-3">
<div class="card-header"><strong>Satisfaction</strong></div>
<div class="card-body text-center">
<div class="mb-1">
<?php for ($s = 1; $s <= 5; $s++): ?>
<span style="font-size:1.5rem;color:<?php echo $s <= $t->satisfaction_rating ? '#f5a623' : '#dee2e6'; ?>;">&#9733;</span>
<?php endfor; ?>
</div>
<div class="text-muted small"><?php echo $t->satisfaction_rating; ?>/5</div>
<?php if (!empty($t->satisfaction_feedback)): ?>
<p class="small mt-2 mb-0"><?php echo $this->escape($t->satisfaction_feedback); ?></p>
<?php endif; ?>
</div>
</div>
<?php elseif ($isClosed): ?>
<div class="card mb-3" id="rating-card">
<div class="card-header"><strong>Rate this Support</strong></div>
<div class="card-body text-center">
<div class="mb-2" id="star-rating">
<?php for ($s = 1; $s <= 5; $s++): ?>
<span class="star-btn" data-value="<?php echo $s; ?>" style="font-size:2rem;cursor:pointer;color:#dee2e6;">&#9733;</span>
<?php endfor; ?>
</div>
<textarea id="rating-feedback" class="form-control form-control-sm mb-2" rows="2" placeholder="Optional feedback..."></textarea>
<button type="button" class="btn btn-primary btn-sm" id="btn-rate"
data-url="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.rateTicket&format=json'); ?>"
data-ticket="<?php echo $t->id; ?>" data-token="<?php echo $token; ?>" disabled>
Submit Rating
</button>
</div>
</div>
<?php endif; ?>
<!-- Status actions -->
<div class="card mb-3">
<div class="card-header"><strong>Actions</strong></div>
<div class="card-body d-grid gap-2">
<?php foreach ($statuses as $s): ?>
<?php if ((int) $s->id !== (int) $t->status_id): ?>
<button type="button" class="btn btn-sm btn-outline-<?php echo $s->is_closed ? 'danger' : 'secondary'; ?> btn-status"
data-url="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.updateTicketStatus&format=json'); ?>"
data-ticket="<?php echo $t->id; ?>" data-status="<?php echo $s->id; ?>" data-token="<?php echo $token; ?>">
<?php echo $this->escape($s->title); ?>
</button>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<!-- Custom Fields -->
<?php if (!empty($this->customFields)): ?>
<div class="card mb-3">
<div class="card-header"><strong>Custom Fields</strong></div>
<div class="card-body">
<table class="table table-sm mb-0">
<?php foreach ($this->customFields as $field): ?>
<tr>
<td class="text-muted"><?php echo $this->escape($field->title); ?></td>
<td><?php echo $this->escape($this->fieldValues[(int) $field->id] ?? '—'); ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
</div>
<?php endif; ?>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Canned response insert
var cannedSel = document.getElementById('canned-select');
if (cannedSel) {
cannedSel.addEventListener('change', function() {
if (this.value) { document.getElementById('reply-body').value = this.value; this.selectedIndex = 0; }
});
}
// Reply buttons (with attachment upload)
document.querySelectorAll('#btn-reply, #btn-internal').forEach(function(btn) {
btn.addEventListener('click', function() {
var body = document.getElementById('reply-body').value.trim();
var fileInput = document.getElementById('reply-attachments');
if (!body && (!fileInput || !fileInput.files.length)) return;
var el = this;
el.disabled = true;
var fd = new FormData();
fd.append('ticket_id', el.dataset.ticket);
fd.append('body', body || '(attachment)');
fd.append('is_internal', el.dataset.internal || '0');
fd.append(el.dataset.token, '1');
fetch(el.dataset.url, {method:'POST', body:fd, headers:{'X-Requested-With':'XMLHttpRequest'}})
.then(function(r){return r.json()})
.then(function(d){
if (!d.success) { Joomla.renderMessages({error:[d.message]}); el.disabled = false; return; }
// Upload attachments if any
if (fileInput && fileInput.files.length > 0) {
var afd = new FormData();
afd.append('ticket_id', el.dataset.ticket);
if (d.reply_id) afd.append('reply_id', d.reply_id);
for (var i = 0; i < fileInput.files.length; i++) {
afd.append('attachments[' + i + ']', fileInput.files[i]);
}
afd.append(el.dataset.token, '1');
fetch('<?php echo $uploadUrl; ?>', {method:'POST', body:afd, headers:{'X-Requested-With':'XMLHttpRequest'}})
.then(function(){ location.reload(); });
} else {
location.reload();
}
})
.catch(function(){ el.disabled = false; });
});
});
// Status buttons
document.querySelectorAll('.btn-status').forEach(function(btn) {
btn.addEventListener('click', function() {
var el = this;
el.disabled = true;
var fd = new FormData();
fd.append('ticket_id', el.dataset.ticket);
fd.append('status', el.dataset.status);
fd.append(el.dataset.token, '1');
fetch(el.dataset.url, {method:'POST', body:fd, headers:{'X-Requested-With':'XMLHttpRequest'}})
.then(function(r){return r.json()})
.then(function(d){ if(d.success) location.reload(); else Joomla.renderMessages({error:[d.message]}); })
.finally(function(){ el.disabled = false; });
});
});
// Star rating
var selectedRating = 0;
document.querySelectorAll('.star-btn').forEach(function(star) {
star.addEventListener('mouseenter', function() {
var val = parseInt(this.dataset.value);
document.querySelectorAll('.star-btn').forEach(function(s) {
s.style.color = parseInt(s.dataset.value) <= val ? '#f5a623' : '#dee2e6';
});
});
star.addEventListener('mouseleave', function() {
document.querySelectorAll('.star-btn').forEach(function(s) {
s.style.color = parseInt(s.dataset.value) <= selectedRating ? '#f5a623' : '#dee2e6';
});
});
star.addEventListener('click', function() {
selectedRating = parseInt(this.dataset.value);
document.getElementById('btn-rate').disabled = false;
});
});
var rateBtn = document.getElementById('btn-rate');
if (rateBtn) {
rateBtn.addEventListener('click', function() {
if (!selectedRating) return;
var el = this;
el.disabled = true;
var fd = new FormData();
fd.append('ticket_id', el.dataset.ticket);
fd.append('rating', selectedRating);
fd.append('feedback', document.getElementById('rating-feedback').value);
fd.append(el.dataset.token, '1');
fetch(el.dataset.url, {method:'POST', body:fd, headers:{'X-Requested-With':'XMLHttpRequest'}})
.then(function(r){return r.json()})
.then(function(d){ if(d.success) location.reload(); else Joomla.renderMessages({error:[d.message]}); })
.finally(function(){ el.disabled = false; });
});
}
});
</script>
@@ -1,317 +0,0 @@
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
$tickets = $this->tickets;
$categories = $this->categories;
$statuses = $this->statuses;
$priorities = $this->priorities;
$counts = $this->statusCounts;
$overdue = $this->overdue;
$atsAvailable = $this->atsAvailable;
$token = Session::getFormToken();
?>
<div id="mokosuiteclient-tickets">
<!-- Status summary cards -->
<div class="row g-3 mb-4">
<?php foreach ($counts as $sc): ?>
<div class="col"><div class="card text-center p-2"><span class="fw-bold fs-4"><?php echo (int) $sc->cnt; ?></span><small class="text-muted"><?php echo $this->escape($sc->title); ?></small></div></div>
<?php endforeach; ?>
<?php if (\count($overdue) > 0): ?>
<div class="col"><div class="card text-center p-2 border-danger"><span class="fw-bold fs-4 text-danger"><?php echo \count($overdue); ?></span><small class="text-danger">SLA Overdue</small></div></div>
<?php endif; ?>
</div>
<!-- New ticket + filters -->
<div class="d-flex flex-wrap justify-content-between align-items-center mb-3">
<div class="d-flex gap-2">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#newTicketModal">
<span class="icon-plus"></span> New Ticket
</button>
<?php if ($atsAvailable): ?>
<button type="button" class="btn btn-outline-info" id="btn-import-ats"
data-url="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.importAts&format=json'); ?>"
data-token="<?php echo $token; ?>"
data-tickets="<?php echo $atsAvailable->tickets; ?>"
data-posts="<?php echo $atsAvailable->posts; ?>">
<span class="icon-upload"></span> Import from Akeeba (<?php echo $atsAvailable->tickets; ?> tickets, <?php echo $atsAvailable->posts; ?> posts)
</button>
<?php endif; ?>
</div>
<form method="get" class="d-flex gap-2">
<input type="hidden" name="option" value="com_mokosuiteclient">
<input type="hidden" name="view" value="tickets">
<select name="filter_status" class="form-select form-select-sm" style="width:auto" onchange="this.form.submit()">
<option value="">All Statuses</option>
<?php foreach ($statuses as $s): ?>
<option value="<?php echo $s->id; ?>" <?php echo Factory::getApplication()->getInput()->getInt('filter_status') === (int) $s->id ? 'selected' : ''; ?>><?php echo $this->escape($s->title); ?></option>
<?php endforeach; ?>
</select>
<select name="filter_priority" class="form-select form-select-sm" style="width:auto" onchange="this.form.submit()">
<option value="">All Priorities</option>
<?php foreach ($priorities as $p): ?>
<option value="<?php echo $p->id; ?>" <?php echo Factory::getApplication()->getInput()->getInt('filter_priority') === (int) $p->id ? 'selected' : ''; ?>><?php echo $this->escape($p->title); ?></option>
<?php endforeach; ?>
</select>
</form>
</div>
<!-- Ticket table -->
<div class="card">
<div class="table-responsive">
<table class="table table-striped table-hover mb-0">
<thead>
<tr>
<th>#</th>
<th>Subject</th>
<th>Status</th>
<th>Priority</th>
<th>Category</th>
<th>Contact</th>
<th>Created By</th>
<th>Assigned To</th>
<th>Created</th>
<th>SLA</th>
</tr>
</thead>
<tbody>
<?php if (empty($tickets)): ?>
<tr><td colspan="10" class="text-center text-muted py-4">No tickets found.</td></tr>
<?php else: ?>
<?php foreach ($tickets as $t): ?>
<?php
$slaClass = '';
$now = time();
if ($t->sla_response_due && !$t->sla_responded && strtotime($t->sla_response_due) < $now) $slaClass = 'table-danger';
elseif ($t->sla_resolution_due && strtotime($t->sla_resolution_due) < $now && empty($t->status_is_closed)) $slaClass = 'table-danger';
elseif ($t->sla_response_due && !$t->sla_responded && strtotime($t->sla_response_due) < $now + 3600) $slaClass = 'table-warning';
?>
<tr class="<?php echo $slaClass; ?>">
<td><a href="<?php echo Route::_('index.php?option=com_mokosuiteclient&view=ticket&id=' . $t->id); ?>"><?php echo $t->id; ?></a></td>
<td><a href="<?php echo Route::_('index.php?option=com_mokosuiteclient&view=ticket&id=' . $t->id); ?>"><?php echo $this->escape(mb_substr($t->subject, 0, 60)); ?></a></td>
<td><span class="badge <?php echo $this->escape($t->status_color ?? 'bg-secondary'); ?>"><?php echo $this->escape($t->status_title ?? $t->status); ?></span></td>
<td><span class="badge <?php echo $this->escape($t->priority_color ?? 'bg-secondary'); ?>"><?php echo $this->escape($t->priority_title ?? $t->priority); ?></span></td>
<td><?php echo $this->escape($t->category_title ?? '—'); ?></td>
<td><?php echo $t->contact_name ? '<a href="' . Route::_('index.php?option=com_contact&task=contact.edit&id=' . (int) $t->contact_id) . '">' . $this->escape($t->contact_name) . '</a>' : '—'; ?></td>
<td><?php echo $this->escape($t->created_by_name ?? ''); ?></td>
<td><?php
if (!empty($t->assignees)) {
$names = [];
foreach ($t->assignees as $a) {
$icon = $a->assignee_type === 'group' ? '<span class="icon-users"></span> ' : '';
$names[] = $icon . $this->escape($a->name);
}
echo implode(', ', $names);
} else {
echo '<em>Unassigned</em>';
}
?></td>
<td class="small"><?php echo HTMLHelper::_('date', $t->created, 'M d H:i'); ?></td>
<td class="small">
<?php if ($t->sla_response_due && !$t->sla_responded): ?>
<span title="Response due"><?php echo HTMLHelper::_('date', $t->sla_response_due, 'M d H:i'); ?></span>
<?php elseif ($t->sla_resolution_due): ?>
<span title="Resolution due"><?php echo HTMLHelper::_('date', $t->sla_resolution_due, 'M d H:i'); ?></span>
<?php else: ?>—<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- New Ticket Modal -->
<div class="modal fade" id="newTicketModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header"><h5 class="modal-title">New Ticket</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
<div class="modal-body">
<!-- KB Search step -->
<div id="modal-kb-step">
<label class="form-label fw-bold">What's the issue?</label>
<div class="input-group mb-3">
<input type="text" id="modal-kb-search" class="form-control" placeholder="Describe your issue to search for existing answers...">
<button type="button" class="btn btn-outline-primary" id="modal-kb-btn"><span class="icon-search"></span></button>
</div>
<div id="modal-kb-results" class="list-group mb-3 d-none"></div>
<button type="button" class="btn btn-primary" id="modal-show-form">
<span class="icon-plus"></span> Create Ticket
</button>
</div>
<!-- Ticket form step (hidden initially) -->
<form id="modal-ticket-form" class="d-none" method="post" action="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.createTicket&format=json'); ?>">
<div class="mb-3">
<label class="form-label">Subject</label>
<input type="text" name="subject" id="modal-subject" class="form-control" required>
</div>
<div class="row mb-3">
<div class="col-md-4">
<label class="form-label">Category</label>
<select name="category_id" class="form-select">
<option value="">— Select —</option>
<?php foreach ($categories as $cat): ?>
<option value="<?php echo $cat->id; ?>"><?php echo $this->escape($cat->title); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Priority</label>
<select name="priority_id" class="form-select">
<?php foreach ($priorities as $p): ?>
<option value="<?php echo $p->id; ?>" <?php echo $p->is_default ? 'selected' : ''; ?>><?php echo $this->escape($p->title); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Contact</label>
<select name="contact_id" class="form-select">
<option value="">— None —</option>
<?php foreach ($this->contacts as $contact): ?>
<option value="<?php echo $contact->id; ?>"><?php echo $this->escape($contact->name); ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label">Assign Users</label>
<select name="assign_users[]" class="form-select" multiple size="4">
<?php foreach ($this->backendUsers as $u): ?>
<option value="<?php echo $u->id; ?>"><?php echo $this->escape($u->name); ?></option>
<?php endforeach; ?>
</select>
<small class="text-muted">Hold Ctrl/Cmd to select multiple</small>
</div>
<div class="col-md-6">
<label class="form-label">Assign Groups</label>
<select name="assign_groups[]" class="form-select" multiple size="4">
<?php foreach ($this->userGroups as $g): ?>
<option value="<?php echo $g->id; ?>"><?php echo $this->escape($g->title); ?></option>
<?php endforeach; ?>
</select>
<small class="text-muted">Hold Ctrl/Cmd to select multiple</small>
</div>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea name="body" class="form-control" rows="6" required></textarea>
</div>
<input type="hidden" name="<?php echo $token; ?>" value="1">
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary"><span class="icon-plus"></span> Create Ticket</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
// Modal KB search
var modalSearch = document.getElementById('modal-kb-search');
var modalSearchBtn = document.getElementById('modal-kb-btn');
var modalResults = document.getElementById('modal-kb-results');
var modalShowForm = document.getElementById('modal-show-form');
var modalKbStep = document.getElementById('modal-kb-step');
var modalForm = document.getElementById('modal-ticket-form');
var modalSubject = document.getElementById('modal-subject');
function modalDoSearch() {
var q = modalSearch.value.trim();
if (q.length < 3) return;
fetch('<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.searchKb&format=json'); ?>&q=' + encodeURIComponent(q), {
headers: {'X-Requested-With': 'XMLHttpRequest'}
}).then(function(r){return r.json()}).then(function(d) {
modalResults.textContent = '';
if (d.results && d.results.length > 0) {
d.results.forEach(function(item) {
var a = document.createElement('a');
a.href = item.url;
a.target = '_blank';
a.className = 'list-group-item list-group-item-action';
var strong = document.createElement('strong');
strong.textContent = item.title;
a.appendChild(strong);
if (item.description) {
a.appendChild(document.createElement('br'));
var small = document.createElement('small');
small.className = 'text-muted';
small.textContent = item.description;
a.appendChild(small);
}
modalResults.appendChild(a);
});
modalResults.classList.remove('d-none');
} else {
modalResults.classList.add('d-none');
}
});
}
if (modalSearchBtn) modalSearchBtn.addEventListener('click', modalDoSearch);
if (modalSearch) modalSearch.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); modalDoSearch(); } });
// Show ticket form
if (modalShowForm) {
modalShowForm.addEventListener('click', function() {
modalKbStep.classList.add('d-none');
modalForm.classList.remove('d-none');
if (modalSearch.value && !modalSubject.value) modalSubject.value = modalSearch.value;
modalSubject.focus();
});
}
// Submit ticket from modal
if (modalForm) {
modalForm.addEventListener('submit', function(e) {
e.preventDefault();
var form = this;
var fd = new FormData(form);
fetch(form.action, {method:'POST', body:fd, headers:{'X-Requested-With':'XMLHttpRequest'}})
.then(function(r){return r.json()})
.then(function(d){
if (d.success) { location.href = 'index.php?option=com_mokosuiteclient&view=ticket&id=' + d.id; }
else { Joomla.renderMessages({error:[d.message]}); }
});
});
}
// Reset modal on close
document.getElementById('newTicketModal').addEventListener('hidden.bs.modal', function() {
modalKbStep.classList.remove('d-none');
modalForm.classList.add('d-none');
modalResults.classList.add('d-none');
modalSearch.value = '';
modalForm.reset();
});
// ATS Import
var atsBtn = document.getElementById('btn-import-ats');
if (atsBtn) {
atsBtn.addEventListener('click', function() {
var el = this;
if (!confirm('Import ' + el.dataset.tickets + ' tickets and ' + el.dataset.posts + ' posts from Akeeba Ticket System? Duplicates will be skipped.')) return;
el.disabled = true;
el.textContent = ' Importing...';
var fd = new FormData();
fd.append(el.dataset.token, '1');
fetch(el.dataset.url, {method:'POST', body:fd, headers:{'X-Requested-With':'XMLHttpRequest'}})
.then(function(r){return r.json()})
.then(function(d){
if (d.success) { Joomla.renderMessages({message:[d.message]}); location.reload(); }
else { Joomla.renderMessages({error:[d.message]}); el.disabled = false; el.textContent = 'Import Failed - Retry'; }
})
.catch(function(){ Joomla.renderMessages({error:['Network error']}); el.disabled = false; });
});
}
</script>
@@ -1,203 +0,0 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-LICENSE-IDENTIFIER: GPL-3.0-or-later
*
* @package MokoSuiteClient
* @subpackage Component
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
$token = Session::getFormToken();
$colorOptions = [
'bg-primary', 'bg-secondary', 'bg-success', 'bg-danger',
'bg-warning text-dark', 'bg-info text-dark', 'bg-dark', 'bg-light text-dark',
];
?>
<div class="row">
<!-- Statuses -->
<div class="col-lg-6">
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<strong><span class="fa-solid fa-circle-dot"></span> Ticket Statuses</strong>
</div>
<div class="card-body p-0">
<table class="table table-striped mb-0">
<thead>
<tr>
<th>Title</th>
<th class="w-10 text-center">Color</th>
<th class="w-10 text-center">Default</th>
<th class="w-10 text-center">Closed?</th>
<th class="w-10 text-center">Order</th>
<th class="w-10 text-center">Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($this->statuses as $s): ?>
<tr>
<td><?php echo $this->escape($s->title); ?> <small class="text-muted">(<?php echo $this->escape($s->alias); ?>)</small></td>
<td class="text-center"><span class="badge <?php echo $this->escape($s->color); ?>">&nbsp;&nbsp;&nbsp;</span></td>
<td class="text-center"><?php echo $s->is_default ? '<span class="badge bg-success">Yes</span>' : ''; ?></td>
<td class="text-center"><?php echo $s->is_closed ? '<span class="badge bg-dark">Closed</span>' : ''; ?></td>
<td class="text-center"><?php echo (int) $s->ordering; ?></td>
<td class="text-center">
<button class="btn btn-sm btn-outline-primary" onclick="editStatus(<?php echo htmlspecialchars(json_encode($s)); ?>)">
<span class="icon-pencil"></span>
</button>
<a href="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.deleteStatus&id=' . $s->id . '&' . $token . '=1'); ?>"
class="btn btn-sm btn-outline-danger"
onclick="return confirm('Delete this status?')">
<span class="icon-trash"></span>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="card-footer">
<form method="post" action="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.saveStatus'); ?>" id="statusForm" class="row g-2 align-items-end">
<input type="hidden" name="id" id="status-id" value="0">
<div class="col-md-3">
<label class="form-label small">Title</label>
<input type="text" name="title" id="status-title" class="form-control form-control-sm" required>
</div>
<div class="col-md-2">
<label class="form-label small">Alias</label>
<input type="text" name="alias" id="status-alias" class="form-control form-control-sm">
</div>
<div class="col-md-2">
<label class="form-label small">Color</label>
<select name="color" id="status-color" class="form-select form-select-sm">
<?php foreach ($colorOptions as $c): ?>
<option value="<?php echo $c; ?>"><?php echo $c; ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-1">
<label class="form-label small">Order</label>
<input type="number" name="ordering" id="status-ordering" class="form-control form-control-sm" value="0">
</div>
<div class="col-md-1 text-center">
<label class="form-label small">Default</label>
<input type="checkbox" name="is_default" id="status-default" value="1" class="form-check-input">
</div>
<div class="col-md-1 text-center">
<label class="form-label small">Closed</label>
<input type="checkbox" name="is_closed" id="status-closed" value="1" class="form-check-input">
</div>
<div class="col-md-2">
<input type="hidden" name="<?php echo $token; ?>" value="1">
<button type="submit" class="btn btn-sm btn-primary w-100" id="status-btn">Add</button>
</div>
</form>
</div>
</div>
</div>
<!-- Priorities -->
<div class="col-lg-6">
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<strong><span class="fa-solid fa-flag"></span> Ticket Priorities</strong>
</div>
<div class="card-body p-0">
<table class="table table-striped mb-0">
<thead>
<tr>
<th>Title</th>
<th class="w-10 text-center">Color</th>
<th class="w-10 text-center">Default</th>
<th class="w-10 text-center">Order</th>
<th class="w-10 text-center">Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($this->priorities as $p): ?>
<tr>
<td><?php echo $this->escape($p->title); ?> <small class="text-muted">(<?php echo $this->escape($p->alias); ?>)</small></td>
<td class="text-center"><span class="badge <?php echo $this->escape($p->color); ?>">&nbsp;&nbsp;&nbsp;</span></td>
<td class="text-center"><?php echo $p->is_default ? '<span class="badge bg-success">Yes</span>' : ''; ?></td>
<td class="text-center"><?php echo (int) $p->ordering; ?></td>
<td class="text-center">
<button class="btn btn-sm btn-outline-primary" onclick="editPriority(<?php echo htmlspecialchars(json_encode($p)); ?>)">
<span class="icon-pencil"></span>
</button>
<a href="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.deletePriority&id=' . $p->id . '&' . $token . '=1'); ?>"
class="btn btn-sm btn-outline-danger"
onclick="return confirm('Delete this priority?')">
<span class="icon-trash"></span>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="card-footer">
<form method="post" action="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.savePriority'); ?>" id="priorityForm" class="row g-2 align-items-end">
<input type="hidden" name="id" id="priority-id" value="0">
<div class="col-md-3">
<label class="form-label small">Title</label>
<input type="text" name="title" id="priority-title" class="form-control form-control-sm" required>
</div>
<div class="col-md-2">
<label class="form-label small">Alias</label>
<input type="text" name="alias" id="priority-alias" class="form-control form-control-sm">
</div>
<div class="col-md-2">
<label class="form-label small">Color</label>
<select name="color" id="priority-color" class="form-select form-select-sm">
<?php foreach ($colorOptions as $c): ?>
<option value="<?php echo $c; ?>"><?php echo $c; ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-1">
<label class="form-label small">Order</label>
<input type="number" name="ordering" id="priority-ordering" class="form-control form-control-sm" value="0">
</div>
<div class="col-md-1 text-center">
<label class="form-label small">Default</label>
<input type="checkbox" name="is_default" id="priority-default" value="1" class="form-check-input">
</div>
<div class="col-md-3">
<input type="hidden" name="<?php echo $token; ?>" value="1">
<button type="submit" class="btn btn-sm btn-primary w-100" id="priority-btn">Add</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
function editStatus(s) {
document.getElementById('status-id').value = s.id;
document.getElementById('status-title').value = s.title;
document.getElementById('status-alias').value = s.alias;
document.getElementById('status-color').value = s.color;
document.getElementById('status-ordering').value = s.ordering;
document.getElementById('status-default').checked = !!parseInt(s.is_default);
document.getElementById('status-closed').checked = !!parseInt(s.is_closed);
document.getElementById('status-btn').textContent = 'Update';
}
function editPriority(p) {
document.getElementById('priority-id').value = p.id;
document.getElementById('priority-title').value = p.title;
document.getElementById('priority-alias').value = p.alias;
document.getElementById('priority-color').value = p.color;
document.getElementById('priority-ordering').value = p.ordering;
document.getElementById('priority-default').checked = !!parseInt(p.is_default);
document.getElementById('priority-btn').textContent = 'Update';
}
</script>
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -3,6 +3,7 @@ defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
@@ -98,7 +99,7 @@ $ruleBadge = [
<?php else: ?>
<?php foreach ($logs as $log): ?>
<tr>
<td class="text-nowrap small"><?php echo HTMLHelper::_('date', $log->created, 'M d H:i:s'); ?></td>
<td class="text-nowrap small"><?php echo HTMLHelper::_('date', $log->created, Text::_('DATE_FORMAT_LC4')); ?></td>
<td><code><?php echo htmlspecialchars($log->ip); ?></code></td>
<td><span class="badge <?php echo $ruleBadge[$log->rule] ?? 'bg-secondary'; ?>"><?php echo htmlspecialchars($log->rule); ?></span></td>
<td class="small" style="max-width:250px;overflow:hidden;text-overflow:ellipsis" title="<?php echo htmlspecialchars($log->uri); ?>"><?php echo htmlspecialchars(mb_substr($log->uri, 0, 60)); ?></td>
@@ -148,7 +149,7 @@ $ruleBadge = [
<tr>
<td><code class="small"><?php echo htmlspecialchars($tip->ip); ?></code></td>
<td class="fw-bold"><?php echo $tip->cnt; ?></td>
<td class="small text-nowrap"><?php echo HTMLHelper::_('date', $tip->last_seen, 'M d'); ?></td>
<td class="small text-nowrap"><?php echo HTMLHelper::_('date', $tip->last_seen, Text::_('DATE_FORMAT_LC4')); ?></td>
<td>
<button type="button" class="btn btn-sm btn-outline-danger btn-ban-ip" data-ip="<?php echo htmlspecialchars($tip->ip); ?>"
data-url="<?php echo Route::_('index.php?option=com_mokosuiteclient&task=display.banIpFromLog&format=json'); ?>"
@@ -20,7 +20,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>MokoSuiteClient admin dashboard and REST API. Provides a control panel for managing MokoSuiteClient feature plugins, site health monitoring, and remote management endpoints.</description>
<namespace path="src">Moko\Component\MokoSuiteClient</namespace>
@@ -7,9 +7,9 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>MOD_MOKOSUITECLIENT_CACHE_DESC</description>
<namespace path="src">Moko\Module\MokoSuiteCache</namespace>
<namespace path="src">Moko\Module\MokoSuiteClientCache</namespace>
<files>
<folder module="mod_mokosuiteclient_cache">services</folder>
@@ -4,11 +4,15 @@ namespace Moko\Module\MokoSuiteClientCache\Administrator\Dispatcher;
defined('_JEXEC') or die;
use Joomla\CMS\Dispatcher\AbstractModuleDispatcher;
use Joomla\CMS\Uri\Uri;
class Dispatcher extends AbstractModuleDispatcher
{
protected function getLayoutData()
{
return parent::getLayoutData();
$data = parent::getLayoutData();
$data['domain'] = parse_url(Uri::root(), PHP_URL_HOST) ?: '';
return $data;
}
}
@@ -13,6 +13,7 @@ use Joomla\CMS\Session\Session;
$token = Session::getFormToken();
$cacheUrl = 'index.php?option=com_mokosuiteclient&task=clearCache&format=json';
$tempUrl = 'index.php?option=com_mokosuiteclient&task=clearTemp&format=json';
$domain = $domain ?? '';
?>
<style>
@@ -21,9 +22,15 @@ $tempUrl = 'index.php?option=com_mokosuiteclient&task=clearTemp&format=json';
.mokosuiteclient-cleaner-btn { cursor:pointer; padding:0.2rem 0.5rem; font-size:0.8rem; border-radius:3px; text-decoration:none; color:var(--template-text-dark,#495057); transition:background 0.15s; white-space:nowrap; }
.mokosuiteclient-cleaner-btn:hover { background:rgba(0,0,0,0.08); color:var(--template-text-dark,#212529); text-decoration:none; }
.mokosuiteclient-cleaner-sep { color:var(--template-text-dark,#adb5bd); padding:0 0.1rem; font-size:0.8rem; }
.mokosuiteclient-domain { font-family:monospace; font-size:0.75rem; color:var(--template-text-dark,#6c757d); cursor:pointer; padding:0.15rem 0.4rem; border-radius:3px; transition:background 0.15s; }
.mokosuiteclient-domain:hover { background:rgba(0,0,0,0.06); }
</style>
<div class="header-item-content mokosuiteclient-cleaner">
<?php if ($domain): ?>
<span class="mokosuiteclient-domain" id="mokosuiteclient-domain" title="Support key — click to copy"><?php echo htmlspecialchars($domain); ?></span>
<span class="mokosuiteclient-cleaner-sep">|</span>
<?php endif; ?>
<span class="mokosuiteclient-cleaner-label">Clear:</span>
<a href="#" class="mokosuiteclient-cleaner-btn" id="mokosuiteclient-clear-cache" title="Clear all Joomla cache">
<span class="icon-bolt" aria-hidden="true" id="mokosuiteclient-cache-icon"></span> Cache
@@ -85,5 +92,17 @@ document.addEventListener('DOMContentLoaded', function() {
setupCleaner('mokosuiteclient-clear-cache', 'mokosuiteclient-cache-icon', '<?php echo $cacheUrl; ?>', '<?php echo $token; ?>');
setupCleaner('mokosuiteclient-clear-temp', 'mokosuiteclient-temp-icon', '<?php echo $tempUrl; ?>', '<?php echo $token; ?>');
// Click-to-copy domain
var domainEl = document.getElementById('mokosuiteclient-domain');
if (domainEl) {
domainEl.addEventListener('click', function() {
navigator.clipboard.writeText(domainEl.textContent.trim()).then(function() {
var orig = domainEl.textContent;
domainEl.textContent = 'Copied!';
setTimeout(function() { domainEl.textContent = orig; }, 1500);
});
});
}
});
</script>
@@ -7,7 +7,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>MOD_MOKOSUITECLIENT_CATEGORIES_DESC</description>
<namespace path="src">Moko\Module\MokoSuiteClientCategories</namespace>
@@ -7,9 +7,9 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>MOD_MOKOSUITECLIENT_CPANEL_DESC</description>
<namespace path="src">Moko\Module\MokoSuiteCpanel</namespace>
<namespace path="src">Moko\Module\MokoSuiteClientCpanel</namespace>
<files>
<folder module="mod_mokosuiteclient_cpanel">services</folder>
@@ -28,14 +28,6 @@
label="MOD_MOKOSUITECLIENT_CPANEL_FIELDSET_DISPLAY"
description="MOD_MOKOSUITECLIENT_CPANEL_FIELDSET_DISPLAY_DESC">
<field name="collapsed" type="radio" default="1"
label="MOD_MOKOSUITECLIENT_CPANEL_COLLAPSED_LABEL"
description="MOD_MOKOSUITECLIENT_CPANEL_COLLAPSED_DESC"
layout="joomla.form.field.radio.switcher">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field name="show_health" type="radio" default="1"
label="MOD_MOKOSUITECLIENT_CPANEL_SHOW_HEALTH_LABEL"
layout="joomla.form.field.radio.switcher">
@@ -47,7 +47,7 @@ class Dispatcher extends AbstractModuleDispatcher implements HelperFactoryAwareI
$data['currentIp'] = $helper->getCurrentIp();
$data['ssl'] = $helper->getSslStatus();
// Support PIN derived from health token
// Daily support PIN derived from health token + today's date (UTC)
$data['supportPin'] = '';
try
@@ -65,7 +65,9 @@ class Dispatcher extends AbstractModuleDispatcher implements HelperFactoryAwareI
if (!empty($token))
{
$data['supportPin'] = 'MOKO-' . strtoupper(substr($token, 0, 4) . '-' . substr($token, 4, 4));
$date = gmdate('Y-m-d');
$hash = hash_hmac('sha256', $date, $token);
$data['supportPin'] = 'MOKO-' . strtoupper(substr($hash, 0, 4)) . '-' . strtoupper(substr($hash, 4, 4));
}
}
catch (\Throwable $e) {}
@@ -22,7 +22,7 @@ $healthOk = $healthOk ?? true;
$counts = $counts ?? (object) ['articles' => 0, 'users' => 0, 'extensions' => 0, 'updates' => 0];
$disk = $disk ?? (object) ['free_mb' => null, 'total_mb' => null];
$currentIp = $currentIp ?? '';
$collapsed = $params->get('collapsed', 0);
$collapsed = true;
$showHealth = $params->get('show_health', 1);
$showStats = $params->get('show_stats', 1);
$showDisk = $params->get('show_disk', 1);
@@ -44,10 +44,13 @@ foreach ($plugins as $p)
}
$labels = [
'mokosuiteclient' => 'Core',
'mokosuiteclient_firewall' => 'Firewall',
'mokosuiteclient_tenant' => 'Tenant',
'mokosuiteclient_devtools' => 'DevTools',
'mokosuiteclient' => 'Core Engine',
'mokosuiteclient_firewall' => 'Web Firewall',
'mokosuiteclient_tenant' => 'Tenant Guard',
'mokosuiteclient_devtools' => 'Dev Tools',
'mokosuiteclient_offline' => 'Offline Bypass',
'mokosuiteclient_dbip' => 'GeoIP Lookup',
'mokosuiteclient_license' => 'License Manager',
];
$diskPct = ($disk->total_mb && $disk->total_mb > 0)
@@ -7,7 +7,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>MokoSuiteClient admin sidebar menu — renders a dedicated MokoSuiteClient section in the admin menu before Joomla's default menu.</description>
<namespace path="src">Moko\Module\MokoSuiteClientMenu</namespace>
@@ -3,7 +3,7 @@
* MokoSuiteClient Admin Sidebar Menu
*
* Each installed Moko component gets its own top-level collapsible section.
* com_mokosuiteclienthq is always pinned first. com_mokosuiteclient uses static views
* com_mokosuitehq is always pinned first. com_mokosuiteclient uses static views
* as children. All other components auto-discover their submenu items.
*/
@@ -20,7 +20,6 @@ $currentView = $app->getInput()->get('view', '');
// ── Static views for com_mokosuiteclient ──────────────────────────────────
$mokosuiteclientStaticViews = [
['icon' => 'icon-cogs', 'title' => 'Dashboard', 'link' => 'index.php?option=com_mokosuiteclient'],
['icon' => 'fa-solid fa-handshake-angle', 'title' => 'Helpdesk', 'link' => 'index.php?option=com_mokosuiteclient&view=tickets'],
['icon' => 'icon-puzzle-piece', 'title' => 'Extensions', 'link' => 'index.php?option=com_mokosuiteclient&view=extensions'],
['icon' => 'fa-solid fa-file-code', 'title' => '.htaccess Maker', 'link' => 'index.php?option=com_mokosuiteclient&view=htaccess'],
['icon' => 'icon-lock', 'title' => 'Privacy Guard', 'link' => 'index.php?option=com_mokosuiteclient&view=privacy'],
@@ -101,7 +100,7 @@ else
// com_mokosuiteclient not in admin menu — add it manually
$mokoComponents['com_mokosuiteclient'] = [
'id' => 0,
'title' => 'MokoSuiteClient',
'title' => 'MokoSuite',
'link' => 'index.php?option=com_mokosuiteclient',
'icon' => 'icon-shield-alt',
'element' => 'com_mokosuiteclient',
@@ -109,16 +108,37 @@ else
];
}
// ── Sort: com_mokosuiteclienthq first, then alphabetical by title ─────────
// ── Sort: HQ first, Client second, then alphabetical ─────────
$hq = null;
$client = null;
$rest = [];
foreach ($mokoComponents as $key => $comp)
{
if ($key === 'com_mokosuiteclienthq')
// Shorten display titles:
// MokoSuiteClient → MokoSuite, MokoSuiteHQ → MokoHQ
// Everything else: MokoSuiteBackup → Backup, MokoSuiteOpenGraph → OpenGraph
if ($key === 'com_mokosuiteclient')
{
$comp['title'] = 'MokoSuite';
}
elseif ($key === 'com_mokosuitehq')
{
$comp['title'] = preg_replace('/^MokoSuite/i', 'Moko', $comp['title']);
}
else
{
$comp['title'] = preg_replace('/^MokoSuite\s*/i', '', $comp['title']);
}
if ($key === 'com_mokosuitehq')
{
$hq = $comp;
}
elseif ($key === 'com_mokosuiteclient')
{
$client = $comp;
}
else
{
$rest[$key] = $comp;
@@ -132,6 +152,10 @@ if ($hq !== null)
{
$sorted[] = $hq;
}
if ($client !== null)
{
$sorted[] = $client;
}
foreach ($rest as $comp)
{
$sorted[] = $comp;
@@ -139,8 +163,7 @@ foreach ($rest as $comp)
?>
<style>
.sidebar-wrapper .mokosuiteclient-ext-item > a { padding-inline-start: 1.5rem; }
.sidebar-wrapper .mokosuiteclient-ext-child > a { padding-inline-start: 2.5rem; }
.sidebar-wrapper { padding-right: 0.5rem; }
</style>
<ul class="nav flex-column main-nav">
@@ -22,7 +22,7 @@
* DEFGROUP: Joomla.Plugin
* INGROUP: MokoSuiteClient
* REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
* VERSION: 02.44.00
* VERSION: 02.47.03
* PATH: /src/Extension/MokoSuiteClient.php
* NOTE: Core system plugin for MokoSuiteClient admin tools suite
*/
@@ -968,13 +968,21 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
if (!in_array($akTable, $tables))
{
return [
'status' => 'ok',
'installed' => false,
];
// Check for MokoSuiteBackup instead
if (!in_array($prefix . 'mokosuitebackup_records', $tables))
{
return [
'status' => 'ok',
'installed' => false,
'message' => 'No backup solution installed (Akeeba Backup or MokoSuiteBackup)',
];
}
// MokoSuiteBackup is installed — query its table
return $this->checkMokoSuiteBackup($db);
}
// Get the most recent backup
// Get the most recent Akeeba Backup
$query = $db->getQuery(true)
->select([
$db->quoteName('id'),
@@ -1050,13 +1058,53 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
}
catch (\Exception $e)
{
\Joomla\CMS\Log\Log::add('Backup check failed: ' . $e->getMessage(), \Joomla\CMS\Log\Log::WARNING, 'mokosuiteclient');
return [
'status' => 'ok',
'installed' => false,
'status' => 'error',
'message' => 'Backup check failed: ' . $e->getMessage(),
];
}
}
/**
* Query MokoSuiteBackup tables for backup status.
*/
protected function checkMokoSuiteBackup($db): array
{
$db->setQuery(
$db->getQuery(true)
->select(['id', 'description', 'status', 'backupstart', 'backupend', 'total_size'])
->from($db->quoteName('#__mokosuitebackup_records'))
->order($db->quoteName('id') . ' DESC'),
0, 1
);
$latest = $db->loadObject();
if (!$latest)
{
return ['status' => 'degraded', 'installed' => true, 'message' => 'MokoSuiteBackup installed but no backups found'];
}
$daysSince = 999;
if (!empty($latest->backupstart) && $latest->backupstart !== '0000-00-00 00:00:00')
{
$daysSince = (int) ((time() - strtotime($latest->backupstart)) / 86400);
}
$status = ($latest->status === 'complete' && $daysSince <= 7) ? 'ok' : 'degraded';
return [
'status' => $status,
'installed' => true,
'last_backup' => $latest->backupstart,
'last_status' => $latest->status,
'days_since' => $daysSince,
'message' => 'MokoSuiteBackup: last backup ' . $daysSince . 'd ago (' . $latest->status . ')',
];
}
/**
* Check Admin Tools status — WAF status, security exceptions.
*
@@ -1083,6 +1131,7 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
return [
'status' => 'ok',
'installed' => false,
'message' => 'Admin Tools is not installed',
];
}
@@ -1189,7 +1238,7 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
}
catch (\Exception $e)
{
return ['status' => 'ok', 'https' => false];
return ['status' => 'error', 'message' => 'SSL check failed: ' . $e->getMessage()];
}
}
@@ -1209,7 +1258,7 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
if (!in_array($prefix . 'scheduler_tasks', $tables))
{
return ['status' => 'ok', 'available' => false];
return ['status' => 'ok', 'available' => false, 'message' => 'Task Scheduler not available'];
}
$db->setQuery(
@@ -1274,7 +1323,7 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
}
catch (\Exception $e)
{
return ['status' => 'ok', 'available' => false];
return ['status' => 'error', 'message' => 'Scheduler check failed: ' . $e->getMessage()];
}
}
@@ -1701,6 +1750,7 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
return [
'status' => $status,
'message' => $issues ? implode('; ', $issues) : 'All configuration settings are optimal',
'debug' => $debug,
'error_report' => $errorReport,
'gzip' => $gzip,
@@ -1709,7 +1759,6 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
'force_ssl' => $forceSSL,
'caching' => $caching,
'lifetime' => $lifetime,
'issues' => $issues ?: null,
];
}
@@ -1786,6 +1835,36 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
*/
protected function getDevAliasDomain(): string
{
// Check devtools plugin params for custom dev domain
try
{
$db = Factory::getDbo();
$db->setQuery(
$db->getQuery(true)
->select($db->quoteName('params'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('element') . ' = ' . $db->quote('mokosuiteclient_devtools'))
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
);
$devParams = json_decode((string) $db->loadResult());
if ($devParams && ($devParams->dev_domain_enabled ?? '1') === '0')
{
return '';
}
if (!empty($devParams->dev_domain))
{
return trim($devParams->dev_domain);
}
}
catch (\Throwable $e)
{
// Fall through to default
}
// Default: dev.{primary_domain}
$primary = $this->getPrimaryHost();
return !empty($primary) ? 'dev.' . $primary : '';
@@ -1800,10 +1879,16 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
*/
protected function isDevAlias(): bool
{
$currentHost = $_SERVER['HTTP_HOST'] ?? '';
$devDomain = $this->getDevAliasDomain();
$devAlias = $this->getDevAliasConfig();
return !empty($devDomain) && strcasecmp($currentHost, $devDomain) === 0;
if ($devAlias === null)
{
return false;
}
$currentHost = $_SERVER['HTTP_HOST'] ?? '';
return !empty($currentHost) && strcasecmp($currentHost, $devAlias['domain']) === 0;
}
protected function getCurrentAlias()
@@ -1888,32 +1973,51 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
*/
protected function handleSiteAlias()
{
// The dev alias (dev.{primary_domain}) always bypasses offline mode
if ($this->isDevAlias())
$devAlias = $this->getDevAliasConfig();
if ($devAlias === null)
{
return;
}
// Check if current request is on the dev domain
$currentHost = $_SERVER['HTTP_HOST'] ?? '';
if (empty($currentHost) || strcasecmp($currentHost, $devAlias['domain']) !== 0)
{
return;
}
// Bypass offline mode if enabled
if (!empty($devAlias['bypass_offline']))
{
$this->app->getConfig()->set('offline', 0);
return;
}
}
/**
* 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)
{
// Always noindex/nofollow on the dev alias domain
if ($this->isDevAlias())
$devAlias = $this->getDevAliasConfig();
if ($devAlias === null)
{
$doc->setMetaData('robots', 'noindex, nofollow');
return;
}
$currentHost = $_SERVER['HTTP_HOST'] ?? '';
if (empty($currentHost) || strcasecmp($currentHost, $devAlias['domain']) !== 0)
{
return;
}
// Set robots directive from devtools config
$robots = $devAlias['robots'] ?? 'noindex, nofollow';
$doc->setMetaData('robots', $robots);
// Inject canonical URL pointing to the primary domain
$primaryHost = $this->getPrimaryHost();
$currentUri = Uri::getInstance();
@@ -1921,6 +2025,98 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
$doc->addHeadLink($canonical, 'canonical');
}
/**
* Get the alias config matching the current request domain.
*
* Reads the site_aliases subform from DevTools plugin params.
* Each entry has: domain, offline_bypass, robots, label.
* Also auto-includes dev.{primary_domain} if no aliases are configured.
*
* @return array|null ['domain' => '...', 'bypass_offline' => bool, 'robots' => '...', 'label' => '...'] or null
*/
private function getDevAliasConfig(): ?array
{
static $config = false;
if ($config !== false)
{
return $config;
}
$config = null;
$currentHost = $_SERVER['HTTP_HOST'] ?? '';
if (empty($currentHost))
{
return null;
}
try
{
$db = Factory::getDbo();
$db->setQuery(
$db->getQuery(true)
->select($db->quoteName('params'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('element') . ' = ' . $db->quote('mokosuiteclient_devtools'))
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
);
$devParams = json_decode((string) $db->loadResult(), true) ?: [];
$aliases = $devParams['site_aliases'] ?? [];
// Normalize — Joomla subform stores as object or array
if (\is_object($aliases))
{
$aliases = (array) $aliases;
}
// Check each configured alias against current host
foreach ($aliases as $entry)
{
$entry = (array) $entry;
$domain = trim($entry['domain'] ?? '');
if (!empty($domain) && strcasecmp($currentHost, $domain) === 0)
{
$config = [
'domain' => $domain,
'bypass_offline' => ($entry['offline_bypass'] ?? '1') === '1',
'robots' => $entry['robots'] ?? 'noindex, nofollow',
'label' => $entry['label'] ?? '',
];
return $config;
}
}
// Auto-include dev.{primary_domain} if no aliases configured
if (empty($aliases))
{
$primary = $this->getPrimaryHost();
$devDomain = !empty($primary) ? 'dev.' . $primary : '';
if (!empty($devDomain) && strcasecmp($currentHost, $devDomain) === 0)
{
$config = [
'domain' => $devDomain,
'bypass_offline' => true,
'robots' => 'noindex, nofollow',
'label' => 'Development',
];
return $config;
}
}
}
catch (\Throwable $e)
{
$config = null;
}
return $config;
}
// ------------------------------------------------------------------
// Heartbeat (called from onExtensionAfterSave)
// ------------------------------------------------------------------
@@ -2506,9 +2702,12 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
$config = Factory::getConfig();
$timestamp = time();
$devDomain = $this->getDevAliasDomain();
$payload = [
'token' => $healthToken,
'domain' => $domain,
'dev_domain' => $devDomain ?: null,
'site_name' => $config->get('sitename', 'Joomla'),
'site_url' => $siteUrl,
'joomla_version' => (new Version())->getShortVersion(),
@@ -2561,15 +2760,18 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
if ($response->code >= 200 && $response->code < 300)
{
$this->app->enqueueMessage('MokoSuiteClientHQ heartbeat: site registered', 'message');
$this->app->enqueueMessage('MokoSuiteHQ heartbeat: site registered successfully.', 'message');
}
else
{
$body = json_decode($response->body, true);
$msg = $body['error'] ?? $body['message'] ?? ('HTTP ' . $response->code);
Log::add(
\sprintf('Heartbeat HTTP %d: %s', $response->code, $response->body),
Log::WARNING,
'mokosuiteclient'
);
$this->app->enqueueMessage('MokoSuiteHQ heartbeat failed: ' . $msg, 'warning');
}
}
catch (\Throwable $e)
@@ -8,7 +8,7 @@
* FILE INFORMATION
* DEFGROUP: Joomla.Plugin
* INGROUP: MokoSuiteClient
* VERSION: 02.44.00
* VERSION: 02.47.03
* PATH: /src/Field/CopyableTokenField.php
* BRIEF: Read-only token field with a copy-to-clipboard button
*/
@@ -0,0 +1,367 @@
<?php
/**
* @package MokoSuiteClient
* @subpackage plg_system_mokosuiteclient
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Plugin\System\MokoSuiteClient\Helper;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\Database\DatabaseInterface;
/**
* MokoGitea License Validator — core DRM enforcement for the MokoSuite platform.
*
* Validates the site's DLID against MokoGitea, caches the result,
* and provides entitlement checking for all suite modules.
*
* Default Gitea server: git.mokoconsulting.tech
*
* @since 02.45.00
*/
final class LicenseValidator
{
/** @var string Default MokoGitea server address */
private const DEFAULT_GITEA_URL = 'https://git.mokoconsulting.tech';
/** @var int Cache TTL in seconds (24 hours) */
private const CACHE_TTL = 86400;
/** @var int Grace period in days after expiry before deactivation */
private const DEFAULT_GRACE_DAYS = 7;
/** @var object|null Cached license data for current request */
private static ?object $cachedLicense = null;
/**
* Validate the site's DLID against MokoGitea.
* Returns cached result if still valid; calls API if expired.
*/
public static function validate(bool $forceRefresh = false): object
{
if (self::$cachedLicense && !$forceRefresh) {
return self::$cachedLicense;
}
$db = Factory::getContainer()->get(DatabaseInterface::class);
$dlid = self::getDlid();
if (!$dlid) {
return self::$cachedLicense = (object) [
'valid' => false,
'status' => 'no_dlid',
'message' => 'No license key configured',
'entitlements'=> [],
];
}
// Check DB cache first
if (!$forceRefresh) {
$cached = self::getCachedResult($db, $dlid);
if ($cached) {
return self::$cachedLicense = $cached;
}
}
// Call MokoGitea API
$result = self::callGiteaApi($dlid);
// Cache the result
self::cacheResult($db, $dlid, $result);
return self::$cachedLicense = $result;
}
/**
* Check if the current license includes entitlement for a specific extension.
*
* @param string $extension Extension element name (e.g., 'com_mokosuite_crm', 'com_mokosuiterestaurant')
* @return bool
*/
public static function isEntitled(string $extension): bool
{
$license = self::validate();
if (!$license->valid) return false;
// Map extension names to repo identifiers
$repoMap = [
'com_mokosuite' => 'MokoSuite',
'com_mokosuite_crm' => 'MokoSuiteCRM',
'com_mokosuite_erp' => 'MokoSuiteERP',
'com_mokosuitechild' => 'MokoSuiteChild',
'com_mokosuitecreate' => 'MokoSuiteCreate',
'com_mokosuitenpo' => 'MokoSuiteNPO',
'com_mokosuitefield' => 'MokoSuiteField',
'com_mokosuitepos' => 'MokoSuitePOS',
'com_mokoshop' => 'MokoSuiteShop',
'com_mokosuitehrm' => 'MokoSuiteHRM',
'com_mokosuitemrp' => 'MokoSuiteMRP',
'com_mokosuiterestaurant' => 'MokoSuiteRestaurant',
];
$repo = $repoMap[$extension] ?? $extension;
$entitlements = $license->entitlements ?? [];
// Base is always entitled if license is valid
if ($repo === 'MokoSuite') return true;
return in_array($repo, $entitlements, true);
}
/**
* Get the full license status for admin display.
*/
public static function getStatus(): object
{
$license = self::validate();
return (object) [
'valid' => $license->valid ?? false,
'status' => $license->status ?? 'unknown',
'tier' => $license->tier ?? 'none',
'entitlements' => $license->entitlements ?? [],
'expires_at' => $license->expires_at ?? null,
'seats' => $license->seats ?? 0,
'seats_used' => $license->seats_used ?? 0,
'days_remaining'=> self::getDaysRemaining($license),
'in_grace' => self::isInGracePeriod($license),
'gitea_url' => self::getGiteaUrl(),
'dlid_configured' => (bool) self::getDlid(),
];
}
/**
* Get available seat count.
*/
public static function getAvailableSeats(): int
{
$license = self::validate();
$total = (int) ($license->seats ?? 0);
$used = (int) ($license->seats_used ?? 0);
if ($total === 0) return PHP_INT_MAX; // Unlimited seats
return max(0, $total - $used);
}
/**
* Report a heartbeat to MokoGitea (active installation check).
* Called by task scheduler daily.
*/
public static function heartbeat(): object
{
$dlid = self::getDlid();
if (!$dlid) return (object) ['success' => false, 'error' => 'No DLID'];
$giteaUrl = self::getGiteaUrl();
$siteUrl = \Joomla\CMS\Uri\Uri::root();
$joomlaVersion = (new \Joomla\CMS\Version())->getShortVersion();
// Count installed suite modules
$db = Factory::getContainer()->get(DatabaseInterface::class);
$db->setQuery($db->getQuery(true)
->select('element')
->from('#__extensions')
->where($db->quoteName('element') . ' LIKE ' . $db->quote('com_mokosuite%'))
->where($db->quoteName('type') . ' = ' . $db->quote('component'))
->where($db->quoteName('enabled') . ' = 1'));
$installedModules = $db->loadColumn() ?: [];
$response = self::httpPost($giteaUrl . '/api/v1/licenses/heartbeat', [
'dlid' => $dlid,
'site_url' => $siteUrl,
'joomla_version' => $joomlaVersion,
'installed_modules' => $installedModules,
'php_version' => PHP_VERSION,
]);
return $response;
}
// ── Private methods ─────────────────────────────────
/**
* Get the configured DLID from component params.
*/
private static function getDlid(): string
{
try {
$params = Factory::getApplication()->getParams('com_mokosuite');
return trim($params->get('dlid', ''));
} catch (\Throwable $e) {
// Component not installed or params not available
try {
$db = Factory::getContainer()->get(DatabaseInterface::class);
$db->setQuery($db->getQuery(true)
->select('params')
->from('#__extensions')
->where($db->quoteName('element') . ' = ' . $db->quote('com_mokosuite'))
->where($db->quoteName('type') . ' = ' . $db->quote('component')));
$paramsJson = $db->loadResult();
$params = json_decode($paramsJson ?: '{}', false);
return trim($params->dlid ?? '');
} catch (\Throwable $e2) {
return '';
}
}
}
/**
* Get the MokoGitea server URL from config.
*/
private static function getGiteaUrl(): string
{
try {
$params = Factory::getApplication()->getParams('com_mokosuite');
return rtrim($params->get('gitea_url', self::DEFAULT_GITEA_URL), '/');
} catch (\Throwable $e) {
return self::DEFAULT_GITEA_URL;
}
}
/**
* Call MokoGitea license validation API.
*/
private static function callGiteaApi(string $dlid): object
{
$giteaUrl = self::getGiteaUrl();
$response = self::httpGet($giteaUrl . '/api/v1/licenses/validate?dlid=' . urlencode($dlid));
if (isset($response->valid)) {
return (object) [
'valid' => (bool) $response->valid,
'status' => $response->status ?? 'unknown',
'tier' => $response->tier ?? '',
'entitlements' => $response->entitlements ?? $response->repo_scope ?? [],
'expires_at' => $response->expires_at ?? null,
'seats' => (int) ($response->seats ?? 0),
'seats_used' => (int) ($response->seats_used ?? 0),
'message' => $response->message ?? '',
];
}
// API error — use cached result if available, otherwise fail gracefully
return (object) [
'valid' => false,
'status' => 'api_error',
'message' => $response->error ?? 'Could not reach license server',
'entitlements' => [],
];
}
/**
* Get cached validation result from database.
*/
private static function getCachedResult(DatabaseInterface $db, string $dlid): ?object
{
$dlidHash = hash('sha256', $dlid);
try {
$db->setQuery($db->getQuery(true)
->select('response_data, checked_at')
->from('#__mokosuite_license_cache')
->where($db->quoteName('dlid_hash') . ' = ' . $db->quote($dlidHash))
->where('checked_at > DATE_SUB(NOW(), INTERVAL ' . (int) self::CACHE_TTL . ' SECOND)'));
$cached = $db->loadObject();
if ($cached && $cached->response_data) {
$data = json_decode($cached->response_data, false);
if ($data) return $data;
}
} catch (\Throwable $e) {
// Table may not exist yet — that's fine
}
return null;
}
/**
* Cache validation result in database.
*/
private static function cacheResult(DatabaseInterface $db, string $dlid, object $result): void
{
$dlidHash = hash('sha256', $dlid);
try {
// Upsert
$db->setQuery('REPLACE INTO #__mokosuite_license_cache (dlid_hash, response_data, checked_at) VALUES ('
. $db->quote($dlidHash) . ', '
. $db->quote(json_encode($result)) . ', '
. $db->quote(Factory::getDate()->toSql()) . ')');
$db->execute();
} catch (\Throwable $e) {
// Cache table may not exist — non-fatal
}
}
/**
* Calculate days remaining on license.
*/
private static function getDaysRemaining(object $license): ?int
{
if (empty($license->expires_at)) return null;
$now = new \DateTime('today');
$expiry = new \DateTime($license->expires_at);
$diff = (int) $now->diff($expiry)->format('%r%a');
return $diff;
}
/**
* Check if license is in grace period (expired but within grace window).
*/
private static function isInGracePeriod(object $license): bool
{
$days = self::getDaysRemaining($license);
if ($days === null || $days >= 0) return false;
$graceDays = self::DEFAULT_GRACE_DAYS;
try {
$graceDays = (int) Factory::getApplication()->getParams('com_mokosuite')->get('license_grace_days', self::DEFAULT_GRACE_DAYS);
} catch (\Throwable $e) {}
return abs($days) <= $graceDays;
}
/**
* HTTP GET helper.
*/
private static function httpGet(string $url): object
{
$response = file_get_contents($url, false, stream_context_create([
'http' => [
'method' => 'GET',
'header' => 'Accept: application/json',
'ignore_errors' => true,
'timeout' => 10,
],
]));
return json_decode($response ?: '{}', false) ?: (object) ['error' => 'No response'];
}
/**
* HTTP POST helper.
*/
private static function httpPost(string $url, array $data): object
{
$response = file_get_contents($url, false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nAccept: application/json",
'ignore_errors' => true,
'timeout' => 10,
'content' => json_encode($data),
],
]));
return json_decode($response ?: '{}', false) ?: (object) ['error' => 'No response'];
}
}
@@ -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.44.00</version>
<version>02.47.03</version>
<description>MokoSuiteClient core system plugin — coordinates feature plugins, heartbeat, health checks, and admin customizations.</description>
<namespace path=".">Moko\Plugin\System\MokoSuiteClient</namespace>
<scriptfile>script.php</scriptfile>
@@ -100,7 +100,7 @@
filter="url" />
<field name="monitor_signing_key" type="hidden"
default="LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tDQpNSUlFdmdJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0JLZ3dnZ1NrQWdFQUFvSUJBUUMvcnVrWE0zZHB0aDg2DQpGSkRXTjM0ZjQ2cUtJem1SMmFtTWUyZ2dWbWxsWnFyMHJkRFk4OTdtQ05FRkk4Q0NwNGR5amkwOU5ETnAvalFxDQovL2JGdUFNOUFTZU5oQTlmRlpwSG5UMGkzY3N4V3RSS2NnMnRkR0wzUXhNRFVBeFJYQ1RSQXVPSWZybGp6Ky85DQpWZ0ZtWHU3M1VSaU9XY1lLeFErejFoZkRGK2ZxRTRlYW9QcUlsY2J5dmtKd2lkSkRWUEEwc2RtbVlUTFg2Q29xDQpQalVDRENlbkZoUXNteVMzM29KSXArK0c3ZzU5NmRYelZIczRQSjIwNnc0Z3JlckRRZk5GVytzZndHSnl3NjBrDQpUQTVmUzF2Wit4NEt2UUh6V1ErYS9xRS9sSGxFVzdOTWVJWExNWGczSDd1eXBabXlVU2t3S0k0djFQQXRGWmtkDQpBaVpPZWZpVkFnTUJBQUVDZ2dFQVI0VGJyVDR0NWJ5MDhIQW0wcTR3WVF4REhEbVlJbzNXdDZ5MURmYU11OVMzDQpDYW5TMm9oazJzaE9TcGhhU2hFajI3WjBKY2hYdjhYWURvbU1BZmVsN3I5eDZjQ2FhTVdUNEdCMU5Zckp1NDhBDQprV2NteTkwWitPNTZQZkZJeTJXdXV6dFRxaFdZb0ZDSTBOZlU2bGw5SzhpSFl6VWx1MzZSSklweWx5OXFPKyt4DQpmTUZYcUovSkk0bVp6NW0raDBnbFMvN21VZ0EvUTRjbVJnRHJ3dkc3bEpBRjhWSDBEdW1uRWJkWkZvSi9XbU9JDQpSTi9lemhqczYrbU9hTnUwQWRsclpLU3QwRWZVYjl3QTFLQm5JMVVDU2w0Y1lidXVpL29jOWo1aGl6RGJvRWRyDQpJL1U5Y2FYUmZvb0pMNlUwOXN1VTdyTlFLbFRhMXM4NVhvY3htT0JMK1FLQmdRRHg5QzB5MjQ5SG1paXJ2WExIDQpBUXdUTjRyMjdhUTZMMFc2SHdDNHdzMUhleDRpeWRXT1lIcWdBSnY4VHZyeVpHOW1SaFh1U1ROTjYxV1UvTWFNDQphQVYwVjJ4Y0RrdDNFUnhNak1XRmhXUTh0cjN2RUtqWjFnOVJXOGhiTE9VYXVCcmJhMlI4RWNZYXFLZXlxR3N4DQpCa0VLZlRIUzNmUysraXNLZ2EzUU1mcjB6d0tCZ1FES3o2SGVKZ0tKRTVMM1ppbkhxaUFyVm5SZ2pYcFZrMWpvDQp6VXh5eTkwNEhmNGlmVXNIZklpdzVpN0VNR0U0RE5ob2MvZUJxcW1oM1N2ejJMUDNzOHUrL0hVZFllVzJIV1hhDQpKZlpMRE5BM0U3WDNkSVJ6MFg5UTh2OHcxaFpQeUxYOUlYeUVyUTNGZHFVdyt1Tko1VFZJell0RHppNnRKTjkvDQpGZGlxS0Q2ZFd3S0JnUURnQnE5bS9LWmdyTnRsa1FkYVBaejVtaDhBWGE4RzlNaEIrZnpJRmc3T1ZhL2tsQzg1DQpJaG5JVm1nWHFPVndWQkJWaVNVN09lbllCc042TE1hR01MYUVMNEkwaGtQWG5pOHVyZFVodVEzRHJZeVZjejUwDQpYR0JZZTN3Njk0bTJRS3NWYVExa1YyeXZPR1AxNXoxQTZrS0V2TURLTnhzclRTVlhHQlZneFRaUlB3S0JnUURBDQp1RFVVcUFIWXlDVHJ1c1VRMm5UZk9iUTAyN3ZYL2NDSzJDdEJHc0FJUjFmcTVpeVozSmozb0lQb0lpRC81aFR1DQpqT1F3N3o5cWRJVURublRGZUxDdnQ2NkNVVGk3cVl2VGxDZEtnYzZKeDgwdWJDWkErRjZIU2FGOWdyS0k5aTBaDQpjT3ltRnR2elBCOFZRQk1qY1E0Rk0yeVc3aUlrbmRsVEppdFE1aFU1NlFLQmdEZ1JIOXBEcGZwWlZ2V2g2MldGDQp5OGZzWUo1ODhzQmRMUlpTYTRuNi9XbjdUcUp1bWg2aWpFcDVyZFdnQkVtaDlJSk9jRUlhZ05mK0s5MXdoaThvDQpTeW01ajJpL1pjVVFYNFJSTDNxQ1RZZWVQVnZ3RHc3aWNLWVowTGQ2S1pFMmdEaDRPbEg4ejU0Zkl3a2tMSzRFDQpCcmtJNWppa05QSkJFR25zTm9zU3pWN2QNCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0NCg=="
default="LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRQ2xZNnNzOTZpeTZOOGMKTHRxbndhbnU4eEozdDcrdDhXT3hoY0Yyclc2QmlmOVhNaEpnYkw0c055N0wwV1dTT2tkMmZxalBNcDFtOFNyNAo1VnNycjE3cFc5b0FNMmtmdFdsaTZ1NkhTVEYyN2pVVUJrT3o4MHZMRklMMGNGNkJCUkpYN2JVWkRpamdUMjc1ClREb3dXZy82Zk9GeWFEelBHUkJuYXFacTljU2lEYWoyNlpSTVZIbktQUERWTG92VzRPTDQzL2gwZ3BtN25nUGIKdWJlLzFFTDRUMHFRbm1Xc2FEOFZ6VStoRXFGSDRTVUtMaDVNeklGbUxFZzRlZ0xCbTBXcWdxbzZRQVBnZDVPYgoybXhmQndta3RLVm5hcWR6eG9KSytzaTVuZkYreGpxbWRMZThUdmEyTHNuTUxlZmsrODVoQ3hxS2x1eWRta1lXCjlvUk5qcDhiQWdNQkFBRUNnZ0VBQkZOUS9NSVZaV2gxdlZUMFh3TFBvUEkyZjI4TTBrM0gzN0t4MXBxK2t5QzYKenRyK1pBczBCaEFEWjAwNHJOUmRYaG45N0QxVXBJYVdLeUJFZkNZQUEzWmxneS9WQmdGR21sR3VuMWNvdGdXUQoyYzg0SWhLdzNzVFFqL2dJWUxOelFWMTBLUTJYd0JZVHZ1MWhjRFpLeUxCUGJTQ1F4cEhQUGdVcUNRNFljR3lFClErVmc1dHJUYk8wQ2xCZ1U5bkVnYU1RakRJZ0F3WVZPV203dUxJTW84UC9nT3FuT2tmaFhzdzl3VTJVYWxFeTEKRmRZbGhMbGJ0ZS9MZ3lkYlJ2RStjNEtqZVp0Z3ptc1RneEh2dzM5YVVmZUZTclFRT0FjcXc0alNzUjdMck9UZAp5bDhpelRrZVBrTVFMamFqR0pabWdPbitkRzhtUlpMa3FKcWdGaVpqRVFLQmdRRFV0L0xlU0h5SmhvY3VFL240CkZreEpaclJoWUVsWnc2WlZJUnQzWDlPQ1Nmaklab3I1ZkZlczhvUzZySFhKdGZYeWx4QUxOSjJjTUhKTTViVnUKbUFSUFU4cThBeVc0OE03cHAyNmtVVTMxNXc2OU1SUkhzbWgyekRabEtDeG5GM1NSQ3U4YW95d3hZc3RUZ3hkTgo2bDhLNHZsS1dsN3FYblBhWjZjb3lQSU9od0tCZ1FESENuRmRRdW5SMVI2dkxGaVFZMTRiT3QwT0tzVGJYMUJyCmpvUGZySkxvRm5mSCs4VDVnNUdxYkV5T2p0WG1tRXhmTFFpcDBQVXRtc1E0YXlJRFBZYWZtU3RpK2dtQXZFd1MKZTlKcVYxYlRuazUrYnVRZ2FlOW16REpJWkxaczRJUlhrd1Q5aDZ4Q2xKeS80TGJSRHdBU3dUVGJlY01hN3A4UgpQN0p0bjdsYnpRS0JnQzNOR2FjUTFuZktGb3N1VS9FOTQ5a2VHeEtvWjhMREpLcEp3WjgzYTlRdTF6bFhFdTlhCi9ZbklnaG1yam9VSy85VG0vOVpaMHVIUmNKcnNEdCtzTGFsaThsRC9JSDBzcEhDYzAyN2Y3cmhXc3M2N3BaRTIKY2RXNmJLL2xNWUpWQTQxRFhHNVEyZkFjUklsTHZaWFNNL3FsR21ZUEJVYlRaWUNPTnVqS000dzdBb0dBU1dBdwpLcEZnWVZxUDFVUWo0aGEvdW9vWXRBQlFVZzd4TnJWektDSVdoampDTDVkQkpqcTZtSGtVUC9tb0lUcEQ3VkpNCnYwMnBGUWJaRDNOdk5vS1gvbjRZNElRTXZNaXR3cUtqRDFEalVXQXF6N0ZScUNGbGdDQUc2V2szVnl2dG5kczEKRzhISVgwTXFCaEp4VXVDVXhsVXpoelY4RjVHZ1VsdUpDNkMyVklFQ2dZQkJWSkxpZlNVOTlHWGZtK3dPd0RWcgo2bHZoUFgxOTBGVktWQXY3aVVWTXBwWXg4Y0QxYkcyUjRLT29JbnkxYTlxdjA2ZGFzeGVQOStkVjJVMWU3MWl5CkFXWDRBVHIrYitvSGk2eUk1MXRHRk54RUxiNXZYMVpYM3VNaDlWM29iYUpuSFNjYllpKzBBNjlyRmNuNEZuLzUKWXJybWxLTzRlRHFVZkswbVFJVCtwUT09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
filter="raw" />
</fieldset>
</fields>
@@ -22,7 +22,7 @@
* DEFGROUP: Joomla.Plugin
* INGROUP: MokoSuiteClient
* REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
* VERSION: 02.44.00
* VERSION: 02.47.03
* PATH: /src/script.php
* BRIEF: Installation script for MokoSuiteClient plugin
* NOTE: Handles installation, update, and uninstallation tasks including language override deployment
@@ -22,7 +22,7 @@
* DEFGROUP: Joomla.Plugin
* INGROUP: MokoSuiteClient
* REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
* VERSION: 02.44.00
* VERSION: 02.47.03
* PATH: /src/services/provider.php
* BRIEF: Service provider for dependency injection in Joomla 5.x
* NOTE: Registers the plugin with Joomla's DI container
@@ -8,7 +8,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>PLG_SYSTEM_MOKOSUITECLIENT_BACKUP_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteClientBackup</namespace>
@@ -118,13 +118,11 @@ class Backup extends CMSPlugin implements SubscriberInterface
}
// Prefer MokoSuiteBackup's own helper (clean public API)
$helperClass = 'Joomla\\Component\\MokoSuiteBackup\\Administrator\\Utility\\BackupStatusHelper';
$helperClass = 'Joomla\\Component\\MokoSuiteBackup\\Administrator\\Helper\\BackupStatusHelper';
if (class_exists($helperClass))
{
$staleDays = (int) $this->params->get('stale_days', 7);
return $helperClass::getStatus($staleDays);
return $helperClass::getStatusSummary();
}
// Fallback: direct table query for older MokoSuiteBackup versions
@@ -244,20 +242,52 @@ class Backup extends CMSPlugin implements SubscriberInterface
? round($latest->total_size / 1048576)
: null;
// Total counts (all time)
$db->setQuery(
$db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__mokosuitebackup_records'))
);
$allTime = (int) $db->loadResult();
$db->setQuery(
$db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__mokosuitebackup_records'))
->where($db->quoteName('status') . ' = ' . $db->quote('fail'))
);
$allFailed = (int) $db->loadResult();
// Recent failures
$cutoff7d = date('Y-m-d H:i:s', strtotime('-7 days'));
$db->setQuery(
$db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__mokosuitebackup_records'))
->where($db->quoteName('status') . ' = ' . $db->quote('fail'))
->where($db->quoteName('backupstart') . ' >= ' . $db->quote($cutoff7d))
);
$recentFailed = (int) $db->loadResult();
return [
'installed' => true,
'status' => $status,
'last_backup' => $latest->backupstart,
'last_status' => $latest->status,
'last_size_mb' => $sizeMb,
'days_since' => $daysSince,
'backup_type' => $latest->backup_type,
'origin' => $latest->origin,
'total_backups' => $totalBackups,
'recent_7d' => $recentBackups,
'fail_count_7d' => $failCount7d,
'files_exist' => (bool) $latest->filesexist,
'description' => $latest->description,
'installed' => true,
'latest' => [
'status' => $latest->status,
'backup_type' => $latest->backup_type ?? 'full',
'description' => $latest->description ?? '',
'backup_start' => $latest->backupstart,
'backup_end' => $latest->backupend ?? null,
'total_size' => (int) ($latest->total_size ?? 0),
'origin' => $latest->origin ?? 'backend',
],
'totals' => [
'all_time' => $allTime,
'all_success' => $totalBackups,
'all_failed' => $allFailed,
'recent_total' => $recentBackups + $recentFailed,
'recent_success' => $recentBackups,
'recent_failed' => $recentFailed,
],
];
}
}
@@ -8,7 +8,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>PLG_SYSTEM_MOKOSUITECLIENT_DBIP_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteClientDBIP</namespace>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<field name="domain" type="text"
label="Domain"
hint="dev.clientsite.com"
filter="raw"
required="true" />
<field name="offline_bypass" type="list" default="1"
label="Offline Mode">
<option value="1">Bypass (stay online)</option>
<option value="0">Respect (go offline)</option>
</field>
<field name="robots" type="list" default="noindex, nofollow"
label="Robots">
<option value="noindex, nofollow">noindex, nofollow</option>
<option value="noindex">noindex</option>
<option value="index, follow">index, follow (production)</option>
</field>
<field name="label" type="text"
label="Label"
hint="Development, Staging, QA..."
filter="string" />
</form>
@@ -15,3 +15,8 @@ PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_DELETE_VERSIONS_LABEL="Delete All Versions"
PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_DELETE_VERSIONS_DESC="One-shot: delete all content version history on save. Automatically turns off after execution."
PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_RESET_DLKEYS_LABEL="Reset Download Keys"
PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_RESET_DLKEYS_DESC="One-shot: clear all download keys (dlid) from update sites on save. Automatically turns off after execution."
PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_FIELDSET_ALIASES="Mirror Domains & Staging Environments"
PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_FIELDSET_ALIASES_DESC="Configure domain aliases that share this site's hosting folder. Each mirror can independently bypass offline mode and control search engine indexing."
PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_ALIASES_LABEL="Domain Mirrors"
PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_ALIASES_DESC="Add CNAME domains for development, staging, or QA. Each mirror gets its own offline and robots settings while sharing the same database and files."
@@ -8,13 +8,14 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteClientDevTools</namespace>
<files>
<folder>src</folder>
<folder>services</folder>
<folder>forms</folder>
<folder>language</folder>
</files>
@@ -61,6 +62,20 @@
<option value="0">JNO</option>
</field>
</fieldset>
<fieldset name="site_aliases"
label="PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_FIELDSET_ALIASES"
description="PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_FIELDSET_ALIASES_DESC">
<field name="site_aliases" type="subform"
label="PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_ALIASES_LABEL"
description="PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_ALIASES_DESC"
formsource="plugins/system/mokosuiteclient_devtools/forms/site_alias_entry.xml"
multiple="true"
layout="joomla.form.field.subform.repeatable-table"
groupByFieldset="false"
buttons="add,remove,move" />
</fieldset>
</fields>
</config>
</extension>
@@ -8,7 +8,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>PLG_SYSTEM_MOKOSUITECLIENT_FIREWALL_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteClientFirewall</namespace>
@@ -33,12 +33,17 @@
</languages>
<config>
<fields name="params">
<fields name="params"
addfieldprefix="Moko\Plugin\System\MokoSuiteClientFirewall\Field">
<!-- Network & Session -->
<fieldset name="basic"
label="PLG_SYSTEM_MOKOSUITECLIENT_FIREWALL_FIELDSET_BASIC"
description="PLG_SYSTEM_MOKOSUITECLIENT_FIREWALL_FIELDSET_BASIC_DESC">
<field name="current_ip_display" type="CurrentIp"
label="Your Current IP Address"
description="This is the IP address you are connecting from. Copy it to add to the Trusted IPs list below." />
<field name="force_https" type="radio" default="1"
label="PLG_SYSTEM_MOKOSUITECLIENT_FIREWALL_FORCE_HTTPS_LABEL"
description="PLG_SYSTEM_MOKOSUITECLIENT_FIREWALL_FORCE_HTTPS_DESC"
@@ -0,0 +1,42 @@
<?php
/**
* @package MokoSuiteClient
* @subpackage plg_system_mokosuiteclient_firewall
*/
namespace Moko\Plugin\System\MokoSuiteClientFirewall\Field;
defined('_JEXEC') or die;
use Joomla\CMS\Form\FormField;
/**
* Read-only field that displays the current user's IP address.
* Useful for quickly copying the IP to add to trusted IPs list.
*/
class CurrentIpField extends FormField
{
protected $type = 'CurrentIp';
protected function getInput(): string
{
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
if (!empty($ip))
{
$ip = trim(explode(',', $ip)[0]);
}
if (empty($ip))
{
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
return '<div class="d-flex align-items-center gap-2">'
. '<code style="font-size:1.1rem;padding:0.4rem 0.8rem;background:#f8f9fa;border:1px solid #dee2e6;border-radius:4px;" id="mokosuiteclient-current-ip">'
. htmlspecialchars($ip)
. '</code>'
. '<button type="button" class="btn btn-sm btn-outline-secondary" onclick="navigator.clipboard.writeText(document.getElementById(\'mokosuiteclient-current-ip\').textContent.trim()).then(function(){this.textContent=\'Copied!\';var b=this;setTimeout(function(){b.textContent=\'Copy\'},1500)}.bind(this))" title="Copy IP to clipboard">Copy</button>'
. '</div>';
}
}
@@ -8,7 +8,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>PLG_SYSTEM_MOKOSUITECLIENT_LICENSE_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteClientLicense</namespace>
<files><folder>src</folder><folder>services</folder><folder>language</folder></files>
@@ -1,13 +0,0 @@
; MokoSuiteClient Health Monitor Plugin
; Copyright (C) 2026 Moko Consulting. All rights reserved.
; License: GPL-3.0-or-later
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR="System - MokoSuiteClient Monitor"
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_DESC="Sends heartbeat data to a MokoSuiteClientHQ control panel for centralized site monitoring."
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_FIELDSET_BASIC="Monitoring"
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_FIELDSET_BASIC_DESC="Configure heartbeat reporting to MokoSuiteClientHQ."
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_HEARTBEAT_LABEL="Send Heartbeat"
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_HEARTBEAT_DESC="Send heartbeat data to MokoSuiteClientHQ when plugin settings are saved."
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_BASE_URL_LABEL="MokoSuiteClientHQ URL"
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_BASE_URL_DESC="URL of the MokoSuiteClientHQ control panel (e.g. https://mokoconsulting.tech). The heartbeat is sent to /api/index.php/v1/mokosuiteclienthq/heartbeat on this host."
@@ -1,3 +0,0 @@
; MokoSuiteClient Health Monitor Plugin - System strings
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR="System - MokoSuiteClient Monitor"
PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_DESC="Site health monitoring, MokoSuiteClientHQ heartbeat integration, and diagnostics."
@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="system" method="upgrade">
<name>System - MokoSuiteClient Monitor</name>
<element>mokosuiteclient_monitor</element>
<author>Moko Consulting</author>
<creationDate>2026-06-02</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.44.00</version>
<description>PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteClientMonitor</namespace>
<files>
<folder>src</folder>
<folder>services</folder>
<folder>language</folder>
</files>
<languages folder="language">
<language tag="en-GB">en-GB/plg_system_mokosuiteclient_monitor.ini</language>
<language tag="en-GB">en-GB/plg_system_mokosuiteclient_monitor.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic"
label="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_FIELDSET_BASIC"
description="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_FIELDSET_BASIC_DESC">
<field name="heartbeat_enabled" type="radio" default="1"
label="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_HEARTBEAT_LABEL"
description="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_HEARTBEAT_DESC"
class="btn-group btn-group-yesno">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field name="base_url" type="url"
default="https://waas.dev.mokoconsulting.tech"
label="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_BASE_URL_LABEL"
description="PLG_SYSTEM_MOKOSUITECLIENT_MONITOR_BASE_URL_DESC"
filter="url" />
<field name="signing_key" type="hidden"
default="LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tDQpNSUlFdmdJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0JLZ3dnZ1NrQWdFQUFvSUJBUUMvcnVrWE0zZHB0aDg2DQpGSkRXTjM0ZjQ2cUtJem1SMmFtTWUyZ2dWbWxsWnFyMHJkRFk4OTdtQ05FRkk4Q0NwNGR5amkwOU5ETnAvalFxDQovL2JGdUFNOUFTZU5oQTlmRlpwSG5UMGkzY3N4V3RSS2NnMnRkR0wzUXhNRFVBeFJYQ1RSQXVPSWZybGp6Ky85DQpWZ0ZtWHU3M1VSaU9XY1lLeFErejFoZkRGK2ZxRTRlYW9QcUlsY2J5dmtKd2lkSkRWUEEwc2RtbVlUTFg2Q29xDQpQalVDRENlbkZoUXNteVMzM29KSXArK0c3ZzU5NmRYelZIczRQSjIwNnc0Z3JlckRRZk5GVytzZndHSnl3NjBrDQpUQTVmUzF2Wit4NEt2UUh6V1ErYS9xRS9sSGxFVzdOTWVJWExNWGczSDd1eXBabXlVU2t3S0k0djFQQXRGWmtkDQpBaVpPZWZpVkFnTUJBQUVDZ2dFQVI4VGJyVDR0NWJ5MDhIQW0wcTR3WVF4REhEbVlJbzNXdDZ5MURmYU11OVMzDQpDYW5TMm9oazJzaE9TcGhhU2hFajI3WjBKY2hYdjhYWURvbU1BZmVsN3I5eDZjQ2FhTVdUNEdCMU5Zckp1NDhBDQprV2NteTkwWitPNTZQZkZJeTJXdXV6dFRxaFdZb0ZDSTBOZlU2bGw5SzhpSFl6VWx1MzZSSklweWx5OXFPKyt4DQpmTUZYcUovSkk0bVp6NW0raDBnbFMvN21VZ0EvUTRjbVJnRHJ3dkc3bEpBRjhWSDBEdW1uRWJkWkZvSi9XbU9JDQpSTi9lemhqczYrbU9hTnUwQWRsclpLU3QwRWZVYjl3QTFLQm5JMVVDU2w0Y1lidXVpL29jOWo1aGl6RGJvRWRyDQpJL1U5Y2FYUmZvb0pMNlUwOXN1VTdyTlFLbFRhMXM4NVhvY3htT0JMK1FLQmdRRHg5QzB5MjQ5SG1paXJ2WExIDQpBUXdUTjRyMjdhUTZMMFc2SHdDNHdzMUhleDRpeWRXT1lIcWdBSnY4VHZyeVpHOW1SaFh1U1ROTjYxV1UvTWFNDQphQVYwVjJ4Y0RrdDNFUnhNak1XRmhXUTh0cjN2RUtqWjFnOVJXOGhiTE9VYXVCcmJhMlI4RWNZYXFLZXlxR3N4DQpCa0VLZlRIUzNmUysraXNLZ2EzUU1mcjB6d0tCZ1FES3o2SGVKZ0tKRTVMM1ppbkhxaUFyVm5SZ2pYcFZrMWpvDQp6VXh5eTkwNEhmNGlmVXNIZklpdzVpN0VNR0U0RE5ob2MvZUJxcW1oM1N2ejJMUDNzOHUrL0hVZFllVzJIV1hhDQpKZlpMRE5BM0U3WDNkSVJ6MFg5UTh2OHcxaFpQeUxYOUlYeUVyUTNGZHFVdyt1Tko1VFZJell0RHppNnRKTjkvDQpGZGlxS0Q2ZFd3S0JnUURnQnE5bS9LWmdyTnRsa1FkYVBaejVtaDhBWGE4RzlNaEIrZnpJRmc3T1ZhL2tsQzg1DQpJaG5JVm1nWHFPVndWQkJWaVNVN09lbllCc042TE1hR01MYUVMNEkwaGtQWG5pOHVyZFVodVEzRHJZeVZjejUwDQpYR0JZZTN3Njk0bTJRS3NWYVExa1YyeXZPR1AxNXoxQTZrS0V2TURLTnhzclRTVlhHQlZneFRaUlB3S0JnUURBDQp1RFVVcUFIWXlDVHJ1c1VRMm5UZk9iUTAyN3ZYL2NDSzJDdEJHc0FJUjFmcTVpeVozSmozb0lQb0lpRC81aFR1DQpqT1F3N3o5cWRJVURublRGZUxDdnQ2NkNVVGk3cVl2VGxDZEtnYzZKeDgwdWJDWkErRjZIU2FGOWdyS0k5aTBaDQpjT3ltRnR2elBCOFZRQk1qY1E4Rk0yeVc3aUlrbmRsVEppdFE1aFU1NlFLQmdEZ1JIOXBEcGZwWlZ2V2g2MldGDQp5OGZzWUo1ODhzQmRMUlpTYTRuNi9XbjdUcUp1bWg2aWpFcDVyZFdnQkVtaDlJSk9jRUlhZ05mK0s5MXdoaThvDQpTeW01ajJpL1pjVVFYNFJSTDNxQ1RZZWVQVnZ3RHc3aWNLWVowTGQ2S1pFMmdEaDRPbEg4ejU0Zkl3a2tMSzRFDQpCcmtJNWppa05QSkJFR25zTm9zU3pWN2QNCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0NCg=="
filter="raw" />
</fieldset>
</fields>
</config>
</extension>
@@ -1,34 +0,0 @@
<?php
/**
* @package MokoSuiteClient
* @subpackage plg_system_mokosuiteclient_monitor
* @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\System\MokoSuiteClientMonitor\Extension\Monitor;
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 Monitor($dispatcher, (array) PluginHelper::getPlugin('system', 'mokosuiteclient_monitor'));
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};
@@ -1,353 +0,0 @@
<?php
/**
* @package MokoSuiteClient
* @subpackage plg_system_mokosuiteclient_monitor
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Plugin\System\MokoSuiteClientMonitor\Extension;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Version;
use Joomla\Event\SubscriberInterface;
use Moko\Plugin\System\MokoSuiteClient\Helper\MokoSuiteClientHelper;
/**
* MokoSuiteClient Health Monitor Plugin
*
* Sends heartbeat data to a MokoSuiteClientHQ control panel instance.
* Each request is RSA-signed with a private key distributed via
* the package manifest, verified by Base using the matching public key.
*
* @since 02.32.00
*/
class Monitor extends CMSPlugin implements SubscriberInterface
{
protected $autoloadLanguage = true;
public static function getSubscribedEvents(): array
{
return [
'onExtensionAfterSave' => 'onExtensionAfterSave',
'onAfterInitialise' => 'onAfterInitialise',
'onExtensionAfterInstall' => 'onExtensionAfterInstall',
];
}
/**
* Send heartbeat on first admin page load after install/update.
*/
public function onAfterInitialise(): void
{
$app = $this->getApplication();
if (!$app->isClient('administrator')) return;
if (!$this->params->get('heartbeat_enabled', 1)) return;
$session = \Joomla\CMS\Factory::getSession();
if ($session->get('mokosuiteclient.heartbeat_sent', false)) return;
// Check if version changed since last heartbeat
$lastVersion = $this->params->get('_last_heartbeat_version', '');
$currentVersion = $this->getMokoSuiteClientVersion();
if ($lastVersion !== $currentVersion)
{
$session->set('mokosuiteclient.heartbeat_sent', true);
$this->sendHeartbeat();
// Store version so we don't re-send every session
try
{
$this->params->set('_last_heartbeat_version', $currentVersion);
$extension = new \Joomla\CMS\Table\Extension(Factory::getDbo());
$extension->load(['element' => 'mokosuiteclient_monitor', 'folder' => 'system', 'type' => 'plugin']);
$extension->params = $this->params->toString();
$extension->store();
}
catch (\Throwable $e) {}
}
}
/**
* Send heartbeat immediately after package install/update.
*/
public function onExtensionAfterInstall($installer, $eid): void
{
if (!$this->params->get('heartbeat_enabled', 1)) return;
try { $this->sendHeartbeat(); }
catch (\Throwable $e) {}
}
/**
* After saving this plugin or the core plugin, send heartbeat.
*/
public function onExtensionAfterSave($event): void
{
// Joomla 6: single event object; Joomla 5: individual args
if (is_object($event) && method_exists($event, 'getArgument'))
{
$context = $event->getArgument('context', $event->getArgument(0, ''));
$table = $event->getArgument('subject', $event->getArgument(1, null));
}
else
{
$context = $event;
$table = func_get_arg(1);
}
if ($context !== 'com_plugins.plugin' || !$table)
{
return;
}
$element = $table->element ?? '';
if (!\in_array($element, ['mokosuiteclient', 'mokosuiteclient_monitor'], true))
{
return;
}
if (!$this->params->get('heartbeat_enabled', 1))
{
return;
}
$this->sendHeartbeat();
}
/**
* Send heartbeat to the MokoSuiteClientHQ control panel.
*
* The request is RSA-signed: the client signs domain|timestamp|token
* with its private key. Base verifies with the matching public key.
*/
private function sendHeartbeat(): void
{
$baseUrl = rtrim($this->params->get('base_url', ''), '/');
if (empty($baseUrl))
{
return;
}
$coreParams = MokoSuiteClientHelper::getCoreParams();
$healthToken = $coreParams->get('health_api_token', '');
if (empty($healthToken))
{
return;
}
$siteUrl = rtrim(Uri::root(), '/');
$domain = parse_url($siteUrl, PHP_URL_HOST) ?: '';
if (empty($domain))
{
return;
}
$app = $this->getApplication();
$config = Factory::getConfig();
$timestamp = time();
$payload = [
'token' => $healthToken,
'domain' => $domain,
'site_name' => $config->get('sitename', 'Joomla'),
'site_url' => $siteUrl,
'joomla_version' => (new Version())->getShortVersion(),
'php_version' => PHP_VERSION,
'mokosuiteclient_version' => $this->getMokoSuiteClientVersion(),
'timestamp' => $timestamp,
'client_info' => [
'company' => $config->get('sitename', ''),
'email' => $config->get('mailfrom', ''),
],
];
// Include live health data
$healthData = $this->fetchLocalHealth($siteUrl, $healthToken);
if ($healthData !== null)
{
$payload['health'] = $healthData;
}
// RSA sign the request
$headers = ['Content-Type: application/json'];
$signature = $this->signRequest($domain, $timestamp, $healthToken);
if ($signature !== null)
{
$headers[] = 'X-MokoSuite-Signature: ' . $signature;
$headers[] = 'X-MokoSuite-Timestamp: ' . $timestamp;
}
$endpoint = $baseUrl . '/api/index.php/v1/mokosuitehq/heartbeat';
$json = json_encode($payload, JSON_UNESCAPED_SLASHES);
try
{
$http = \Joomla\CMS\Http\HttpFactory::getHttp(
new \Joomla\Registry\Registry(['follow_location' => true, 'transport.curl' => ['certpath' => false]]),
['curl', 'stream']
);
$headerMap = [];
foreach ($headers as $h)
{
[$key, $val] = explode(': ', $h, 2);
$headerMap[$key] = $val;
}
$response = $http->post($endpoint, $json, $headerMap, 15);
$code = $response->code;
$body = json_decode($response->body, true);
if ($code >= 200 && $code < 300)
{
$app->enqueueMessage(
'MokoSuiteClientHQ heartbeat: ' . ($body['status'] ?? 'ok'),
'message'
);
}
else
{
Log::add(
\sprintf('Monitor heartbeat HTTP %d: %s', $code, $body['error'] ?? 'Unknown'),
Log::WARNING,
'mokosuiteclient'
);
$app->enqueueMessage(
'MokoSuiteClientHQ heartbeat failed (HTTP ' . $code . ')',
'warning'
);
}
}
catch (\Throwable $e)
{
Log::add('Monitor heartbeat failed: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient');
}
}
/**
* RSA-sign the request message.
*
* @param string $domain Site domain.
* @param int $timestamp Unix timestamp.
* @param string $token Health API token.
*
* @return string|null Base64-encoded signature, or null if signing fails.
*/
private function signRequest(string $domain, int $timestamp, string $token): ?string
{
$signingKeyB64 = $this->params->get('signing_key', '');
// Fall back to manifest XML default if not yet saved in params
if (empty($signingKeyB64))
{
$manifestFile = JPATH_PLUGINS . '/system/mokosuiteclient_monitor/mokosuiteclient_monitor.xml';
if (is_file($manifestFile))
{
$xml = simplexml_load_file($manifestFile);
if ($xml)
{
foreach ($xml->xpath('//field[@name="signing_key"]') as $field)
{
$signingKeyB64 = (string) $field['default'];
break;
}
}
}
}
if (empty($signingKeyB64))
{
return null;
}
$privateKeyPem = base64_decode($signingKeyB64);
if (empty($privateKeyPem))
{
return null;
}
$message = $domain . '|' . $timestamp . '|' . $token;
$privateKey = openssl_pkey_get_private($privateKeyPem);
if ($privateKey === false)
{
return null;
}
$signature = '';
if (openssl_sign($message, $signature, $privateKey, OPENSSL_ALGO_SHA256))
{
return base64_encode($signature);
}
return null;
}
/**
* Fetch health data from the local site's health endpoint.
*/
private function fetchLocalHealth(string $siteUrl, string $healthToken): ?array
{
try
{
$http = \Joomla\CMS\Http\HttpFactory::getHttp(
new \Joomla\Registry\Registry(['follow_location' => true, 'transport.curl' => ['certpath' => false]]),
['curl', 'stream']
);
$response = $http->get(
$siteUrl . '/?mokosuiteclient=health',
['Authorization' => 'Bearer ' . $healthToken, 'Accept' => 'application/json'],
10
);
if ($response->code !== 200 || empty($response->body))
{
return null;
}
return json_decode($response->body, true) ?: null;
}
catch (\Throwable $e)
{
return null;
}
}
/**
* Get the installed MokoSuiteClient package version.
*/
private function getMokoSuiteClientVersion(): string
{
try
{
$extension = new \Joomla\CMS\Table\Extension(Factory::getDbo());
$extension->load(['element' => 'pkg_mokosuiteclient', 'type' => 'package']);
$manifest = json_decode($extension->manifest_cache ?? '{}');
return $manifest->version ?? '';
}
catch (\Throwable $e)
{
return '';
}
}
}
@@ -8,7 +8,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>PLG_SYSTEM_MOKOSUITECLIENT_OFFLINE_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteClientOffline</namespace>
@@ -8,7 +8,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>PLG_SYSTEM_MOKOSUITECLIENT_TENANT_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteClientTenant</namespace>
@@ -1,8 +0,0 @@
PLG_TASK_MOKOSUITECLIENT_TICKETS="Task - MokoSuiteClient Ticket Automation"
PLG_TASK_MOKOSUITECLIENT_TICKETS_DESC="Runs scheduled helpdesk automation rules."
PLG_TASK_MOKOSUITECLIENT_TICKETS_AUTOMATION_TITLE="MokoSuiteClient: Ticket Automation"
PLG_TASK_MOKOSUITECLIENT_TICKETS_AUTOMATION_DESC="Runs time-based automation rules against open tickets (auto-close, SLA escalation, etc.)."
PLG_TASK_MOKOSUITECLIENT_TICKETS_IMAP_POLL_TITLE="MokoSuiteClient: IMAP Email Polling"
PLG_TASK_MOKOSUITECLIENT_TICKETS_IMAP_POLL_DESC="Polls an IMAP inbox for new emails and creates tickets or replies from unread messages."
PLG_TASK_MOKOSUITECLIENT_TICKETS_AUTOCLOSE_TITLE="MokoSuiteClient: Auto-Close Resolved Tickets"
PLG_TASK_MOKOSUITECLIENT_TICKETS_AUTOCLOSE_DESC="Automatically closes tickets that have been in resolved status longer than the configured number of days."
@@ -1,2 +0,0 @@
PLG_TASK_MOKOSUITECLIENT_TICKETS="Task - MokoSuiteClient Ticket Automation"
PLG_TASK_MOKOSUITECLIENT_TICKETS_DESC="Runs scheduled helpdesk automation rules — auto-close, SLA escalation, and time-based actions."
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="task" method="upgrade">
<name>Task - MokoSuiteClient Ticket Automation</name>
<element>mokosuiteclient_tickets</element>
<author>Moko Consulting</author>
<creationDate>2026-06-02</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.44.00</version>
<description>Runs scheduled helpdesk automation rules — auto-close resolved tickets, SLA breach escalation, and time-based actions.</description>
<namespace path="src">Moko\Plugin\Task\MokoSuiteClientTickets</namespace>
<files>
<folder>src</folder>
<folder>services</folder>
<folder>language</folder>
</files>
<languages folder="language">
<language tag="en-GB">en-GB/plg_task_mokosuiteclient_tickets.ini</language>
<language tag="en-GB">en-GB/plg_task_mokosuiteclient_tickets.sys.ini</language>
</languages>
</extension>
@@ -1,27 +0,0 @@
<?php
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\Task\MokoSuiteClientTickets\Extension\TicketAutomation;
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 TicketAutomation($dispatcher, (array) PluginHelper::getPlugin('task', 'mokosuiteclient_tickets'));
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};
@@ -1,313 +0,0 @@
<?php
/**
* @package MokoSuiteClient
* @subpackage plg_task_mokosuiteclient_tickets
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Plugin\Task\MokoSuiteClientTickets\Extension;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent;
use Joomla\Component\Scheduler\Administrator\Task\Status;
use Joomla\Component\Scheduler\Administrator\Traits\TaskPluginTrait;
use Joomla\Event\SubscriberInterface;
use Moko\Component\MokoSuiteClient\Administrator\Model\TicketsModel;
use Moko\Component\MokoSuiteClient\Administrator\Service\AttachmentService;
use Moko\Component\MokoSuiteClient\Administrator\Service\NotificationService;
class TicketAutomation extends CMSPlugin implements SubscriberInterface
{
use TaskPluginTrait;
protected const TASKS_MAP = [
'mokosuiteclient.ticket.automation' => [
'langConstPrefix' => 'PLG_TASK_MOKOSUITECLIENT_TICKETS_AUTOMATION',
'method' => 'runAutomation',
],
'mokosuiteclient.ticket.imap_poll' => [
'langConstPrefix' => 'PLG_TASK_MOKOSUITECLIENT_TICKETS_IMAP_POLL',
'method' => 'runImapPoll',
],
'mokosuiteclient.ticket.autoclose' => [
'langConstPrefix' => 'PLG_TASK_MOKOSUITECLIENT_TICKETS_AUTOCLOSE',
'method' => 'runAutoClose',
],
];
protected $autoloadLanguage = true;
public static function getSubscribedEvents(): array
{
return [
'onTaskOptionsList' => 'advertiseRoutines',
'onExecuteTask' => 'standardRoutineHandler',
'onContentPrepareForm' => 'enhanceTaskItemForm',
];
}
/**
* Run all scheduled automation rules against open tickets.
*/
private function runAutomation(ExecuteTaskEvent $event): int
{
try
{
$model = new TicketsModel();
$results = $model->runScheduledAutomation();
$this->logTask(
\sprintf('Ticket automation: evaluated %d tickets, acted on %d', $results['evaluated'], $results['acted'])
);
return Status::OK;
}
catch (\Throwable $e)
{
$this->logTask('Ticket automation failed: ' . $e->getMessage(), 'error');
return Status::KNOCKOUT;
}
}
/**
* Poll IMAP inbox and create tickets from unread emails (#136).
*/
private function runImapPoll(ExecuteTaskEvent $event): int
{
$config = $this->getComponentConfig();
$host = $config['imap_host'] ?? '';
$port = (int) ($config['imap_port'] ?? 993);
$user = $config['imap_user'] ?? '';
$pass = $config['imap_password'] ?? '';
$ssl = ($config['imap_ssl'] ?? '1') === '1';
$folder = $config['imap_folder'] ?? 'INBOX';
$processed = $config['imap_processed_folder'] ?? 'INBOX.Processed';
$defaultCat = (int) ($config['default_category'] ?? 0) ?: null;
if (empty($host) || empty($user) || empty($pass))
{
$this->logTask('IMAP not configured — skipping', 'warning');
return Status::OK;
}
if (!function_exists('imap_open'))
{
$this->logTask('php-imap extension not available', 'error');
return Status::KNOCKOUT;
}
$mailbox = '{' . $host . ':' . $port . '/imap' . ($ssl ? '/ssl' : '') . '/novalidate-cert}' . $folder;
$mbox = @imap_open($mailbox, $user, $pass);
if (!$mbox)
{
$this->logTask('IMAP connection failed: ' . imap_last_error(), 'error');
return Status::KNOCKOUT;
}
$db = Factory::getDbo();
$created = 0;
$replied = 0;
$emails = imap_search($mbox, 'UNSEEN');
if ($emails === false)
{
imap_close($mbox);
$this->logTask('No new emails');
return Status::OK;
}
foreach ($emails as $msgNum)
{
try
{
$header = imap_headerinfo($mbox, $msgNum);
$subject = isset($header->subject) ? imap_utf8($header->subject) : '(no subject)';
$fromAddr = $header->from[0]->mailbox . '@' . $header->from[0]->host;
$body = $this->getImapBody($mbox, $msgNum);
// Match sender to Joomla user
$userId = $this->findUserByEmail($fromAddr);
// Check if this is a reply (subject contains [#123])
$ticketId = 0;
if (preg_match('/\[#(\d+)\]/', $subject, $m))
{
$ticketId = (int) $m[1];
}
if ($ticketId > 0)
{
// Add as reply to existing ticket
$reply = (object) [
'ticket_id' => $ticketId,
'user_id' => $userId,
'body' => $body,
'is_internal' => 0,
'created' => Factory::getDate()->toSql(),
];
$db->insertObject('#__mokosuiteclient_ticket_replies', $reply, 'id');
$replied++;
// Notify
$db->setQuery($db->getQuery(true)->select('*')->from('#__mokosuiteclient_tickets')->where('id = ' . $ticketId));
$ticket = $db->loadObject();
if ($ticket) {
NotificationService::notify('ticket_replied', $ticket, ['reply_body' => $body]);
}
}
else
{
// Create new ticket
$ticket = (object) [
'subject' => $subject,
'body' => $body,
'status' => 'open',
'priority' => 'normal',
'category_id' => $defaultCat,
'created_by' => $userId,
'created' => Factory::getDate()->toSql(),
];
$db->insertObject('#__mokosuiteclient_tickets', $ticket, 'id');
$created++;
NotificationService::notify('ticket_created', $ticket);
}
// Mark as seen / move to processed folder
imap_setflag_full($mbox, (string) $msgNum, '\\Seen');
if ($processed && $processed !== $folder)
{
@imap_mail_move($mbox, (string) $msgNum, $processed);
}
}
catch (\Throwable $e)
{
Log::add('IMAP message processing error: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient');
}
}
imap_expunge($mbox);
imap_close($mbox);
$this->logTask("IMAP poll: {$created} tickets created, {$replied} replies added");
return Status::OK;
}
/**
* Auto-close resolved tickets after configured days.
*/
private function runAutoClose(ExecuteTaskEvent $event): int
{
$config = $this->getComponentConfig();
$days = (int) ($config['autoclose_days'] ?? 7);
if ($days <= 0)
{
$this->logTask('Auto-close disabled (days = 0)');
return Status::OK;
}
$db = Factory::getDbo();
$cutoff = Factory::getDate('-' . $days . ' days')->toSql();
$db->setQuery(
"UPDATE {$db->quoteName('#__mokosuiteclient_tickets')}"
. " SET status = 'closed', closed = {$db->quote(Factory::getDate()->toSql())}"
. " WHERE status = 'resolved'"
. " AND resolved IS NOT NULL"
. " AND resolved < {$db->quote($cutoff)}"
);
$db->execute();
$closed = $db->getAffectedRows();
$this->logTask("Auto-close: {$closed} tickets closed (resolved > {$days} days ago)");
return Status::OK;
}
// ── Helpers ──────────────────────────────────────────────────
private function getComponentConfig(): array
{
try
{
$db = Factory::getDbo();
$db->setQuery(
$db->getQuery(true)
->select('params')
->from('#__extensions')
->where('element = ' . $db->quote('com_mokosuiteclient'))
->where('type = ' . $db->quote('component'))
);
return json_decode($db->loadResult() ?? '{}', true) ?: [];
}
catch (\Throwable $e)
{
Log::add('Failed to load component config: ' . $e->getMessage(), Log::ERROR, 'mokosuiteclient');
return [];
}
}
private function findUserByEmail(string $email): int
{
$db = Factory::getDbo();
$db->setQuery(
$db->getQuery(true)
->select('id')
->from('#__users')
->where('email = ' . $db->quote($email))
->setLimit(1)
);
return (int) $db->loadResult();
}
private function getImapBody($mbox, int $msgNum): string
{
$structure = imap_fetchstructure($mbox, $msgNum);
// Simple single-part message
if (empty($structure->parts))
{
$body = imap_fetchbody($mbox, $msgNum, '1');
if ($structure->encoding === 3) $body = base64_decode($body);
if ($structure->encoding === 4) $body = quoted_printable_decode($body);
return trim(strip_tags($body));
}
// Multipart — find text/plain or text/html
$textBody = '';
foreach ($structure->parts as $i => $part)
{
$partNum = (string) ($i + 1);
if ($part->type === 0) // text
{
$content = imap_fetchbody($mbox, $msgNum, $partNum);
if ($part->encoding === 3) $content = base64_decode($content);
if ($part->encoding === 4) $content = quoted_printable_decode($content);
$subtype = strtolower($part->subtype ?? '');
if ($subtype === 'plain' && empty($textBody))
{
$textBody = $content;
}
elseif ($subtype === 'html' && empty($textBody))
{
$textBody = strip_tags($content);
}
}
}
return trim($textBody);
}
}
@@ -12,7 +12,7 @@
<license>GNU General Public License version 3 or later; see LICENSE</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>PLG_TASK_MOKOSUITECLIENTDEMO_DESC</description>
<namespace path="src">Moko\Plugin\Task\MokoSuiteClientDemo</namespace>
@@ -10,7 +10,7 @@
* INGROUP: MokoSuiteClient
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteClient
* PATH: /src/packages/plg_system_mokosuiteclient/Service/DemoResetService.php
* VERSION: 02.44.00
* VERSION: 02.47.03
* BRIEF: Content-only snapshot/restore for demo site reset
*/
@@ -12,7 +12,7 @@
<license>GNU General Public License version 3 or later; see LICENSE</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>PLG_TASK_MOKOSUITECLIENTSYNC_DESC</description>
<namespace path="src">Moko\Plugin\Task\MokoSuiteClientSync</namespace>
@@ -10,7 +10,7 @@
* INGROUP: MokoSuiteClient
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteClient
* PATH: /src/packages/plg_system_mokosuiteclient/Service/ContentSyncReceiver.php
* VERSION: 02.44.00
* VERSION: 02.47.03
* BRIEF: Receiver-side content sync — applies incoming payload to local DB
*/
@@ -10,7 +10,7 @@
* INGROUP: MokoSuiteClient
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteClient
* PATH: /src/packages/plg_system_mokosuiteclient/Service/ContentSyncService.php
* VERSION: 02.44.00
* VERSION: 02.47.03
* BRIEF: Sender-side content sync — builds payload and pushes to remote sites
*/
@@ -7,7 +7,7 @@
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>02.44.00</version>
<version>02.47.03</version>
<description>Joomla Web Services API routes for MokoSuiteClient site management — health checks, cache, updates, backups, and site info.</description>
<namespace path="src">Moko\Plugin\WebServices\MokoSuiteClient</namespace>
<files>
+1 -3
View File
@@ -2,7 +2,7 @@
<extension type="package" method="upgrade">
<name>Package - MokoSuiteClient</name>
<packagename>mokosuiteclient</packagename>
<version>02.44.00</version>
<version>02.47.03</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -25,11 +25,9 @@
<file type="module" id="mod_mokosuiteclient_menu" client="administrator">mod_mokosuiteclient_menu.zip</file>
<file type="module" id="mod_mokosuiteclient_cache" client="administrator">mod_mokosuiteclient_cache.zip</file>
<file type="module" id="mod_mokosuiteclient_categories" client="administrator">mod_mokosuiteclient_categories.zip</file>
<file type="plugin" id="plg_system_mokosuiteclient_backup" group="system">plg_system_mokosuiteclient_backup.zip</file>
<file type="plugin" id="plg_webservices_mokosuiteclient" group="webservices">plg_webservices_mokosuiteclient.zip</file>
<file type="plugin" id="plg_task_mokosuiteclientdemo" group="task">plg_task_mokosuiteclientdemo.zip</file>
<file type="plugin" id="plg_task_mokosuiteclientsync" group="task">plg_task_mokosuiteclientsync.zip</file>
<file type="plugin" id="plg_task_mokosuiteclient_tickets" group="task">plg_task_mokosuiteclient_tickets.zip</file>
</files>
<updateservers>
+284 -28
View File
@@ -46,6 +46,9 @@ class Pkg_MokosuiteclientInstallerScript
{
$this->saveDownloadKey();
// Joomla's package installer INSERTs extension rows without element first.
// MySQL strict mode requires a default. Set DEFAULT '' so the INSERT succeeds,
// then postflight cleans up the empty-element rows and stale files.
try
{
$db = Factory::getDbo();
@@ -55,12 +58,16 @@ class Pkg_MokosuiteclientInstallerScript
}
catch (\Throwable $e)
{
// Non-fatal — column may already have a default
// Non-fatal
}
}
/** @var \Joomla\CMS\Installer\InstallerAdapter|null */
private $installerParent = null;
public function postflight($type, $parent)
{
$this->installerParent = $parent;
// Migrate MokoWaaS database tables to MokoSuiteClient naming
$this->migrateWaasTables();
@@ -82,11 +89,9 @@ class Pkg_MokosuiteclientInstallerScript
$this->enablePlugin('system', 'mokosuiteclient_devtools');
$this->enablePlugin('system', 'mokosuiteclient_offline');
$this->enablePlugin('system', 'mokosuiteclient_dbip');
$this->enablePlugin('system', 'mokosuiteclient_backup');
$this->enablePlugin('webservices', 'mokosuiteclient');
$this->enablePlugin('task', 'mokosuiteclientdemo');
$this->enablePlugin('task', 'mokosuiteclientsync');
$this->enablePlugin('task', 'mokosuiteclient_tickets');
// Migrate params from core plugin to feature plugins (one-time)
$this->migrateFeatureParams();
@@ -109,21 +114,15 @@ class Pkg_MokosuiteclientInstallerScript
// Set up MokoSuiteClient guided tours and unpublish Joomla defaults
$this->setupGuidedTours();
// Clean up orphaned empty-element rows and stale files from old DEFAULT '' bug
$this->cleanupEmptyElements();
// Mark MokoSuiteClient extensions as protected (prevents disable/uninstall at framework level)
$this->protectExtensions();
// Migrate all Moko update server URLs to new format
$this->migrateUpdateServerUrls();
// Clean up stale/duplicate update sites
$this->cleanupStaleUpdateSites();
// Restore download key saved in preflight
$this->restoreDownloadKey();
// Fix orphaned update records (extension_id=0)
$this->fixUpdateRecords();
// Trigger heartbeat registration
$this->sendHeartbeat();
@@ -464,6 +463,16 @@ class Pkg_MokosuiteclientInstallerScript
{
try
{
// Only enable if the plugin files actually exist on disk
$pluginDir = JPATH_PLUGINS . '/' . $group . '/' . $element;
if (!is_dir($pluginDir))
{
Log::add('Skipping enable for ' . $group . '/' . $element . ' — files not installed', Log::DEBUG, 'mokosuiteclient');
return;
}
$db = Factory::getDbo();
$query = $db->getQuery(true)
->update($db->quoteName('#__extensions'))
@@ -497,7 +506,22 @@ class Pkg_MokosuiteclientInstallerScript
if ($db->getAffectedRows() > 0)
{
Log::add('Fixed empty element for plugin ' . $group . '/' . $element, Log::NOTICE, 'mokosuiteclient');
return;
}
// Verify no row exists before inserting (prevent duplicates)
$db->setQuery(
$db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__extensions'))
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('folder') . ' = ' . $db->quote($group))
->where($db->quoteName('element') . ' = ' . $db->quote($element))
);
// No row exists — reinstallBrokenPlugins() will handle it via
// Joomla's Installer which properly registers the namespace
}
catch (\Throwable $e)
{
@@ -515,6 +539,246 @@ class Pkg_MokosuiteclientInstallerScript
*
* @since 02.03.10
*/
private function cleanupEmptyElements(): void
{
try
{
$db = Factory::getDbo();
// 1. Delete orphaned MokoSuiteClient extension rows with empty element
$db->setQuery("DELETE FROM " . $db->quoteName('#__extensions')
. " WHERE " . $db->quoteName('element') . " = ''"
. " AND " . $db->quoteName('type') . " = " . $db->quote('plugin')
. " AND " . $db->quoteName('name') . " LIKE " . $db->quote('%MokoSuiteClient%'));
$db->execute();
$deleted = $db->getAffectedRows();
// Delete rows where element is the display name (spaces)
$db->setQuery(
$db->getQuery(true)
->delete($db->quoteName('#__extensions'))
->where($db->quoteName('element') . ' LIKE ' . $db->quote('% %'))
->where($db->quoteName('element') . ' LIKE ' . $db->quote('%mokosuiteclient%'))
);
$db->execute();
$deleted += $db->getAffectedRows();
if ($deleted > 0)
{
Log::add("Deleted {$deleted} orphaned plugin row(s)", Log::INFO, 'mokosuiteclient');
}
// Deduplicate: keep only the lowest extension_id per element+folder
$db->setQuery(
"DELETE e1 FROM " . $db->quoteName('#__extensions') . " e1"
. " INNER JOIN " . $db->quoteName('#__extensions') . " e2"
. " ON e1.element = e2.element AND e1.folder = e2.folder AND e1.type = e2.type"
. " AND e1.extension_id > e2.extension_id"
. " WHERE e1.element LIKE 'mokosuiteclient%' AND e1.type = 'plugin'"
);
$db->execute();
$deduped = $db->getAffectedRows();
if ($deduped > 0)
{
Log::add("Removed {$deduped} duplicate extension row(s)", Log::INFO, 'mokosuiteclient');
}
// 2. Clean up stale plugin files that leaked to group roots
$groupDirs = [JPATH_PLUGINS . '/system', JPATH_PLUGINS . '/task', JPATH_PLUGINS . '/webservices'];
foreach ($groupDirs as $groupDir)
{
foreach (['services', 'src', 'language'] as $dir)
{
$path = $groupDir . '/' . $dir;
if (is_dir($path))
{
$this->rmdirRecursive($path);
}
}
// Remove stale manifest XMLs at group root
foreach (glob($groupDir . '/mokosuiteclient*.xml') ?: [] as $staleXml)
{
@unlink($staleXml);
}
// Remove dirs with spaces (Joomla uses display name as dir)
foreach (glob($groupDir . '/*mokosuiteclient*', GLOB_ONLYDIR) ?: [] as $badDir)
{
if (strpos(basename($badDir), ' ') !== false)
{
$this->rmdirRecursive($badDir);
}
}
}
// 3. Reinstall plugins that are missing their directory
$this->reinstallBrokenPlugins();
}
catch (\Throwable $e)
{
Log::add('Empty element cleanup error: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient');
}
}
/**
* Reinstall plugins whose files are missing from disk.
*
* Uses the sub-extension zip files from the package source directory
* (still available during postflight) to reinstall any plugin that
* doesn't have its directory on disk.
*/
private function reinstallBrokenPlugins(): void
{
if (!$this->installerParent)
{
return;
}
try
{
$parentInstaller = $this->installerParent->getParent();
$sourceDir = $parentInstaller->getPath('source');
if (empty($sourceDir) || !is_dir($sourceDir . '/packages'))
{
return;
}
// Plugins that should exist on disk
$expected = [
'system' => ['mokosuiteclient_offline', 'mokosuiteclient_firewall', 'mokosuiteclient_tenant', 'mokosuiteclient_devtools', 'mokosuiteclient_dbip'],
'task' => [],
];
foreach ($expected as $group => $elements)
{
foreach ($elements as $element)
{
$pluginDir = JPATH_PLUGINS . '/' . $group . '/' . $element;
$zipName = 'plg_' . $group . '_' . $element . '.zip';
$zipPath = $sourceDir . '/packages/' . $zipName;
if (!is_file($zipPath))
{
continue;
}
// Extract to plugin dir
$zip = new \ZipArchive();
if ($zip->open($zipPath) !== true)
{
continue;
}
if (is_dir($pluginDir))
{
$this->rmdirRecursive($pluginDir);
}
@mkdir($pluginDir, 0755, true);
$zip->extractTo($pluginDir);
$zip->close();
}
}
// Step 2: Create DB records for plugins that have files but no record
$db = Factory::getDbo();
foreach ($expected as $group => $elements)
{
foreach ($elements as $element)
{
$pluginDir = JPATH_PLUGINS . '/' . $group . '/' . $element;
$manifestFile = $pluginDir . '/' . $element . '.xml';
if (!is_file($manifestFile))
{
continue;
}
// Check if record exists
$db->setQuery(
$db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__extensions'))
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('folder') . ' = ' . $db->quote($group))
->where($db->quoteName('element') . ' = ' . $db->quote($element))
);
if ((int) $db->loadResult() > 0)
{
continue;
}
// Parse manifest for name and namespace
$xml = @simplexml_load_file($manifestFile);
$name = $xml ? (string) ($xml->name ?? '') : '';
$namespace = $xml ? (string) ($xml->namespace ?? '') : '';
$version = $xml ? (string) ($xml->version ?? '') : '';
if (empty($name))
{
$name = 'plg_' . $group . '_' . $element;
}
// Build manifest cache
$cache = json_encode([
'name' => $name,
'type' => 'plugin',
'version' => $version,
'group' => $group,
]);
// INSERT with all required fields including namespace
$columns = 'name, type, element, folder, client_id, enabled, access, protected, locked, params, manifest_cache, custom_data, state, ordering';
$values = $db->quote($name) . ', '
. $db->quote('plugin') . ', '
. $db->quote($element) . ', '
. $db->quote($group) . ', '
. '0, 1, 1, 0, 0, '
. $db->quote('{}') . ', '
. $db->quote($cache) . ', '
. $db->quote('') . ', 0, 0';
$sql = "INSERT INTO " . $db->quoteName('#__extensions')
. " ({$columns}) VALUES ({$values})";
$db->setQuery($sql)->execute();
$newId = $db->insertid();
// Set namespace if column exists
if (!empty($namespace))
{
try
{
$db->setQuery(
$db->getQuery(true)
->update($db->quoteName('#__extensions'))
->set($db->quoteName('namespace') . ' = ' . $db->quote($namespace))
->where($db->quoteName('extension_id') . ' = ' . (int) $newId)
)->execute();
}
catch (\Throwable $e)
{
// namespace column may not exist in this Joomla version
}
}
Log::add("Created extension record for {$group}/{$element} (ID {$newId})", Log::INFO, 'mokosuiteclient');
}
}
}
catch (\Throwable $e)
{
Log::add('Plugin reinstall error: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient');
}
}
private function protectExtensions(): void
{
try
@@ -534,8 +798,6 @@ class Pkg_MokosuiteclientInstallerScript
$db->quote('mod_mokosuiteclient_cpanel'),
$db->quote('mokosuiteclientdemo'),
$db->quote('mokosuiteclientsync'),
$db->quote('mokosuiteclient_tickets'),
$db->quote('mokosuiteclient_backup'),
$db->quote('mokoonyx'),
];
@@ -547,8 +809,6 @@ class Pkg_MokosuiteclientInstallerScript
$db->setQuery($query);
$db->execute();
// Ensure update server stays enabled
$this->enableUpdateServer();
}
catch (\Throwable $e)
{
@@ -970,19 +1230,24 @@ class Pkg_MokosuiteclientInstallerScript
if ($error)
{
Log::add('Heartbeat connection failed: ' . $error, Log::WARNING, 'mokosuiteclient');
Factory::getApplication()->enqueueMessage('MokoSuiteHQ heartbeat failed: ' . $error, 'warning');
}
elseif ($code >= 200 && $code < 300)
{
Factory::getApplication()->enqueueMessage('MokoSuiteClientHQ heartbeat: site registered', 'message');
Factory::getApplication()->enqueueMessage('MokoSuiteHQ heartbeat: site registered successfully.', 'message');
}
else
{
$body = json_decode($response, true);
$msg = $body['error'] ?? $body['message'] ?? ('HTTP ' . $code);
Log::add(sprintf('Heartbeat HTTP %d: %s', $code, $response), Log::WARNING, 'mokosuiteclient');
Factory::getApplication()->enqueueMessage('MokoSuiteHQ heartbeat failed: ' . $msg, 'warning');
}
}
catch (\Throwable $e)
{
Log::add('Heartbeat failed: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient');
Factory::getApplication()->enqueueMessage('MokoSuiteHQ heartbeat failed: ' . $e->getMessage(), 'warning');
}
}
@@ -1254,17 +1519,6 @@ class Pkg_MokosuiteclientInstallerScript
['title' => 'IP Blocklist', 'desc' => 'Block specific IP addresses, CIDR ranges, or wildcard patterns. The auto-ban feature automatically blocks IPs that trigger too many WAF alerts.', 'target' => '', 'type' => 0],
],
],
[
'uid' => 'mokosuiteclient-helpdesk',
'title' => 'MokoSuiteClient Helpdesk',
'desc' => 'Learn how to manage support tickets, categories, and automation rules.',
'url' => 'administrator/index.php?option=com_mokosuiteclient&view=tickets',
'steps' => [
['title' => 'Ticket List', 'desc' => 'View all support tickets with status, priority, SLA tracking, and assignment. Filter by status or search to find specific tickets.', 'target' => '', 'type' => 0],
['title' => 'Create a Ticket', 'desc' => 'Click the New button to create a support ticket. Assign a category, priority, and optional SLA deadline.', 'target' => '', 'type' => 0],
['title' => 'Ticket Automation', 'desc' => 'Set up automation rules that trigger on ticket events (new ticket, status change) or Joomla events (user login, registration). Automate assignment, notifications, and status changes.', 'target' => '', 'type' => 0],
],
],
[
'uid' => 'mokosuiteclient-extensions',
'title' => 'Moko Extensions Manager',
@@ -1350,6 +1604,8 @@ class Pkg_MokosuiteclientInstallerScript
*/
private function setupSupportMenuItem(): void
{
// Tickets moved to MokoSuiteCRM — no frontend support menu needed
return;
try
{
$db = Factory::getDbo();