From aa36a01a7ff5c0d37bfa4256066b71527ab38e26 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 2 Jun 2026 21:58:43 -0500 Subject: [PATCH 01/81] chore: bump version to 01.01.00-dev for next development cycle Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/manifest.xml | 2 +- src/packages/com_mokobackup/mokobackup.xml | 2 +- src/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- src/packages/plg_system_mokobackup/mokobackup.xml | 2 +- src/packages/plg_task_mokobackup/mokobackup.xml | 2 +- src/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- src/pkg_mokobackup.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 41e81ee..5ef8494 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.00.00 + 01.01.00-dev GNU General Public License v3 diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 131212e..4711c60 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.00.00 + 01.01.00-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/src/packages/plg_quickicon_mokobackup/mokobackup.xml index b2ff32a..5a55e79 100644 --- a/src/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/src/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.00.00 + 01.01.00-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/src/packages/plg_system_mokobackup/mokobackup.xml index f13d733..1f1b1eb 100644 --- a/src/packages/plg_system_mokobackup/mokobackup.xml +++ b/src/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.00.00 + 01.01.00-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml index 92b274e..ab99004 100644 --- a/src/packages/plg_task_mokobackup/mokobackup.xml +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.00.00 + 01.01.00-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/src/packages/plg_webservices_mokobackup/mokobackup.xml index 5d4f103..e54426a 100644 --- a/src/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/src/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.00.00 + 01.01.00-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index 334846c..2de7a14 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.00.00 + 01.01.00-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 507b2c9448501486262ae5b3e10744fff0d0457e Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Wed, 3 Jun 2026 03:11:26 +0000 Subject: [PATCH 02/81] chore: sync .mokogitea/workflows/repo-health.yml from moko-platform [skip ci] --- .mokogitea/workflows/repo-health.yml | 1635 +++++++++++++------------- 1 file changed, 818 insertions(+), 817 deletions(-) diff --git a/.mokogitea/workflows/repo-health.yml b/.mokogitea/workflows/repo-health.yml index e3653eb..d7743f0 100644 --- a/.mokogitea/workflows/repo-health.yml +++ b/.mokogitea/workflows/repo-health.yml @@ -1,817 +1,818 @@ -# ============================================================================ -# Copyright (C) 2025 Moko Consulting -# -# This file is part of a Moko Consulting project. -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Validation -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform -# PATH: /templates/workflows/joomla/repo_health.yml.template -# VERSION: 09.23.00 -# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts. -# ============================================================================ - -name: "Generic: Repo Health" - -defaults: - run: - shell: bash - -on: - workflow_dispatch: - inputs: - profile: - description: 'Validation profile: all, release, scripts, or repo' - required: true - default: all - type: choice - options: - - all - - release - - scripts - - repo - pull_request: - push: - -permissions: - contents: read - -env: - # Release policy - Repository Variables Only - RELEASE_REQUIRED_REPO_VARS: RS_FTP_PATH_SUFFIX - RELEASE_OPTIONAL_REPO_VARS: DEV_FTP_SUFFIX - - # Scripts governance policy - SCRIPTS_REQUIRED_DIRS: - SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate - - # Repo health policy - REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogitea/workflows/ - REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/ - REPO_DISALLOWED_DIRS: - REPO_DISALLOWED_FILES: TODO.md,todo.md - - # Extended checks toggles - EXTENDED_CHECKS: "true" - - # File / directory variables - DOCS_INDEX: docs/docs-index.md - SCRIPT_DIR: scripts - WORKFLOWS_DIR: .mokogitea/workflows - SHELLCHECK_PATTERN: '*.sh' - SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml' - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - access_check: - name: Access control - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - contents: read - - outputs: - allowed: ${{ steps.perm.outputs.allowed }} - permission: ${{ steps.perm.outputs.permission }} - - steps: - - name: Check actor permission (admin only) - id: perm - env: - TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }} - REPO: ${{ github.repository }} - ACTOR: ${{ github.actor }} - run: | - set -euo pipefail - ALLOWED=false - PERMISSION=unknown - METHOD="" - - # Hardcoded authorized users — always allowed - case "$ACTOR" in - jmiller|gitea-actions[bot]) - ALLOWED=true - PERMISSION=admin - METHOD="hardcoded allowlist" - ;; - *) - # Detect platform and check permissions via API - API_BASE="${GITHUB_API_URL:-${GITEA_API_URL:-https://api.github.com}}" - RESP=$(curl -sf -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/repos/${REPO}/collaborators/${ACTOR}/permission" 2>/dev/null || echo '{}') - PERMISSION=$(echo "$RESP" | grep -oP '"permission"\s*:\s*"\K[^"]+' || echo "unknown") - if [ "$PERMISSION" = "admin" ] || [ "$PERMISSION" = "maintain" ] || [ "$PERMISSION" = "owner" ]; then - ALLOWED=true - fi - METHOD="collaborator API" - ;; - esac - - echo "permission=${PERMISSION}" >> "$GITHUB_OUTPUT" - echo "allowed=${ALLOWED}" >> "$GITHUB_OUTPUT" - - { - echo "## Access Authorization" - echo "" - echo "| Field | Value |" - echo "|-------|-------|" - echo "| **Actor** | \`${ACTOR}\` |" - echo "| **Repository** | \`${REPO}\` |" - echo "| **Permission** | \`${PERMISSION}\` |" - echo "| **Method** | ${METHOD} |" - echo "| **Authorized** | ${ALLOWED} |" - echo "" - if [ "$ALLOWED" = "true" ]; then - echo "${ACTOR} authorized (${METHOD})" - else - echo "${ACTOR} is NOT authorized. Requires admin or maintain role." - fi - } >> "${GITHUB_STEP_SUMMARY}" - - - name: Deny execution when not permitted - if: ${{ steps.perm.outputs.allowed != 'true' }} - run: | - set -euo pipefail - printf '%s\n' 'ERROR: Access denied. Admin permission required.' >> "${GITHUB_STEP_SUMMARY}" - exit 1 - - release_config: - name: Release configuration - needs: access_check - if: ${{ needs.access_check.outputs.allowed == 'true' }} - runs-on: ubuntu-latest - timeout-minutes: 20 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - - - name: Guardrails release vars - env: - PROFILE_RAW: ${{ github.event.inputs.profile }} - RS_FTP_PATH_SUFFIX: ${{ vars.RS_FTP_PATH_SUFFIX }} - DEV_FTP_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = 'scripts' ] || [ "${profile}" = 'repo' ]; then - { - printf '%s\n' '### Release configuration (Repository Variables)' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' 'Status: SKIPPED' - printf '%s\n' 'Reason: profile excludes release validation' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - IFS=',' read -r -a required <<< "${RELEASE_REQUIRED_REPO_VARS}" - IFS=',' read -r -a optional <<< "${RELEASE_OPTIONAL_REPO_VARS}" - - missing=() - missing_optional=() - - for k in "${required[@]}"; do - v="${!k:-}" - [ -z "${v}" ] && missing+=("${k}") - done - - for k in "${optional[@]}"; do - v="${!k:-}" - [ -z "${v}" ] && missing_optional+=("${k}") - done - - { - printf '%s\n' '### Release configuration (Repository Variables)' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' '| Variable | Status |' - printf '%s\n' '|---|---|' - printf '%s\n' "| RS_FTP_PATH_SUFFIX | ${RS_FTP_PATH_SUFFIX:-NOT SET} |" - printf '%s\n' "| DEV_FTP_SUFFIX | ${DEV_FTP_SUFFIX:-NOT SET} |" - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${#missing_optional[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing optional repository variables' - for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - if [ "${#missing[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing required repository variables' - for m in "${missing[@]}"; do printf '%s\n' "- ${m}"; done - printf '%s\n' 'ERROR: Guardrails failed. Missing required repository variables.' - } >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - { - printf '%s\n' '### Repository variables validation result' - printf '%s\n' 'Status: OK' - printf '%s\n' 'All required repository variables present.' - printf '%s\n' '' - printf '%s\n' '**Note**: Organization secrets (RS_FTP_HOST, RS_FTP_USER, etc.) are validated at deployment time, not in repository health checks.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - scripts_governance: - name: Scripts governance - needs: access_check - if: ${{ needs.access_check.outputs.allowed == 'true' }} - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - - - name: Scripts folder checks - env: - PROFILE_RAW: ${{ github.event.inputs.profile }} - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = 'release' ] || [ "${profile}" = 'repo' ]; then - { - printf '%s\n' '### Scripts governance' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' 'Status: SKIPPED' - printf '%s\n' 'Reason: profile excludes scripts governance' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - if [ ! -d "${SCRIPT_DIR}" ]; then - { - printf '%s\n' '### Scripts governance' - printf '%s\n' 'Status: OK (advisory)' - printf '%s\n' 'scripts/ directory not present. No scripts governance enforced.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - if [ -n "${SCRIPTS_REQUIRED_DIRS:-}" ]; then IFS=',' read -r -a required_dirs <<< "${SCRIPTS_REQUIRED_DIRS}"; else required_dirs=(); fi - IFS=',' read -r -a allowed_dirs <<< "${SCRIPTS_ALLOWED_DIRS}" - - missing_dirs=() - unapproved_dirs=() - - for d in "${required_dirs[@]}"; do - req="${d%/}" - [ ! -d "${req}" ] && missing_dirs+=("${req}/") - done - - while IFS= read -r d; do - allowed=false - for a in "${allowed_dirs[@]}"; do - a_norm="${a%/}" - [ "${d%/}" = "${a_norm}" ] && allowed=true - done - [ "${allowed}" = false ] && unapproved_dirs+=("${d%/}/") - done < <(find "${SCRIPT_DIR}" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sed 's#^\./##') - - { - printf '%s\n' '### Scripts governance' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' '| Area | Status | Notes |' - printf '%s\n' '|---|---|---|' - - if [ "${#missing_dirs[@]}" -gt 0 ]; then - printf '%s\n' '| Required directories | Warning | Missing required subfolders |' - else - printf '%s\n' '| Required directories | OK | All required subfolders present |' - fi - - if [ "${#unapproved_dirs[@]}" -gt 0 ]; then - printf '%s\n' '| Directory policy | Warning | Unapproved directories detected |' - else - printf '%s\n' '| Directory policy | OK | No unapproved directories |' - fi - - printf '%s\n' '| Enforcement mode | Advisory | scripts folder is optional |' - printf '\n' - - if [ "${#missing_dirs[@]}" -gt 0 ]; then - printf '%s\n' 'Missing required script directories:' - for m in "${missing_dirs[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - else - printf '%s\n' 'Missing required script directories: none.' - printf '\n' - fi - - if [ "${#unapproved_dirs[@]}" -gt 0 ]; then - printf '%s\n' 'Unapproved script directories detected:' - for m in "${unapproved_dirs[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - else - printf '%s\n' 'Unapproved script directories detected: none.' - printf '\n' - fi - - printf '%s\n' 'Scripts governance completed in advisory mode.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - repo_health: - name: Repository health - needs: access_check - if: ${{ needs.access_check.outputs.allowed == 'true' }} - runs-on: ubuntu-latest - timeout-minutes: 20 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - - - name: Repository health checks - env: - PROFILE_RAW: ${{ github.event.inputs.profile }} - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = 'release' ] || [ "${profile}" = 'scripts' ]; then - { - printf '%s\n' '### Repository health' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' 'Status: SKIPPED' - printf '%s\n' 'Reason: profile excludes repository health' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}" - IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}" - if [ -n "${REPO_DISALLOWED_DIRS:-}" ]; then IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"; else disallowed_dirs=(); fi - IFS=',' read -r -a disallowed_files <<< "${REPO_DISALLOWED_FILES:-}" - - missing_required=() - missing_optional=() - - # Source directory: src/ or htdocs/ (either is valid for extension repos) - SOURCE_DIR="" - if [ -d "src" ]; then - SOURCE_DIR="src" - elif [ -d "htdocs" ]; then - SOURCE_DIR="htdocs" - elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then - # Platform/tooling repos don't need src/ - SOURCE_DIR="" - else - missing_required+=("src/ or htdocs/ (source directory required)") - fi - - for item in "${required_artifacts[@]}"; do - if printf '%s' "${item}" | grep -q '/$'; then - d="${item%/}" - [ ! -d "${d}" ] && missing_required+=("${item}") - else - [ ! -f "${item}" ] && missing_required+=("${item}") - fi - done - - for f in "${optional_files[@]}"; do - if printf '%s' "${f}" | grep -q '/$'; then - d="${f%/}" - [ ! -d "${d}" ] && missing_optional+=("${f}") - else - [ ! -f "${f}" ] && missing_optional+=("${f}") - fi - done - - for d in "${disallowed_dirs[@]}"; do - d_norm="${d%/}" - [ -d "${d_norm}" ] && missing_required+=("${d_norm}/ (disallowed)") - done - - for f in "${disallowed_files[@]}"; do - [ -f "${f}" ] && missing_required+=("${f} (disallowed)") - done - - git fetch origin --prune - - dev_paths=() - dev_branches=() - - while IFS= read -r b; do - name="${b#origin/}" - if [ "${name}" = 'dev' ]; then - dev_branches+=("${name}") - else - dev_paths+=("${name}") - fi - done < <(git branch -r --list 'origin/dev*' | sed 's/^ *//') - - if [ "${#dev_paths[@]}" -eq 0 ] && [ "${#dev_branches[@]}" -eq 0 ]; then - missing_required+=("dev or dev/* branch") - fi - - content_warnings=() - - if [ -f 'CHANGELOG.md' ] && ! grep -Eq '^# Changelog' CHANGELOG.md; then - content_warnings+=("CHANGELOG.md missing '# Changelog' header") - fi - - if [ -f 'CHANGELOG.md' ] && grep -Eq '^[# ]*Unreleased' CHANGELOG.md; then - content_warnings+=("CHANGELOG.md contains Unreleased section (review release readiness)") - fi - - if [ -f 'LICENSE' ] && ! grep -qiE 'GNU GENERAL PUBLIC LICENSE|GPL' LICENSE; then - content_warnings+=("LICENSE does not look like a GPL text") - fi - - if [ -f 'README.md' ] && ! grep -qiE 'moko|Moko' README.md; then - content_warnings+=("README.md missing expected brand keyword") - fi - - export PROFILE_RAW="${profile}" - export MISSING_REQUIRED="$(printf '%s\n' "${missing_required[@]:-}")" - export MISSING_OPTIONAL="$(printf '%s\n' "${missing_optional[@]:-}")" - export CONTENT_WARNINGS="$(printf '%s\n' "${content_warnings[@]:-}")" - - report_json=$(printf '{"profile":"%s","missing_required":%d,"missing_optional":%d,"content_warnings":%d}' "$profile" "${#missing_required[@]}" "${#missing_optional[@]}" "${#content_warnings[@]}") - - { - printf '%s\n' '### Repository health' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' '| Metric | Value |' - printf '%s\n' '|---|---|' - printf '%s\n' "| Missing required | ${#missing_required[@]} |" - printf '%s\n' "| Missing optional | ${#missing_optional[@]} |" - printf '%s\n' "| Content warnings | ${#content_warnings[@]} |" - printf '\n' - - printf '%s\n' '### Guardrails report (JSON)' - printf '%s\n' '```json' - printf '%s\n' "${report_json}" - printf '%s\n' '```' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${#missing_required[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing required repo artifacts' - for m in "${missing_required[@]}"; do printf '%s\n' "- ${m}"; done - printf '%s\n' 'ERROR: Guardrails failed. Missing required repository artifacts.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#missing_optional[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing optional repo artifacts' - for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - if [ "${#content_warnings[@]}" -gt 0 ]; then - { - printf '%s\n' '### Repo content warnings' - for m in "${content_warnings[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - # -- Joomla-specific checks -- - joomla_findings=() - - MANIFEST="$(find . -maxdepth 2 -name '*.xml' -exec grep -l '/dev/null | head -1 || true)" - if [ -z "${MANIFEST}" ]; then - joomla_findings+=("Joomla XML manifest not found (no *.xml with tag)") - else - if ! grep -qP '' "${MANIFEST}"; then - joomla_findings+=("XML manifest: tag missing") - fi - if ! grep -qP 'type="(component|module|plugin|library|package|template|language)"' "${MANIFEST}"; then - joomla_findings+=("XML manifest: type attribute missing or invalid") - fi - if ! grep -qP '' "${MANIFEST}"; then - joomla_findings+=("XML manifest: tag missing") - fi - if ! grep -qP '' "${MANIFEST}"; then - joomla_findings+=("XML manifest: tag missing") - fi - if ! grep -qP ' missing (required for Joomla 5+)") - fi - fi - - INI_COUNT="$(find . -name '*.ini' -type f 2>/dev/null | wc -l)" - if [ "${INI_COUNT}" -eq 0 ]; then - joomla_findings+=("No .ini language files found") - fi - - if [ ! -f 'updates.xml' ]; then - joomla_findings+=("updates.xml missing in root (required for Joomla update server)") - fi - - if [ -n "${SOURCE_DIR}" ]; then - INDEX_DIRS=("${SOURCE_DIR}" "${SOURCE_DIR}/admin" "${SOURCE_DIR}/site") - for dir in "${INDEX_DIRS[@]}"; do - if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then - joomla_findings+=("${dir}/index.html missing (directory listing protection)") - fi - done - fi - - if [ "${#joomla_findings[@]}" -gt 0 ]; then - { - printf '%s\n' '### Joomla extension checks' - printf '%s\n' '| Check | Status |' - printf '%s\n' '|---|---|' - for f in "${joomla_findings[@]}"; do - printf '%s\n' "| ${f} | Warning |" - done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - else - { - printf '%s\n' '### Joomla extension checks' - printf '%s\n' 'All Joomla-specific checks passed.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - extended_enabled="${EXTENDED_CHECKS:-true}" - extended_findings=() - - if [ "${extended_enabled}" = 'true' ]; then - if [ -f '.github/CODEOWNERS' ] || [ -f 'CODEOWNERS' ] || [ -f 'docs/CODEOWNERS' ]; then - : - else - extended_findings+=("CODEOWNERS not found (.github/CODEOWNERS preferred)") - fi - - if ls "${WORKFLOWS_DIR}"/*.yml >/dev/null 2>&1 || ls "${WORKFLOWS_DIR}"/*.yaml >/dev/null 2>&1; then - bad_refs="$(grep -RIn --include='*.yml' --include='*.yaml' -E '^[[:space:]]*uses:[[:space:]]*[^#]+@(main|master)\b' "${WORKFLOWS_DIR}" 2>/dev/null || true)" - if [ -n "${bad_refs}" ]; then - extended_findings+=("Workflows reference actions @main/@master (pin versions): see log excerpt") - { - printf '%s\n' '### Workflow pinning advisory' - printf '%s\n' 'Found uses: entries pinned to main/master:' - printf '%s\n' '```' - printf '%s\n' "${bad_refs}" - printf '%s\n' '```' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - fi - - if [ -f "${DOCS_INDEX}" ]; then - missing_links="" - while IFS= read -r docline; do - for link in $(echo "$docline" | grep -oE '\]\([^)]+\)' | sed 's/\](//' | sed 's/)$//' || true); do - case "$link" in http://*|https://*|"#"*|mailto:*) continue ;; esac - linkpath="${link%%#*}" - linkpath="${linkpath%%\?*}" - [ -z "$linkpath" ] && continue - if [ "${linkpath:0:1}" = "/" ]; then - testpath="${linkpath#/}" - else - testpath="$(dirname "${DOCS_INDEX}")/${linkpath}" - fi - [ ! -e "$testpath" ] && missing_links="${missing_links}${testpath} " - done - done < "${DOCS_INDEX}" - if [ -n "${missing_links}" ]; then - extended_findings+=("docs/docs-index.md contains broken relative links") - { - printf '%s\n' '### Docs index link integrity' - printf '%s\n' 'Broken relative links:' - for bl in ${missing_links}; do - printf '%s\n' "- ${bl}" - done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - fi - - if [ -d "${SCRIPT_DIR}" ]; then - if ! command -v shellcheck >/dev/null 2>&1; then - sudo apt-get update -qq - sudo apt-get install -y shellcheck >/dev/null - fi - - sc_out='' - while IFS= read -r shf; do - [ -z "${shf}" ] && continue - out_one="$(shellcheck -S warning -x "${shf}" 2>/dev/null || true)" - if [ -n "${out_one}" ]; then - sc_out="${sc_out}${out_one}\n" - fi - done < <(find "${SCRIPT_DIR}" -type f -name "${SHELLCHECK_PATTERN}" 2>/dev/null | sort) - - if [ -n "${sc_out}" ]; then - extended_findings+=("ShellCheck warnings detected (advisory)") - sc_head="$(printf '%s' "${sc_out}" | head -n 200)" - { - printf '%s\n' '### ShellCheck (advisory)' - printf '%s\n' '```' - printf '%s\n' "${sc_head}" - printf '%s\n' '```' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - fi - - spdx_missing=() - IFS=',' read -r -a spdx_globs <<< "${SPDX_FILE_GLOBS}" - spdx_args=() - for g in "${spdx_globs[@]}"; do spdx_args+=("${g}"); done - - while IFS= read -r f; do - [ -z "${f}" ] && continue - if ! head -n 40 "${f}" | grep -q 'SPDX-License-Identifier:'; then - spdx_missing+=("${f}") - fi - done < <(git ls-files "${spdx_args[@]}" 2>/dev/null || true) - - if [ "${#spdx_missing[@]}" -gt 0 ]; then - extended_findings+=("SPDX header missing in some tracked files (advisory)") - { - printf '%s\n' '### SPDX header advisory' - printf '%s\n' 'Files missing SPDX-License-Identifier (first 40 lines scan):' - for f in "${spdx_missing[@]}"; do printf '%s\n' "- ${f}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - stale_cutoff_days=180 - stale_branches="$(git for-each-ref --format='%(refname:short) %(committerdate:unix)' refs/remotes/origin 2>/dev/null | awk -v now="$(date +%s)" -v days="${stale_cutoff_days}" '{if (now-$2 > days*86400) print $1}' | head -50)" - if [ -n "${stale_branches}" ]; then - extended_findings+=("Stale remote branches detected (advisory)") - { - printf '%s\n' '### Git hygiene advisory' - printf '%s\n' "Branches with last commit older than ${stale_cutoff_days} days (sample up to 50):" - while IFS= read -r b; do [ -n "${b}" ] && printf '%s\n' "- ${b}"; done <<< "${stale_branches}" - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - fi - - { - printf '%s\n' '### Guardrails coverage matrix' - printf '%s\n' '| Domain | Status | Notes |' - printf '%s\n' '|---|---|---|' - printf '%s\n' '| Access control | OK | Admin-only execution gate |' - printf '%s\n' '| Release variables | OK | Repository variables validation |' - printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |' - printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |' - printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |' - if [ "${extended_enabled}" = 'true' ]; then - if [ "${#extended_findings[@]}" -gt 0 ]; then - printf '%s\n' '| Extended checks | Warning | See extended findings below |' - else - printf '%s\n' '| Extended checks | OK | No findings |' - fi - else - printf '%s\n' '| Extended checks | SKIPPED | EXTENDED_CHECKS disabled |' - fi - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${extended_enabled}" = 'true' ] && [ "${#extended_findings[@]}" -gt 0 ]; then - { - printf '%s\n' '### Extended findings (advisory)' - for f in "${extended_findings[@]}"; do printf '%s\n' "- ${f}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - printf '%s\n' 'Repository health guardrails passed.' >> "${GITHUB_STEP_SUMMARY}" - - - site-health: - name: Site Health - runs-on: ubuntu-latest - if: github.event_name == 'workflow_dispatch' - steps: - - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.3' - - - name: Uptime check - if: env.URLS != '' - run: | - echo "$URLS" > /tmp/urls.txt - php monitoring/uptime-probe.php --urls /tmp/urls.txt --timeout 15 || echo "::warning::Some sites are down" - rm -f /tmp/urls.txt - env: - URLS: ${{ vars.MONITORED_URLS }} - - - name: SSL certificate check - if: env.DOMAINS != '' - run: | - echo "$DOMAINS" > /tmp/domains.txt - php monitoring/ssl-check.php --domains /tmp/domains.txt --warn-days 30 || echo "::warning::SSL certificates expiring soon" - rm -f /tmp/domains.txt - env: - DOMAINS: ${{ vars.MONITORED_DOMAINS }} - - - name: Summary - if: always() - run: | - echo "### Site Health" >> $GITHUB_STEP_SUMMARY - echo "Uptime and SSL checks completed." >> $GITHUB_STEP_SUMMARY - - # ═══════════════════════════════════════════════════════════════════════ - # Issue Reporter — file issues for failed gates - # ═══════════════════════════════════════════════════════════════════════ - report-issues: - name: "Report Issues" - runs-on: ubuntu-latest - needs: [access_check, release_config, scripts_governance, repo_health] - if: >- - always() && - (needs.release_config.result == 'failure' || - needs.scripts_governance.result == 'failure' || - needs.repo_health.result == 'failure') - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - sparse-checkout: automation/ci-issue-reporter.sh - sparse-checkout-cone-mode: false - - - name: "File issues for failed gates" - env: - GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - run: | - chmod +x automation/ci-issue-reporter.sh - REPORTER="./automation/ci-issue-reporter.sh" - WF="Repo Health" - - report_gate() { - local gate="$1" result="$2" details="$3" - if [ "$result" = "failure" ]; then - "$REPORTER" --gate "$gate" --details "$details" --workflow "$WF" --severity error - fi - } - - report_gate "Release Configuration" \ - "${{ needs.release_config.result }}" \ - "Required repository variables are missing (RS_FTP_PATH_SUFFIX). Check repository settings." - - report_gate "Scripts Governance" \ - "${{ needs.scripts_governance.result }}" \ - "Scripts directory policy violations detected. Review required and allowed directories." - - report_gate "Repository Health" \ - "${{ needs.repo_health.result }}" \ - "Repository health checks failed — missing required artifacts, disallowed files, or content warnings. Check the CI run summary." - +# ============================================================================ +# Copyright (C) 2025 Moko Consulting +# +# This file is part of a Moko Consulting project. +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Validation +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform +# PATH: /templates/workflows/joomla/repo_health.yml.template +# VERSION: 09.23.00 +# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts. +# ============================================================================ + +name: "Generic: Repo Health" + +defaults: + run: + shell: bash + +on: + workflow_dispatch: + inputs: + profile: + description: 'Validation profile: all, release, scripts, or repo' + required: true + default: all + type: choice + options: + - all + - release + - scripts + - repo + pull_request: + push: + +permissions: + contents: read + +env: + # Release policy - Repository Variables Only + # RS_FTP_PATH_SUFFIX removed — MokoGitea handles all releases now + RELEASE_REQUIRED_REPO_VARS: + RELEASE_OPTIONAL_REPO_VARS: DEV_FTP_SUFFIX + + # Scripts governance policy + SCRIPTS_REQUIRED_DIRS: + SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate + + # Repo health policy + REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogitea/workflows/ + REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/ + REPO_DISALLOWED_DIRS: + REPO_DISALLOWED_FILES: TODO.md,todo.md + + # Extended checks toggles + EXTENDED_CHECKS: "true" + + # File / directory variables + DOCS_INDEX: docs/docs-index.md + SCRIPT_DIR: scripts + WORKFLOWS_DIR: .mokogitea/workflows + SHELLCHECK_PATTERN: '*.sh' + SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml' + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + access_check: + name: Access control + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + + outputs: + allowed: ${{ steps.perm.outputs.allowed }} + permission: ${{ steps.perm.outputs.permission }} + + steps: + - name: Check actor permission (admin only) + id: perm + env: + TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }} + REPO: ${{ github.repository }} + ACTOR: ${{ github.actor }} + run: | + set -euo pipefail + ALLOWED=false + PERMISSION=unknown + METHOD="" + + # Hardcoded authorized users — always allowed + case "$ACTOR" in + jmiller|gitea-actions[bot]) + ALLOWED=true + PERMISSION=admin + METHOD="hardcoded allowlist" + ;; + *) + # Detect platform and check permissions via API + API_BASE="${GITHUB_API_URL:-${GITEA_API_URL:-https://api.github.com}}" + RESP=$(curl -sf -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/repos/${REPO}/collaborators/${ACTOR}/permission" 2>/dev/null || echo '{}') + PERMISSION=$(echo "$RESP" | grep -oP '"permission"\s*:\s*"\K[^"]+' || echo "unknown") + if [ "$PERMISSION" = "admin" ] || [ "$PERMISSION" = "maintain" ] || [ "$PERMISSION" = "owner" ]; then + ALLOWED=true + fi + METHOD="collaborator API" + ;; + esac + + echo "permission=${PERMISSION}" >> "$GITHUB_OUTPUT" + echo "allowed=${ALLOWED}" >> "$GITHUB_OUTPUT" + + { + echo "## Access Authorization" + echo "" + echo "| Field | Value |" + echo "|-------|-------|" + echo "| **Actor** | \`${ACTOR}\` |" + echo "| **Repository** | \`${REPO}\` |" + echo "| **Permission** | \`${PERMISSION}\` |" + echo "| **Method** | ${METHOD} |" + echo "| **Authorized** | ${ALLOWED} |" + echo "" + if [ "$ALLOWED" = "true" ]; then + echo "${ACTOR} authorized (${METHOD})" + else + echo "${ACTOR} is NOT authorized. Requires admin or maintain role." + fi + } >> "${GITHUB_STEP_SUMMARY}" + + - name: Deny execution when not permitted + if: ${{ steps.perm.outputs.allowed != 'true' }} + run: | + set -euo pipefail + printf '%s\n' 'ERROR: Access denied. Admin permission required.' >> "${GITHUB_STEP_SUMMARY}" + exit 1 + + release_config: + name: Release configuration + needs: access_check + if: ${{ needs.access_check.outputs.allowed == 'true' }} + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Guardrails release vars + env: + PROFILE_RAW: ${{ github.event.inputs.profile }} + RS_FTP_PATH_SUFFIX: ${{ vars.RS_FTP_PATH_SUFFIX }} + DEV_FTP_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} + run: | + set -euo pipefail + + profile="${PROFILE_RAW:-all}" + case "${profile}" in + all|release|scripts|repo) ;; + *) + printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" + exit 1 + ;; + esac + + if [ "${profile}" = 'scripts' ] || [ "${profile}" = 'repo' ]; then + { + printf '%s\n' '### Release configuration (Repository Variables)' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' 'Status: SKIPPED' + printf '%s\n' 'Reason: profile excludes release validation' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + IFS=',' read -r -a required <<< "${RELEASE_REQUIRED_REPO_VARS}" + IFS=',' read -r -a optional <<< "${RELEASE_OPTIONAL_REPO_VARS}" + + missing=() + missing_optional=() + + for k in "${required[@]}"; do + v="${!k:-}" + [ -z "${v}" ] && missing+=("${k}") + done + + for k in "${optional[@]}"; do + v="${!k:-}" + [ -z "${v}" ] && missing_optional+=("${k}") + done + + { + printf '%s\n' '### Release configuration (Repository Variables)' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' '| Variable | Status |' + printf '%s\n' '|---|---|' + printf '%s\n' "| RS_FTP_PATH_SUFFIX | ${RS_FTP_PATH_SUFFIX:-NOT SET} |" + printf '%s\n' "| DEV_FTP_SUFFIX | ${DEV_FTP_SUFFIX:-NOT SET} |" + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + if [ "${#missing_optional[@]}" -gt 0 ]; then + { + printf '%s\n' '### Missing optional repository variables' + for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + if [ "${#missing[@]}" -gt 0 ]; then + { + printf '%s\n' '### Missing required repository variables' + for m in "${missing[@]}"; do printf '%s\n' "- ${m}"; done + printf '%s\n' 'ERROR: Guardrails failed. Missing required repository variables.' + } >> "${GITHUB_STEP_SUMMARY}" + exit 1 + fi + + { + printf '%s\n' '### Repository variables validation result' + printf '%s\n' 'Status: OK' + printf '%s\n' 'All required repository variables present.' + printf '%s\n' '' + printf '%s\n' '**Note**: Organization secrets (RS_FTP_HOST, RS_FTP_USER, etc.) are validated at deployment time, not in repository health checks.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + scripts_governance: + name: Scripts governance + needs: access_check + if: ${{ needs.access_check.outputs.allowed == 'true' }} + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Scripts folder checks + env: + PROFILE_RAW: ${{ github.event.inputs.profile }} + run: | + set -euo pipefail + + profile="${PROFILE_RAW:-all}" + case "${profile}" in + all|release|scripts|repo) ;; + *) + printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" + exit 1 + ;; + esac + + if [ "${profile}" = 'release' ] || [ "${profile}" = 'repo' ]; then + { + printf '%s\n' '### Scripts governance' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' 'Status: SKIPPED' + printf '%s\n' 'Reason: profile excludes scripts governance' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + if [ ! -d "${SCRIPT_DIR}" ]; then + { + printf '%s\n' '### Scripts governance' + printf '%s\n' 'Status: OK (advisory)' + printf '%s\n' 'scripts/ directory not present. No scripts governance enforced.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + if [ -n "${SCRIPTS_REQUIRED_DIRS:-}" ]; then IFS=',' read -r -a required_dirs <<< "${SCRIPTS_REQUIRED_DIRS}"; else required_dirs=(); fi + IFS=',' read -r -a allowed_dirs <<< "${SCRIPTS_ALLOWED_DIRS}" + + missing_dirs=() + unapproved_dirs=() + + for d in "${required_dirs[@]}"; do + req="${d%/}" + [ ! -d "${req}" ] && missing_dirs+=("${req}/") + done + + while IFS= read -r d; do + allowed=false + for a in "${allowed_dirs[@]}"; do + a_norm="${a%/}" + [ "${d%/}" = "${a_norm}" ] && allowed=true + done + [ "${allowed}" = false ] && unapproved_dirs+=("${d%/}/") + done < <(find "${SCRIPT_DIR}" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sed 's#^\./##') + + { + printf '%s\n' '### Scripts governance' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' '| Area | Status | Notes |' + printf '%s\n' '|---|---|---|' + + if [ "${#missing_dirs[@]}" -gt 0 ]; then + printf '%s\n' '| Required directories | Warning | Missing required subfolders |' + else + printf '%s\n' '| Required directories | OK | All required subfolders present |' + fi + + if [ "${#unapproved_dirs[@]}" -gt 0 ]; then + printf '%s\n' '| Directory policy | Warning | Unapproved directories detected |' + else + printf '%s\n' '| Directory policy | OK | No unapproved directories |' + fi + + printf '%s\n' '| Enforcement mode | Advisory | scripts folder is optional |' + printf '\n' + + if [ "${#missing_dirs[@]}" -gt 0 ]; then + printf '%s\n' 'Missing required script directories:' + for m in "${missing_dirs[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + else + printf '%s\n' 'Missing required script directories: none.' + printf '\n' + fi + + if [ "${#unapproved_dirs[@]}" -gt 0 ]; then + printf '%s\n' 'Unapproved script directories detected:' + for m in "${unapproved_dirs[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + else + printf '%s\n' 'Unapproved script directories detected: none.' + printf '\n' + fi + + printf '%s\n' 'Scripts governance completed in advisory mode.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + repo_health: + name: Repository health + needs: access_check + if: ${{ needs.access_check.outputs.allowed == 'true' }} + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Repository health checks + env: + PROFILE_RAW: ${{ github.event.inputs.profile }} + run: | + set -euo pipefail + + profile="${PROFILE_RAW:-all}" + case "${profile}" in + all|release|scripts|repo) ;; + *) + printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" + exit 1 + ;; + esac + + if [ "${profile}" = 'release' ] || [ "${profile}" = 'scripts' ]; then + { + printf '%s\n' '### Repository health' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' 'Status: SKIPPED' + printf '%s\n' 'Reason: profile excludes repository health' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}" + IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}" + if [ -n "${REPO_DISALLOWED_DIRS:-}" ]; then IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"; else disallowed_dirs=(); fi + IFS=',' read -r -a disallowed_files <<< "${REPO_DISALLOWED_FILES:-}" + + missing_required=() + missing_optional=() + + # Source directory: src/ or htdocs/ (either is valid for extension repos) + SOURCE_DIR="" + if [ -d "src" ]; then + SOURCE_DIR="src" + elif [ -d "htdocs" ]; then + SOURCE_DIR="htdocs" + elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then + # Platform/tooling repos don't need src/ + SOURCE_DIR="" + else + missing_required+=("src/ or htdocs/ (source directory required)") + fi + + for item in "${required_artifacts[@]}"; do + if printf '%s' "${item}" | grep -q '/$'; then + d="${item%/}" + [ ! -d "${d}" ] && missing_required+=("${item}") + else + [ ! -f "${item}" ] && missing_required+=("${item}") + fi + done + + for f in "${optional_files[@]}"; do + if printf '%s' "${f}" | grep -q '/$'; then + d="${f%/}" + [ ! -d "${d}" ] && missing_optional+=("${f}") + else + [ ! -f "${f}" ] && missing_optional+=("${f}") + fi + done + + for d in "${disallowed_dirs[@]}"; do + d_norm="${d%/}" + [ -d "${d_norm}" ] && missing_required+=("${d_norm}/ (disallowed)") + done + + for f in "${disallowed_files[@]}"; do + [ -f "${f}" ] && missing_required+=("${f} (disallowed)") + done + + git fetch origin --prune + + dev_paths=() + dev_branches=() + + while IFS= read -r b; do + name="${b#origin/}" + if [ "${name}" = 'dev' ]; then + dev_branches+=("${name}") + else + dev_paths+=("${name}") + fi + done < <(git branch -r --list 'origin/dev*' | sed 's/^ *//') + + if [ "${#dev_paths[@]}" -eq 0 ] && [ "${#dev_branches[@]}" -eq 0 ]; then + missing_required+=("dev or dev/* branch") + fi + + content_warnings=() + + if [ -f 'CHANGELOG.md' ] && ! grep -Eq '^# Changelog' CHANGELOG.md; then + content_warnings+=("CHANGELOG.md missing '# Changelog' header") + fi + + if [ -f 'CHANGELOG.md' ] && grep -Eq '^[# ]*Unreleased' CHANGELOG.md; then + content_warnings+=("CHANGELOG.md contains Unreleased section (review release readiness)") + fi + + if [ -f 'LICENSE' ] && ! grep -qiE 'GNU GENERAL PUBLIC LICENSE|GPL' LICENSE; then + content_warnings+=("LICENSE does not look like a GPL text") + fi + + if [ -f 'README.md' ] && ! grep -qiE 'moko|Moko' README.md; then + content_warnings+=("README.md missing expected brand keyword") + fi + + export PROFILE_RAW="${profile}" + export MISSING_REQUIRED="$(printf '%s\n' "${missing_required[@]:-}")" + export MISSING_OPTIONAL="$(printf '%s\n' "${missing_optional[@]:-}")" + export CONTENT_WARNINGS="$(printf '%s\n' "${content_warnings[@]:-}")" + + report_json=$(printf '{"profile":"%s","missing_required":%d,"missing_optional":%d,"content_warnings":%d}' "$profile" "${#missing_required[@]}" "${#missing_optional[@]}" "${#content_warnings[@]}") + + { + printf '%s\n' '### Repository health' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' '| Metric | Value |' + printf '%s\n' '|---|---|' + printf '%s\n' "| Missing required | ${#missing_required[@]} |" + printf '%s\n' "| Missing optional | ${#missing_optional[@]} |" + printf '%s\n' "| Content warnings | ${#content_warnings[@]} |" + printf '\n' + + printf '%s\n' '### Guardrails report (JSON)' + printf '%s\n' '```json' + printf '%s\n' "${report_json}" + printf '%s\n' '```' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + if [ "${#missing_required[@]}" -gt 0 ]; then + { + printf '%s\n' '### Missing required repo artifacts' + for m in "${missing_required[@]}"; do printf '%s\n' "- ${m}"; done + printf '%s\n' 'ERROR: Guardrails failed. Missing required repository artifacts.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 1 + fi + + if [ "${#missing_optional[@]}" -gt 0 ]; then + { + printf '%s\n' '### Missing optional repo artifacts' + for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + if [ "${#content_warnings[@]}" -gt 0 ]; then + { + printf '%s\n' '### Repo content warnings' + for m in "${content_warnings[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + # -- Joomla-specific checks -- + joomla_findings=() + + MANIFEST="$(find . -maxdepth 2 -name '*.xml' -exec grep -l '/dev/null | head -1 || true)" + if [ -z "${MANIFEST}" ]; then + joomla_findings+=("Joomla XML manifest not found (no *.xml with tag)") + else + if ! grep -qP '' "${MANIFEST}"; then + joomla_findings+=("XML manifest: tag missing") + fi + if ! grep -qP 'type="(component|module|plugin|library|package|template|language)"' "${MANIFEST}"; then + joomla_findings+=("XML manifest: type attribute missing or invalid") + fi + if ! grep -qP '' "${MANIFEST}"; then + joomla_findings+=("XML manifest: tag missing") + fi + if ! grep -qP '' "${MANIFEST}"; then + joomla_findings+=("XML manifest: tag missing") + fi + if ! grep -qP ' missing (required for Joomla 5+)") + fi + fi + + INI_COUNT="$(find . -name '*.ini' -type f 2>/dev/null | wc -l)" + if [ "${INI_COUNT}" -eq 0 ]; then + joomla_findings+=("No .ini language files found") + fi + + if [ ! -f 'updates.xml' ]; then + joomla_findings+=("updates.xml missing in root (required for Joomla update server)") + fi + + if [ -n "${SOURCE_DIR}" ]; then + INDEX_DIRS=("${SOURCE_DIR}" "${SOURCE_DIR}/admin" "${SOURCE_DIR}/site") + for dir in "${INDEX_DIRS[@]}"; do + if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then + joomla_findings+=("${dir}/index.html missing (directory listing protection)") + fi + done + fi + + if [ "${#joomla_findings[@]}" -gt 0 ]; then + { + printf '%s\n' '### Joomla extension checks' + printf '%s\n' '| Check | Status |' + printf '%s\n' '|---|---|' + for f in "${joomla_findings[@]}"; do + printf '%s\n' "| ${f} | Warning |" + done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + else + { + printf '%s\n' '### Joomla extension checks' + printf '%s\n' 'All Joomla-specific checks passed.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + extended_enabled="${EXTENDED_CHECKS:-true}" + extended_findings=() + + if [ "${extended_enabled}" = 'true' ]; then + if [ -f '.github/CODEOWNERS' ] || [ -f 'CODEOWNERS' ] || [ -f 'docs/CODEOWNERS' ]; then + : + else + extended_findings+=("CODEOWNERS not found (.github/CODEOWNERS preferred)") + fi + + if ls "${WORKFLOWS_DIR}"/*.yml >/dev/null 2>&1 || ls "${WORKFLOWS_DIR}"/*.yaml >/dev/null 2>&1; then + bad_refs="$(grep -RIn --include='*.yml' --include='*.yaml' -E '^[[:space:]]*uses:[[:space:]]*[^#]+@(main|master)\b' "${WORKFLOWS_DIR}" 2>/dev/null || true)" + if [ -n "${bad_refs}" ]; then + extended_findings+=("Workflows reference actions @main/@master (pin versions): see log excerpt") + { + printf '%s\n' '### Workflow pinning advisory' + printf '%s\n' 'Found uses: entries pinned to main/master:' + printf '%s\n' '```' + printf '%s\n' "${bad_refs}" + printf '%s\n' '```' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + fi + + if [ -f "${DOCS_INDEX}" ]; then + missing_links="" + while IFS= read -r docline; do + for link in $(echo "$docline" | grep -oE '\]\([^)]+\)' | sed 's/\](//' | sed 's/)$//' || true); do + case "$link" in http://*|https://*|"#"*|mailto:*) continue ;; esac + linkpath="${link%%#*}" + linkpath="${linkpath%%\?*}" + [ -z "$linkpath" ] && continue + if [ "${linkpath:0:1}" = "/" ]; then + testpath="${linkpath#/}" + else + testpath="$(dirname "${DOCS_INDEX}")/${linkpath}" + fi + [ ! -e "$testpath" ] && missing_links="${missing_links}${testpath} " + done + done < "${DOCS_INDEX}" + if [ -n "${missing_links}" ]; then + extended_findings+=("docs/docs-index.md contains broken relative links") + { + printf '%s\n' '### Docs index link integrity' + printf '%s\n' 'Broken relative links:' + for bl in ${missing_links}; do + printf '%s\n' "- ${bl}" + done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + fi + + if [ -d "${SCRIPT_DIR}" ]; then + if ! command -v shellcheck >/dev/null 2>&1; then + sudo apt-get update -qq + sudo apt-get install -y shellcheck >/dev/null + fi + + sc_out='' + while IFS= read -r shf; do + [ -z "${shf}" ] && continue + out_one="$(shellcheck -S warning -x "${shf}" 2>/dev/null || true)" + if [ -n "${out_one}" ]; then + sc_out="${sc_out}${out_one}\n" + fi + done < <(find "${SCRIPT_DIR}" -type f -name "${SHELLCHECK_PATTERN}" 2>/dev/null | sort) + + if [ -n "${sc_out}" ]; then + extended_findings+=("ShellCheck warnings detected (advisory)") + sc_head="$(printf '%s' "${sc_out}" | head -n 200)" + { + printf '%s\n' '### ShellCheck (advisory)' + printf '%s\n' '```' + printf '%s\n' "${sc_head}" + printf '%s\n' '```' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + fi + + spdx_missing=() + IFS=',' read -r -a spdx_globs <<< "${SPDX_FILE_GLOBS}" + spdx_args=() + for g in "${spdx_globs[@]}"; do spdx_args+=("${g}"); done + + while IFS= read -r f; do + [ -z "${f}" ] && continue + if ! head -n 40 "${f}" | grep -q 'SPDX-License-Identifier:'; then + spdx_missing+=("${f}") + fi + done < <(git ls-files "${spdx_args[@]}" 2>/dev/null || true) + + if [ "${#spdx_missing[@]}" -gt 0 ]; then + extended_findings+=("SPDX header missing in some tracked files (advisory)") + { + printf '%s\n' '### SPDX header advisory' + printf '%s\n' 'Files missing SPDX-License-Identifier (first 40 lines scan):' + for f in "${spdx_missing[@]}"; do printf '%s\n' "- ${f}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + stale_cutoff_days=180 + stale_branches="$(git for-each-ref --format='%(refname:short) %(committerdate:unix)' refs/remotes/origin 2>/dev/null | awk -v now="$(date +%s)" -v days="${stale_cutoff_days}" '{if (now-$2 > days*86400) print $1}' | head -50)" + if [ -n "${stale_branches}" ]; then + extended_findings+=("Stale remote branches detected (advisory)") + { + printf '%s\n' '### Git hygiene advisory' + printf '%s\n' "Branches with last commit older than ${stale_cutoff_days} days (sample up to 50):" + while IFS= read -r b; do [ -n "${b}" ] && printf '%s\n' "- ${b}"; done <<< "${stale_branches}" + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + fi + + { + printf '%s\n' '### Guardrails coverage matrix' + printf '%s\n' '| Domain | Status | Notes |' + printf '%s\n' '|---|---|---|' + printf '%s\n' '| Access control | OK | Admin-only execution gate |' + printf '%s\n' '| Release variables | OK | Repository variables validation |' + printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |' + printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |' + printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |' + if [ "${extended_enabled}" = 'true' ]; then + if [ "${#extended_findings[@]}" -gt 0 ]; then + printf '%s\n' '| Extended checks | Warning | See extended findings below |' + else + printf '%s\n' '| Extended checks | OK | No findings |' + fi + else + printf '%s\n' '| Extended checks | SKIPPED | EXTENDED_CHECKS disabled |' + fi + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + if [ "${extended_enabled}" = 'true' ] && [ "${#extended_findings[@]}" -gt 0 ]; then + { + printf '%s\n' '### Extended findings (advisory)' + for f in "${extended_findings[@]}"; do printf '%s\n' "- ${f}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + printf '%s\n' 'Repository health guardrails passed.' >> "${GITHUB_STEP_SUMMARY}" + + + site-health: + name: Site Health + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + + - name: Uptime check + if: env.URLS != '' + run: | + echo "$URLS" > /tmp/urls.txt + php monitoring/uptime-probe.php --urls /tmp/urls.txt --timeout 15 || echo "::warning::Some sites are down" + rm -f /tmp/urls.txt + env: + URLS: ${{ vars.MONITORED_URLS }} + + - name: SSL certificate check + if: env.DOMAINS != '' + run: | + echo "$DOMAINS" > /tmp/domains.txt + php monitoring/ssl-check.php --domains /tmp/domains.txt --warn-days 30 || echo "::warning::SSL certificates expiring soon" + rm -f /tmp/domains.txt + env: + DOMAINS: ${{ vars.MONITORED_DOMAINS }} + + - name: Summary + if: always() + run: | + echo "### Site Health" >> $GITHUB_STEP_SUMMARY + echo "Uptime and SSL checks completed." >> $GITHUB_STEP_SUMMARY + + # ═══════════════════════════════════════════════════════════════════════ + # Issue Reporter — file issues for failed gates + # ═══════════════════════════════════════════════════════════════════════ + report-issues: + name: "Report Issues" + runs-on: ubuntu-latest + needs: [access_check, release_config, scripts_governance, repo_health] + if: >- + always() && + (needs.release_config.result == 'failure' || + needs.scripts_governance.result == 'failure' || + needs.repo_health.result == 'failure') + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: automation/ci-issue-reporter.sh + sparse-checkout-cone-mode: false + + - name: "File issues for failed gates" + env: + GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + run: | + chmod +x automation/ci-issue-reporter.sh + REPORTER="./automation/ci-issue-reporter.sh" + WF="Repo Health" + + report_gate() { + local gate="$1" result="$2" details="$3" + if [ "$result" = "failure" ]; then + "$REPORTER" --gate "$gate" --details "$details" --workflow "$WF" --severity error + fi + } + + report_gate "Release Configuration" \ + "${{ needs.release_config.result }}" \ + "Required repository variables are missing (RS_FTP_PATH_SUFFIX). Check repository settings." + + report_gate "Scripts Governance" \ + "${{ needs.scripts_governance.result }}" \ + "Scripts directory policy violations detected. Review required and allowed directories." + + report_gate "Repository Health" \ + "${{ needs.repo_health.result }}" \ + "Repository health checks failed — missing required artifacts, disallowed files, or content warnings. Check the CI run summary." + -- 2.52.0 From 610f875ad959f659dbd12dc56b1ad3a634be7add Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Wed, 3 Jun 2026 04:37:27 -0500 Subject: [PATCH 03/81] refactor: rename Kickstart to MokoRestore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace all Akeeba Kickstart branding with MokoRestore: - Rename Kickstart.php to MokoRestore.php - Rename class Kickstart to MokoRestore - Update DB column: include_kickstart → include_mokorestore - Update form field and language string keys - Update all variable names, log messages, and comments - Update CHANGELOG references Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/workflows/repo-health.yml | 1636 ++++++++--------- CHANGELOG.md | 4 +- src/packages/com_mokobackup/forms/profile.xml | 6 +- .../language/en-GB/com_mokobackup.ini | 4 +- .../com_mokobackup/sql/install.mysql.sql | 2 +- .../src/Engine/AkeebaImporter.php | 2 +- .../src/Engine/BackupEngine.php | 18 +- .../Engine/{Kickstart.php => MokoRestore.php} | 10 +- .../src/Engine/SteppedBackupEngine.php | 18 +- .../src/Engine/SteppedSession.php | 2 +- 10 files changed, 851 insertions(+), 851 deletions(-) rename src/packages/com_mokobackup/src/Engine/{Kickstart.php => MokoRestore.php} (98%) diff --git a/.mokogitea/workflows/repo-health.yml b/.mokogitea/workflows/repo-health.yml index d7743f0..03290b0 100644 --- a/.mokogitea/workflows/repo-health.yml +++ b/.mokogitea/workflows/repo-health.yml @@ -1,818 +1,818 @@ -# ============================================================================ -# Copyright (C) 2025 Moko Consulting -# -# This file is part of a Moko Consulting project. -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Validation -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform -# PATH: /templates/workflows/joomla/repo_health.yml.template -# VERSION: 09.23.00 -# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts. -# ============================================================================ - -name: "Generic: Repo Health" - -defaults: - run: - shell: bash - -on: - workflow_dispatch: - inputs: - profile: - description: 'Validation profile: all, release, scripts, or repo' - required: true - default: all - type: choice - options: - - all - - release - - scripts - - repo - pull_request: - push: - -permissions: - contents: read - -env: - # Release policy - Repository Variables Only - # RS_FTP_PATH_SUFFIX removed — MokoGitea handles all releases now - RELEASE_REQUIRED_REPO_VARS: - RELEASE_OPTIONAL_REPO_VARS: DEV_FTP_SUFFIX - - # Scripts governance policy - SCRIPTS_REQUIRED_DIRS: - SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate - - # Repo health policy - REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogitea/workflows/ - REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/ - REPO_DISALLOWED_DIRS: - REPO_DISALLOWED_FILES: TODO.md,todo.md - - # Extended checks toggles - EXTENDED_CHECKS: "true" - - # File / directory variables - DOCS_INDEX: docs/docs-index.md - SCRIPT_DIR: scripts - WORKFLOWS_DIR: .mokogitea/workflows - SHELLCHECK_PATTERN: '*.sh' - SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml' - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - access_check: - name: Access control - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - contents: read - - outputs: - allowed: ${{ steps.perm.outputs.allowed }} - permission: ${{ steps.perm.outputs.permission }} - - steps: - - name: Check actor permission (admin only) - id: perm - env: - TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }} - REPO: ${{ github.repository }} - ACTOR: ${{ github.actor }} - run: | - set -euo pipefail - ALLOWED=false - PERMISSION=unknown - METHOD="" - - # Hardcoded authorized users — always allowed - case "$ACTOR" in - jmiller|gitea-actions[bot]) - ALLOWED=true - PERMISSION=admin - METHOD="hardcoded allowlist" - ;; - *) - # Detect platform and check permissions via API - API_BASE="${GITHUB_API_URL:-${GITEA_API_URL:-https://api.github.com}}" - RESP=$(curl -sf -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/repos/${REPO}/collaborators/${ACTOR}/permission" 2>/dev/null || echo '{}') - PERMISSION=$(echo "$RESP" | grep -oP '"permission"\s*:\s*"\K[^"]+' || echo "unknown") - if [ "$PERMISSION" = "admin" ] || [ "$PERMISSION" = "maintain" ] || [ "$PERMISSION" = "owner" ]; then - ALLOWED=true - fi - METHOD="collaborator API" - ;; - esac - - echo "permission=${PERMISSION}" >> "$GITHUB_OUTPUT" - echo "allowed=${ALLOWED}" >> "$GITHUB_OUTPUT" - - { - echo "## Access Authorization" - echo "" - echo "| Field | Value |" - echo "|-------|-------|" - echo "| **Actor** | \`${ACTOR}\` |" - echo "| **Repository** | \`${REPO}\` |" - echo "| **Permission** | \`${PERMISSION}\` |" - echo "| **Method** | ${METHOD} |" - echo "| **Authorized** | ${ALLOWED} |" - echo "" - if [ "$ALLOWED" = "true" ]; then - echo "${ACTOR} authorized (${METHOD})" - else - echo "${ACTOR} is NOT authorized. Requires admin or maintain role." - fi - } >> "${GITHUB_STEP_SUMMARY}" - - - name: Deny execution when not permitted - if: ${{ steps.perm.outputs.allowed != 'true' }} - run: | - set -euo pipefail - printf '%s\n' 'ERROR: Access denied. Admin permission required.' >> "${GITHUB_STEP_SUMMARY}" - exit 1 - - release_config: - name: Release configuration - needs: access_check - if: ${{ needs.access_check.outputs.allowed == 'true' }} - runs-on: ubuntu-latest - timeout-minutes: 20 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - - - name: Guardrails release vars - env: - PROFILE_RAW: ${{ github.event.inputs.profile }} - RS_FTP_PATH_SUFFIX: ${{ vars.RS_FTP_PATH_SUFFIX }} - DEV_FTP_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = 'scripts' ] || [ "${profile}" = 'repo' ]; then - { - printf '%s\n' '### Release configuration (Repository Variables)' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' 'Status: SKIPPED' - printf '%s\n' 'Reason: profile excludes release validation' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - IFS=',' read -r -a required <<< "${RELEASE_REQUIRED_REPO_VARS}" - IFS=',' read -r -a optional <<< "${RELEASE_OPTIONAL_REPO_VARS}" - - missing=() - missing_optional=() - - for k in "${required[@]}"; do - v="${!k:-}" - [ -z "${v}" ] && missing+=("${k}") - done - - for k in "${optional[@]}"; do - v="${!k:-}" - [ -z "${v}" ] && missing_optional+=("${k}") - done - - { - printf '%s\n' '### Release configuration (Repository Variables)' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' '| Variable | Status |' - printf '%s\n' '|---|---|' - printf '%s\n' "| RS_FTP_PATH_SUFFIX | ${RS_FTP_PATH_SUFFIX:-NOT SET} |" - printf '%s\n' "| DEV_FTP_SUFFIX | ${DEV_FTP_SUFFIX:-NOT SET} |" - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${#missing_optional[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing optional repository variables' - for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - if [ "${#missing[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing required repository variables' - for m in "${missing[@]}"; do printf '%s\n' "- ${m}"; done - printf '%s\n' 'ERROR: Guardrails failed. Missing required repository variables.' - } >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - { - printf '%s\n' '### Repository variables validation result' - printf '%s\n' 'Status: OK' - printf '%s\n' 'All required repository variables present.' - printf '%s\n' '' - printf '%s\n' '**Note**: Organization secrets (RS_FTP_HOST, RS_FTP_USER, etc.) are validated at deployment time, not in repository health checks.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - scripts_governance: - name: Scripts governance - needs: access_check - if: ${{ needs.access_check.outputs.allowed == 'true' }} - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - - - name: Scripts folder checks - env: - PROFILE_RAW: ${{ github.event.inputs.profile }} - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = 'release' ] || [ "${profile}" = 'repo' ]; then - { - printf '%s\n' '### Scripts governance' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' 'Status: SKIPPED' - printf '%s\n' 'Reason: profile excludes scripts governance' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - if [ ! -d "${SCRIPT_DIR}" ]; then - { - printf '%s\n' '### Scripts governance' - printf '%s\n' 'Status: OK (advisory)' - printf '%s\n' 'scripts/ directory not present. No scripts governance enforced.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - if [ -n "${SCRIPTS_REQUIRED_DIRS:-}" ]; then IFS=',' read -r -a required_dirs <<< "${SCRIPTS_REQUIRED_DIRS}"; else required_dirs=(); fi - IFS=',' read -r -a allowed_dirs <<< "${SCRIPTS_ALLOWED_DIRS}" - - missing_dirs=() - unapproved_dirs=() - - for d in "${required_dirs[@]}"; do - req="${d%/}" - [ ! -d "${req}" ] && missing_dirs+=("${req}/") - done - - while IFS= read -r d; do - allowed=false - for a in "${allowed_dirs[@]}"; do - a_norm="${a%/}" - [ "${d%/}" = "${a_norm}" ] && allowed=true - done - [ "${allowed}" = false ] && unapproved_dirs+=("${d%/}/") - done < <(find "${SCRIPT_DIR}" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sed 's#^\./##') - - { - printf '%s\n' '### Scripts governance' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' '| Area | Status | Notes |' - printf '%s\n' '|---|---|---|' - - if [ "${#missing_dirs[@]}" -gt 0 ]; then - printf '%s\n' '| Required directories | Warning | Missing required subfolders |' - else - printf '%s\n' '| Required directories | OK | All required subfolders present |' - fi - - if [ "${#unapproved_dirs[@]}" -gt 0 ]; then - printf '%s\n' '| Directory policy | Warning | Unapproved directories detected |' - else - printf '%s\n' '| Directory policy | OK | No unapproved directories |' - fi - - printf '%s\n' '| Enforcement mode | Advisory | scripts folder is optional |' - printf '\n' - - if [ "${#missing_dirs[@]}" -gt 0 ]; then - printf '%s\n' 'Missing required script directories:' - for m in "${missing_dirs[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - else - printf '%s\n' 'Missing required script directories: none.' - printf '\n' - fi - - if [ "${#unapproved_dirs[@]}" -gt 0 ]; then - printf '%s\n' 'Unapproved script directories detected:' - for m in "${unapproved_dirs[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - else - printf '%s\n' 'Unapproved script directories detected: none.' - printf '\n' - fi - - printf '%s\n' 'Scripts governance completed in advisory mode.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - repo_health: - name: Repository health - needs: access_check - if: ${{ needs.access_check.outputs.allowed == 'true' }} - runs-on: ubuntu-latest - timeout-minutes: 20 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - - - name: Repository health checks - env: - PROFILE_RAW: ${{ github.event.inputs.profile }} - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = 'release' ] || [ "${profile}" = 'scripts' ]; then - { - printf '%s\n' '### Repository health' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' 'Status: SKIPPED' - printf '%s\n' 'Reason: profile excludes repository health' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}" - IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}" - if [ -n "${REPO_DISALLOWED_DIRS:-}" ]; then IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"; else disallowed_dirs=(); fi - IFS=',' read -r -a disallowed_files <<< "${REPO_DISALLOWED_FILES:-}" - - missing_required=() - missing_optional=() - - # Source directory: src/ or htdocs/ (either is valid for extension repos) - SOURCE_DIR="" - if [ -d "src" ]; then - SOURCE_DIR="src" - elif [ -d "htdocs" ]; then - SOURCE_DIR="htdocs" - elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then - # Platform/tooling repos don't need src/ - SOURCE_DIR="" - else - missing_required+=("src/ or htdocs/ (source directory required)") - fi - - for item in "${required_artifacts[@]}"; do - if printf '%s' "${item}" | grep -q '/$'; then - d="${item%/}" - [ ! -d "${d}" ] && missing_required+=("${item}") - else - [ ! -f "${item}" ] && missing_required+=("${item}") - fi - done - - for f in "${optional_files[@]}"; do - if printf '%s' "${f}" | grep -q '/$'; then - d="${f%/}" - [ ! -d "${d}" ] && missing_optional+=("${f}") - else - [ ! -f "${f}" ] && missing_optional+=("${f}") - fi - done - - for d in "${disallowed_dirs[@]}"; do - d_norm="${d%/}" - [ -d "${d_norm}" ] && missing_required+=("${d_norm}/ (disallowed)") - done - - for f in "${disallowed_files[@]}"; do - [ -f "${f}" ] && missing_required+=("${f} (disallowed)") - done - - git fetch origin --prune - - dev_paths=() - dev_branches=() - - while IFS= read -r b; do - name="${b#origin/}" - if [ "${name}" = 'dev' ]; then - dev_branches+=("${name}") - else - dev_paths+=("${name}") - fi - done < <(git branch -r --list 'origin/dev*' | sed 's/^ *//') - - if [ "${#dev_paths[@]}" -eq 0 ] && [ "${#dev_branches[@]}" -eq 0 ]; then - missing_required+=("dev or dev/* branch") - fi - - content_warnings=() - - if [ -f 'CHANGELOG.md' ] && ! grep -Eq '^# Changelog' CHANGELOG.md; then - content_warnings+=("CHANGELOG.md missing '# Changelog' header") - fi - - if [ -f 'CHANGELOG.md' ] && grep -Eq '^[# ]*Unreleased' CHANGELOG.md; then - content_warnings+=("CHANGELOG.md contains Unreleased section (review release readiness)") - fi - - if [ -f 'LICENSE' ] && ! grep -qiE 'GNU GENERAL PUBLIC LICENSE|GPL' LICENSE; then - content_warnings+=("LICENSE does not look like a GPL text") - fi - - if [ -f 'README.md' ] && ! grep -qiE 'moko|Moko' README.md; then - content_warnings+=("README.md missing expected brand keyword") - fi - - export PROFILE_RAW="${profile}" - export MISSING_REQUIRED="$(printf '%s\n' "${missing_required[@]:-}")" - export MISSING_OPTIONAL="$(printf '%s\n' "${missing_optional[@]:-}")" - export CONTENT_WARNINGS="$(printf '%s\n' "${content_warnings[@]:-}")" - - report_json=$(printf '{"profile":"%s","missing_required":%d,"missing_optional":%d,"content_warnings":%d}' "$profile" "${#missing_required[@]}" "${#missing_optional[@]}" "${#content_warnings[@]}") - - { - printf '%s\n' '### Repository health' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' '| Metric | Value |' - printf '%s\n' '|---|---|' - printf '%s\n' "| Missing required | ${#missing_required[@]} |" - printf '%s\n' "| Missing optional | ${#missing_optional[@]} |" - printf '%s\n' "| Content warnings | ${#content_warnings[@]} |" - printf '\n' - - printf '%s\n' '### Guardrails report (JSON)' - printf '%s\n' '```json' - printf '%s\n' "${report_json}" - printf '%s\n' '```' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${#missing_required[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing required repo artifacts' - for m in "${missing_required[@]}"; do printf '%s\n' "- ${m}"; done - printf '%s\n' 'ERROR: Guardrails failed. Missing required repository artifacts.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#missing_optional[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing optional repo artifacts' - for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - if [ "${#content_warnings[@]}" -gt 0 ]; then - { - printf '%s\n' '### Repo content warnings' - for m in "${content_warnings[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - # -- Joomla-specific checks -- - joomla_findings=() - - MANIFEST="$(find . -maxdepth 2 -name '*.xml' -exec grep -l '/dev/null | head -1 || true)" - if [ -z "${MANIFEST}" ]; then - joomla_findings+=("Joomla XML manifest not found (no *.xml with tag)") - else - if ! grep -qP '' "${MANIFEST}"; then - joomla_findings+=("XML manifest: tag missing") - fi - if ! grep -qP 'type="(component|module|plugin|library|package|template|language)"' "${MANIFEST}"; then - joomla_findings+=("XML manifest: type attribute missing or invalid") - fi - if ! grep -qP '' "${MANIFEST}"; then - joomla_findings+=("XML manifest: tag missing") - fi - if ! grep -qP '' "${MANIFEST}"; then - joomla_findings+=("XML manifest: tag missing") - fi - if ! grep -qP ' missing (required for Joomla 5+)") - fi - fi - - INI_COUNT="$(find . -name '*.ini' -type f 2>/dev/null | wc -l)" - if [ "${INI_COUNT}" -eq 0 ]; then - joomla_findings+=("No .ini language files found") - fi - - if [ ! -f 'updates.xml' ]; then - joomla_findings+=("updates.xml missing in root (required for Joomla update server)") - fi - - if [ -n "${SOURCE_DIR}" ]; then - INDEX_DIRS=("${SOURCE_DIR}" "${SOURCE_DIR}/admin" "${SOURCE_DIR}/site") - for dir in "${INDEX_DIRS[@]}"; do - if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then - joomla_findings+=("${dir}/index.html missing (directory listing protection)") - fi - done - fi - - if [ "${#joomla_findings[@]}" -gt 0 ]; then - { - printf '%s\n' '### Joomla extension checks' - printf '%s\n' '| Check | Status |' - printf '%s\n' '|---|---|' - for f in "${joomla_findings[@]}"; do - printf '%s\n' "| ${f} | Warning |" - done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - else - { - printf '%s\n' '### Joomla extension checks' - printf '%s\n' 'All Joomla-specific checks passed.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - extended_enabled="${EXTENDED_CHECKS:-true}" - extended_findings=() - - if [ "${extended_enabled}" = 'true' ]; then - if [ -f '.github/CODEOWNERS' ] || [ -f 'CODEOWNERS' ] || [ -f 'docs/CODEOWNERS' ]; then - : - else - extended_findings+=("CODEOWNERS not found (.github/CODEOWNERS preferred)") - fi - - if ls "${WORKFLOWS_DIR}"/*.yml >/dev/null 2>&1 || ls "${WORKFLOWS_DIR}"/*.yaml >/dev/null 2>&1; then - bad_refs="$(grep -RIn --include='*.yml' --include='*.yaml' -E '^[[:space:]]*uses:[[:space:]]*[^#]+@(main|master)\b' "${WORKFLOWS_DIR}" 2>/dev/null || true)" - if [ -n "${bad_refs}" ]; then - extended_findings+=("Workflows reference actions @main/@master (pin versions): see log excerpt") - { - printf '%s\n' '### Workflow pinning advisory' - printf '%s\n' 'Found uses: entries pinned to main/master:' - printf '%s\n' '```' - printf '%s\n' "${bad_refs}" - printf '%s\n' '```' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - fi - - if [ -f "${DOCS_INDEX}" ]; then - missing_links="" - while IFS= read -r docline; do - for link in $(echo "$docline" | grep -oE '\]\([^)]+\)' | sed 's/\](//' | sed 's/)$//' || true); do - case "$link" in http://*|https://*|"#"*|mailto:*) continue ;; esac - linkpath="${link%%#*}" - linkpath="${linkpath%%\?*}" - [ -z "$linkpath" ] && continue - if [ "${linkpath:0:1}" = "/" ]; then - testpath="${linkpath#/}" - else - testpath="$(dirname "${DOCS_INDEX}")/${linkpath}" - fi - [ ! -e "$testpath" ] && missing_links="${missing_links}${testpath} " - done - done < "${DOCS_INDEX}" - if [ -n "${missing_links}" ]; then - extended_findings+=("docs/docs-index.md contains broken relative links") - { - printf '%s\n' '### Docs index link integrity' - printf '%s\n' 'Broken relative links:' - for bl in ${missing_links}; do - printf '%s\n' "- ${bl}" - done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - fi - - if [ -d "${SCRIPT_DIR}" ]; then - if ! command -v shellcheck >/dev/null 2>&1; then - sudo apt-get update -qq - sudo apt-get install -y shellcheck >/dev/null - fi - - sc_out='' - while IFS= read -r shf; do - [ -z "${shf}" ] && continue - out_one="$(shellcheck -S warning -x "${shf}" 2>/dev/null || true)" - if [ -n "${out_one}" ]; then - sc_out="${sc_out}${out_one}\n" - fi - done < <(find "${SCRIPT_DIR}" -type f -name "${SHELLCHECK_PATTERN}" 2>/dev/null | sort) - - if [ -n "${sc_out}" ]; then - extended_findings+=("ShellCheck warnings detected (advisory)") - sc_head="$(printf '%s' "${sc_out}" | head -n 200)" - { - printf '%s\n' '### ShellCheck (advisory)' - printf '%s\n' '```' - printf '%s\n' "${sc_head}" - printf '%s\n' '```' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - fi - - spdx_missing=() - IFS=',' read -r -a spdx_globs <<< "${SPDX_FILE_GLOBS}" - spdx_args=() - for g in "${spdx_globs[@]}"; do spdx_args+=("${g}"); done - - while IFS= read -r f; do - [ -z "${f}" ] && continue - if ! head -n 40 "${f}" | grep -q 'SPDX-License-Identifier:'; then - spdx_missing+=("${f}") - fi - done < <(git ls-files "${spdx_args[@]}" 2>/dev/null || true) - - if [ "${#spdx_missing[@]}" -gt 0 ]; then - extended_findings+=("SPDX header missing in some tracked files (advisory)") - { - printf '%s\n' '### SPDX header advisory' - printf '%s\n' 'Files missing SPDX-License-Identifier (first 40 lines scan):' - for f in "${spdx_missing[@]}"; do printf '%s\n' "- ${f}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - stale_cutoff_days=180 - stale_branches="$(git for-each-ref --format='%(refname:short) %(committerdate:unix)' refs/remotes/origin 2>/dev/null | awk -v now="$(date +%s)" -v days="${stale_cutoff_days}" '{if (now-$2 > days*86400) print $1}' | head -50)" - if [ -n "${stale_branches}" ]; then - extended_findings+=("Stale remote branches detected (advisory)") - { - printf '%s\n' '### Git hygiene advisory' - printf '%s\n' "Branches with last commit older than ${stale_cutoff_days} days (sample up to 50):" - while IFS= read -r b; do [ -n "${b}" ] && printf '%s\n' "- ${b}"; done <<< "${stale_branches}" - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - fi - - { - printf '%s\n' '### Guardrails coverage matrix' - printf '%s\n' '| Domain | Status | Notes |' - printf '%s\n' '|---|---|---|' - printf '%s\n' '| Access control | OK | Admin-only execution gate |' - printf '%s\n' '| Release variables | OK | Repository variables validation |' - printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |' - printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |' - printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |' - if [ "${extended_enabled}" = 'true' ]; then - if [ "${#extended_findings[@]}" -gt 0 ]; then - printf '%s\n' '| Extended checks | Warning | See extended findings below |' - else - printf '%s\n' '| Extended checks | OK | No findings |' - fi - else - printf '%s\n' '| Extended checks | SKIPPED | EXTENDED_CHECKS disabled |' - fi - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${extended_enabled}" = 'true' ] && [ "${#extended_findings[@]}" -gt 0 ]; then - { - printf '%s\n' '### Extended findings (advisory)' - for f in "${extended_findings[@]}"; do printf '%s\n' "- ${f}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - printf '%s\n' 'Repository health guardrails passed.' >> "${GITHUB_STEP_SUMMARY}" - - - site-health: - name: Site Health - runs-on: ubuntu-latest - if: github.event_name == 'workflow_dispatch' - steps: - - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.3' - - - name: Uptime check - if: env.URLS != '' - run: | - echo "$URLS" > /tmp/urls.txt - php monitoring/uptime-probe.php --urls /tmp/urls.txt --timeout 15 || echo "::warning::Some sites are down" - rm -f /tmp/urls.txt - env: - URLS: ${{ vars.MONITORED_URLS }} - - - name: SSL certificate check - if: env.DOMAINS != '' - run: | - echo "$DOMAINS" > /tmp/domains.txt - php monitoring/ssl-check.php --domains /tmp/domains.txt --warn-days 30 || echo "::warning::SSL certificates expiring soon" - rm -f /tmp/domains.txt - env: - DOMAINS: ${{ vars.MONITORED_DOMAINS }} - - - name: Summary - if: always() - run: | - echo "### Site Health" >> $GITHUB_STEP_SUMMARY - echo "Uptime and SSL checks completed." >> $GITHUB_STEP_SUMMARY - - # ═══════════════════════════════════════════════════════════════════════ - # Issue Reporter — file issues for failed gates - # ═══════════════════════════════════════════════════════════════════════ - report-issues: - name: "Report Issues" - runs-on: ubuntu-latest - needs: [access_check, release_config, scripts_governance, repo_health] - if: >- - always() && - (needs.release_config.result == 'failure' || - needs.scripts_governance.result == 'failure' || - needs.repo_health.result == 'failure') - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - sparse-checkout: automation/ci-issue-reporter.sh - sparse-checkout-cone-mode: false - - - name: "File issues for failed gates" - env: - GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - run: | - chmod +x automation/ci-issue-reporter.sh - REPORTER="./automation/ci-issue-reporter.sh" - WF="Repo Health" - - report_gate() { - local gate="$1" result="$2" details="$3" - if [ "$result" = "failure" ]; then - "$REPORTER" --gate "$gate" --details "$details" --workflow "$WF" --severity error - fi - } - - report_gate "Release Configuration" \ - "${{ needs.release_config.result }}" \ - "Required repository variables are missing (RS_FTP_PATH_SUFFIX). Check repository settings." - - report_gate "Scripts Governance" \ - "${{ needs.scripts_governance.result }}" \ - "Scripts directory policy violations detected. Review required and allowed directories." - - report_gate "Repository Health" \ - "${{ needs.repo_health.result }}" \ - "Repository health checks failed — missing required artifacts, disallowed files, or content warnings. Check the CI run summary." - +# ============================================================================ +# Copyright (C) 2025 Moko Consulting +# +# This file is part of a Moko Consulting project. +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Validation +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform +# PATH: /templates/workflows/joomla/repo_health.yml.template +# VERSION: 09.23.00 +# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts. +# ============================================================================ + +name: "Generic: Repo Health" + +defaults: + run: + shell: bash + +on: + workflow_dispatch: + inputs: + profile: + description: 'Validation profile: all, release, scripts, or repo' + required: true + default: all + type: choice + options: + - all + - release + - scripts + - repo + pull_request: + push: + +permissions: + contents: read + +env: + # Release policy - Repository Variables Only + # RS_FTP_PATH_SUFFIX removed — MokoGitea handles all releases now + RELEASE_REQUIRED_REPO_VARS: + RELEASE_OPTIONAL_REPO_VARS: DEV_FTP_SUFFIX + + # Scripts governance policy + SCRIPTS_REQUIRED_DIRS: + SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate + + # Repo health policy + REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogitea/workflows/ + REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/ + REPO_DISALLOWED_DIRS: + REPO_DISALLOWED_FILES: TODO.md,todo.md + + # Extended checks toggles + EXTENDED_CHECKS: "true" + + # File / directory variables + DOCS_INDEX: docs/docs-index.md + SCRIPT_DIR: scripts + WORKFLOWS_DIR: .mokogitea/workflows + SHELLCHECK_PATTERN: '*.sh' + SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml' + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + access_check: + name: Access control + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + + outputs: + allowed: ${{ steps.perm.outputs.allowed }} + permission: ${{ steps.perm.outputs.permission }} + + steps: + - name: Check actor permission (admin only) + id: perm + env: + TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }} + REPO: ${{ github.repository }} + ACTOR: ${{ github.actor }} + run: | + set -euo pipefail + ALLOWED=false + PERMISSION=unknown + METHOD="" + + # Hardcoded authorized users — always allowed + case "$ACTOR" in + jmiller|gitea-actions[bot]) + ALLOWED=true + PERMISSION=admin + METHOD="hardcoded allowlist" + ;; + *) + # Detect platform and check permissions via API + API_BASE="${GITHUB_API_URL:-${GITEA_API_URL:-https://api.github.com}}" + RESP=$(curl -sf -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/repos/${REPO}/collaborators/${ACTOR}/permission" 2>/dev/null || echo '{}') + PERMISSION=$(echo "$RESP" | grep -oP '"permission"\s*:\s*"\K[^"]+' || echo "unknown") + if [ "$PERMISSION" = "admin" ] || [ "$PERMISSION" = "maintain" ] || [ "$PERMISSION" = "owner" ]; then + ALLOWED=true + fi + METHOD="collaborator API" + ;; + esac + + echo "permission=${PERMISSION}" >> "$GITHUB_OUTPUT" + echo "allowed=${ALLOWED}" >> "$GITHUB_OUTPUT" + + { + echo "## Access Authorization" + echo "" + echo "| Field | Value |" + echo "|-------|-------|" + echo "| **Actor** | \`${ACTOR}\` |" + echo "| **Repository** | \`${REPO}\` |" + echo "| **Permission** | \`${PERMISSION}\` |" + echo "| **Method** | ${METHOD} |" + echo "| **Authorized** | ${ALLOWED} |" + echo "" + if [ "$ALLOWED" = "true" ]; then + echo "${ACTOR} authorized (${METHOD})" + else + echo "${ACTOR} is NOT authorized. Requires admin or maintain role." + fi + } >> "${GITHUB_STEP_SUMMARY}" + + - name: Deny execution when not permitted + if: ${{ steps.perm.outputs.allowed != 'true' }} + run: | + set -euo pipefail + printf '%s\n' 'ERROR: Access denied. Admin permission required.' >> "${GITHUB_STEP_SUMMARY}" + exit 1 + + release_config: + name: Release configuration + needs: access_check + if: ${{ needs.access_check.outputs.allowed == 'true' }} + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Guardrails release vars + env: + PROFILE_RAW: ${{ github.event.inputs.profile }} + RS_FTP_PATH_SUFFIX: ${{ vars.RS_FTP_PATH_SUFFIX }} + DEV_FTP_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} + run: | + set -euo pipefail + + profile="${PROFILE_RAW:-all}" + case "${profile}" in + all|release|scripts|repo) ;; + *) + printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" + exit 1 + ;; + esac + + if [ "${profile}" = 'scripts' ] || [ "${profile}" = 'repo' ]; then + { + printf '%s\n' '### Release configuration (Repository Variables)' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' 'Status: SKIPPED' + printf '%s\n' 'Reason: profile excludes release validation' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + IFS=',' read -r -a required <<< "${RELEASE_REQUIRED_REPO_VARS}" + IFS=',' read -r -a optional <<< "${RELEASE_OPTIONAL_REPO_VARS}" + + missing=() + missing_optional=() + + for k in "${required[@]}"; do + v="${!k:-}" + [ -z "${v}" ] && missing+=("${k}") + done + + for k in "${optional[@]}"; do + v="${!k:-}" + [ -z "${v}" ] && missing_optional+=("${k}") + done + + { + printf '%s\n' '### Release configuration (Repository Variables)' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' '| Variable | Status |' + printf '%s\n' '|---|---|' + printf '%s\n' "| RS_FTP_PATH_SUFFIX | ${RS_FTP_PATH_SUFFIX:-NOT SET} |" + printf '%s\n' "| DEV_FTP_SUFFIX | ${DEV_FTP_SUFFIX:-NOT SET} |" + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + if [ "${#missing_optional[@]}" -gt 0 ]; then + { + printf '%s\n' '### Missing optional repository variables' + for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + if [ "${#missing[@]}" -gt 0 ]; then + { + printf '%s\n' '### Missing required repository variables' + for m in "${missing[@]}"; do printf '%s\n' "- ${m}"; done + printf '%s\n' 'ERROR: Guardrails failed. Missing required repository variables.' + } >> "${GITHUB_STEP_SUMMARY}" + exit 1 + fi + + { + printf '%s\n' '### Repository variables validation result' + printf '%s\n' 'Status: OK' + printf '%s\n' 'All required repository variables present.' + printf '%s\n' '' + printf '%s\n' '**Note**: Organization secrets (RS_FTP_HOST, RS_FTP_USER, etc.) are validated at deployment time, not in repository health checks.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + scripts_governance: + name: Scripts governance + needs: access_check + if: ${{ needs.access_check.outputs.allowed == 'true' }} + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Scripts folder checks + env: + PROFILE_RAW: ${{ github.event.inputs.profile }} + run: | + set -euo pipefail + + profile="${PROFILE_RAW:-all}" + case "${profile}" in + all|release|scripts|repo) ;; + *) + printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" + exit 1 + ;; + esac + + if [ "${profile}" = 'release' ] || [ "${profile}" = 'repo' ]; then + { + printf '%s\n' '### Scripts governance' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' 'Status: SKIPPED' + printf '%s\n' 'Reason: profile excludes scripts governance' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + if [ ! -d "${SCRIPT_DIR}" ]; then + { + printf '%s\n' '### Scripts governance' + printf '%s\n' 'Status: OK (advisory)' + printf '%s\n' 'scripts/ directory not present. No scripts governance enforced.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + if [ -n "${SCRIPTS_REQUIRED_DIRS:-}" ]; then IFS=',' read -r -a required_dirs <<< "${SCRIPTS_REQUIRED_DIRS}"; else required_dirs=(); fi + IFS=',' read -r -a allowed_dirs <<< "${SCRIPTS_ALLOWED_DIRS}" + + missing_dirs=() + unapproved_dirs=() + + for d in "${required_dirs[@]}"; do + req="${d%/}" + [ ! -d "${req}" ] && missing_dirs+=("${req}/") + done + + while IFS= read -r d; do + allowed=false + for a in "${allowed_dirs[@]}"; do + a_norm="${a%/}" + [ "${d%/}" = "${a_norm}" ] && allowed=true + done + [ "${allowed}" = false ] && unapproved_dirs+=("${d%/}/") + done < <(find "${SCRIPT_DIR}" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sed 's#^\./##') + + { + printf '%s\n' '### Scripts governance' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' '| Area | Status | Notes |' + printf '%s\n' '|---|---|---|' + + if [ "${#missing_dirs[@]}" -gt 0 ]; then + printf '%s\n' '| Required directories | Warning | Missing required subfolders |' + else + printf '%s\n' '| Required directories | OK | All required subfolders present |' + fi + + if [ "${#unapproved_dirs[@]}" -gt 0 ]; then + printf '%s\n' '| Directory policy | Warning | Unapproved directories detected |' + else + printf '%s\n' '| Directory policy | OK | No unapproved directories |' + fi + + printf '%s\n' '| Enforcement mode | Advisory | scripts folder is optional |' + printf '\n' + + if [ "${#missing_dirs[@]}" -gt 0 ]; then + printf '%s\n' 'Missing required script directories:' + for m in "${missing_dirs[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + else + printf '%s\n' 'Missing required script directories: none.' + printf '\n' + fi + + if [ "${#unapproved_dirs[@]}" -gt 0 ]; then + printf '%s\n' 'Unapproved script directories detected:' + for m in "${unapproved_dirs[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + else + printf '%s\n' 'Unapproved script directories detected: none.' + printf '\n' + fi + + printf '%s\n' 'Scripts governance completed in advisory mode.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + repo_health: + name: Repository health + needs: access_check + if: ${{ needs.access_check.outputs.allowed == 'true' }} + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Repository health checks + env: + PROFILE_RAW: ${{ github.event.inputs.profile }} + run: | + set -euo pipefail + + profile="${PROFILE_RAW:-all}" + case "${profile}" in + all|release|scripts|repo) ;; + *) + printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" + exit 1 + ;; + esac + + if [ "${profile}" = 'release' ] || [ "${profile}" = 'scripts' ]; then + { + printf '%s\n' '### Repository health' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' 'Status: SKIPPED' + printf '%s\n' 'Reason: profile excludes repository health' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}" + IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}" + if [ -n "${REPO_DISALLOWED_DIRS:-}" ]; then IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"; else disallowed_dirs=(); fi + IFS=',' read -r -a disallowed_files <<< "${REPO_DISALLOWED_FILES:-}" + + missing_required=() + missing_optional=() + + # Source directory: src/ or htdocs/ (either is valid for extension repos) + SOURCE_DIR="" + if [ -d "src" ]; then + SOURCE_DIR="src" + elif [ -d "htdocs" ]; then + SOURCE_DIR="htdocs" + elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then + # Platform/tooling repos don't need src/ + SOURCE_DIR="" + else + missing_required+=("src/ or htdocs/ (source directory required)") + fi + + for item in "${required_artifacts[@]}"; do + if printf '%s' "${item}" | grep -q '/$'; then + d="${item%/}" + [ ! -d "${d}" ] && missing_required+=("${item}") + else + [ ! -f "${item}" ] && missing_required+=("${item}") + fi + done + + for f in "${optional_files[@]}"; do + if printf '%s' "${f}" | grep -q '/$'; then + d="${f%/}" + [ ! -d "${d}" ] && missing_optional+=("${f}") + else + [ ! -f "${f}" ] && missing_optional+=("${f}") + fi + done + + for d in "${disallowed_dirs[@]}"; do + d_norm="${d%/}" + [ -d "${d_norm}" ] && missing_required+=("${d_norm}/ (disallowed)") + done + + for f in "${disallowed_files[@]}"; do + [ -f "${f}" ] && missing_required+=("${f} (disallowed)") + done + + git fetch origin --prune + + dev_paths=() + dev_branches=() + + while IFS= read -r b; do + name="${b#origin/}" + if [ "${name}" = 'dev' ]; then + dev_branches+=("${name}") + else + dev_paths+=("${name}") + fi + done < <(git branch -r --list 'origin/dev*' | sed 's/^ *//') + + if [ "${#dev_paths[@]}" -eq 0 ] && [ "${#dev_branches[@]}" -eq 0 ]; then + missing_required+=("dev or dev/* branch") + fi + + content_warnings=() + + if [ -f 'CHANGELOG.md' ] && ! grep -Eq '^# Changelog' CHANGELOG.md; then + content_warnings+=("CHANGELOG.md missing '# Changelog' header") + fi + + if [ -f 'CHANGELOG.md' ] && grep -Eq '^[# ]*Unreleased' CHANGELOG.md; then + content_warnings+=("CHANGELOG.md contains Unreleased section (review release readiness)") + fi + + if [ -f 'LICENSE' ] && ! grep -qiE 'GNU GENERAL PUBLIC LICENSE|GPL' LICENSE; then + content_warnings+=("LICENSE does not look like a GPL text") + fi + + if [ -f 'README.md' ] && ! grep -qiE 'moko|Moko' README.md; then + content_warnings+=("README.md missing expected brand keyword") + fi + + export PROFILE_RAW="${profile}" + export MISSING_REQUIRED="$(printf '%s\n' "${missing_required[@]:-}")" + export MISSING_OPTIONAL="$(printf '%s\n' "${missing_optional[@]:-}")" + export CONTENT_WARNINGS="$(printf '%s\n' "${content_warnings[@]:-}")" + + report_json=$(printf '{"profile":"%s","missing_required":%d,"missing_optional":%d,"content_warnings":%d}' "$profile" "${#missing_required[@]}" "${#missing_optional[@]}" "${#content_warnings[@]}") + + { + printf '%s\n' '### Repository health' + printf '%s\n' "Profile: ${profile}" + printf '%s\n' '| Metric | Value |' + printf '%s\n' '|---|---|' + printf '%s\n' "| Missing required | ${#missing_required[@]} |" + printf '%s\n' "| Missing optional | ${#missing_optional[@]} |" + printf '%s\n' "| Content warnings | ${#content_warnings[@]} |" + printf '\n' + + printf '%s\n' '### Guardrails report (JSON)' + printf '%s\n' '```json' + printf '%s\n' "${report_json}" + printf '%s\n' '```' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + if [ "${#missing_required[@]}" -gt 0 ]; then + { + printf '%s\n' '### Missing required repo artifacts' + for m in "${missing_required[@]}"; do printf '%s\n' "- ${m}"; done + printf '%s\n' 'ERROR: Guardrails failed. Missing required repository artifacts.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + exit 1 + fi + + if [ "${#missing_optional[@]}" -gt 0 ]; then + { + printf '%s\n' '### Missing optional repo artifacts' + for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + if [ "${#content_warnings[@]}" -gt 0 ]; then + { + printf '%s\n' '### Repo content warnings' + for m in "${content_warnings[@]}"; do printf '%s\n' "- ${m}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + # -- Joomla-specific checks -- + joomla_findings=() + + MANIFEST="$(find . -maxdepth 2 -name '*.xml' -exec grep -l '/dev/null | head -1 || true)" + if [ -z "${MANIFEST}" ]; then + joomla_findings+=("Joomla XML manifest not found (no *.xml with tag)") + else + if ! grep -qP '' "${MANIFEST}"; then + joomla_findings+=("XML manifest: tag missing") + fi + if ! grep -qP 'type="(component|module|plugin|library|package|template|language)"' "${MANIFEST}"; then + joomla_findings+=("XML manifest: type attribute missing or invalid") + fi + if ! grep -qP '' "${MANIFEST}"; then + joomla_findings+=("XML manifest: tag missing") + fi + if ! grep -qP '' "${MANIFEST}"; then + joomla_findings+=("XML manifest: tag missing") + fi + if ! grep -qP ' missing (required for Joomla 5+)") + fi + fi + + INI_COUNT="$(find . -name '*.ini' -type f 2>/dev/null | wc -l)" + if [ "${INI_COUNT}" -eq 0 ]; then + joomla_findings+=("No .ini language files found") + fi + + if [ ! -f 'updates.xml' ]; then + joomla_findings+=("updates.xml missing in root (required for Joomla update server)") + fi + + if [ -n "${SOURCE_DIR}" ]; then + INDEX_DIRS=("${SOURCE_DIR}" "${SOURCE_DIR}/admin" "${SOURCE_DIR}/site") + for dir in "${INDEX_DIRS[@]}"; do + if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then + joomla_findings+=("${dir}/index.html missing (directory listing protection)") + fi + done + fi + + if [ "${#joomla_findings[@]}" -gt 0 ]; then + { + printf '%s\n' '### Joomla extension checks' + printf '%s\n' '| Check | Status |' + printf '%s\n' '|---|---|' + for f in "${joomla_findings[@]}"; do + printf '%s\n' "| ${f} | Warning |" + done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + else + { + printf '%s\n' '### Joomla extension checks' + printf '%s\n' 'All Joomla-specific checks passed.' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + extended_enabled="${EXTENDED_CHECKS:-true}" + extended_findings=() + + if [ "${extended_enabled}" = 'true' ]; then + if [ -f '.github/CODEOWNERS' ] || [ -f 'CODEOWNERS' ] || [ -f 'docs/CODEOWNERS' ]; then + : + else + extended_findings+=("CODEOWNERS not found (.github/CODEOWNERS preferred)") + fi + + if ls "${WORKFLOWS_DIR}"/*.yml >/dev/null 2>&1 || ls "${WORKFLOWS_DIR}"/*.yaml >/dev/null 2>&1; then + bad_refs="$(grep -RIn --include='*.yml' --include='*.yaml' -E '^[[:space:]]*uses:[[:space:]]*[^#]+@(main|master)\b' "${WORKFLOWS_DIR}" 2>/dev/null || true)" + if [ -n "${bad_refs}" ]; then + extended_findings+=("Workflows reference actions @main/@master (pin versions): see log excerpt") + { + printf '%s\n' '### Workflow pinning advisory' + printf '%s\n' 'Found uses: entries pinned to main/master:' + printf '%s\n' '```' + printf '%s\n' "${bad_refs}" + printf '%s\n' '```' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + fi + + if [ -f "${DOCS_INDEX}" ]; then + missing_links="" + while IFS= read -r docline; do + for link in $(echo "$docline" | grep -oE '\]\([^)]+\)' | sed 's/\](//' | sed 's/)$//' || true); do + case "$link" in http://*|https://*|"#"*|mailto:*) continue ;; esac + linkpath="${link%%#*}" + linkpath="${linkpath%%\?*}" + [ -z "$linkpath" ] && continue + if [ "${linkpath:0:1}" = "/" ]; then + testpath="${linkpath#/}" + else + testpath="$(dirname "${DOCS_INDEX}")/${linkpath}" + fi + [ ! -e "$testpath" ] && missing_links="${missing_links}${testpath} " + done + done < "${DOCS_INDEX}" + if [ -n "${missing_links}" ]; then + extended_findings+=("docs/docs-index.md contains broken relative links") + { + printf '%s\n' '### Docs index link integrity' + printf '%s\n' 'Broken relative links:' + for bl in ${missing_links}; do + printf '%s\n' "- ${bl}" + done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + fi + + if [ -d "${SCRIPT_DIR}" ]; then + if ! command -v shellcheck >/dev/null 2>&1; then + sudo apt-get update -qq + sudo apt-get install -y shellcheck >/dev/null + fi + + sc_out='' + while IFS= read -r shf; do + [ -z "${shf}" ] && continue + out_one="$(shellcheck -S warning -x "${shf}" 2>/dev/null || true)" + if [ -n "${out_one}" ]; then + sc_out="${sc_out}${out_one}\n" + fi + done < <(find "${SCRIPT_DIR}" -type f -name "${SHELLCHECK_PATTERN}" 2>/dev/null | sort) + + if [ -n "${sc_out}" ]; then + extended_findings+=("ShellCheck warnings detected (advisory)") + sc_head="$(printf '%s' "${sc_out}" | head -n 200)" + { + printf '%s\n' '### ShellCheck (advisory)' + printf '%s\n' '```' + printf '%s\n' "${sc_head}" + printf '%s\n' '```' + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + fi + + spdx_missing=() + IFS=',' read -r -a spdx_globs <<< "${SPDX_FILE_GLOBS}" + spdx_args=() + for g in "${spdx_globs[@]}"; do spdx_args+=("${g}"); done + + while IFS= read -r f; do + [ -z "${f}" ] && continue + if ! head -n 40 "${f}" | grep -q 'SPDX-License-Identifier:'; then + spdx_missing+=("${f}") + fi + done < <(git ls-files "${spdx_args[@]}" 2>/dev/null || true) + + if [ "${#spdx_missing[@]}" -gt 0 ]; then + extended_findings+=("SPDX header missing in some tracked files (advisory)") + { + printf '%s\n' '### SPDX header advisory' + printf '%s\n' 'Files missing SPDX-License-Identifier (first 40 lines scan):' + for f in "${spdx_missing[@]}"; do printf '%s\n' "- ${f}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + stale_cutoff_days=180 + stale_branches="$(git for-each-ref --format='%(refname:short) %(committerdate:unix)' refs/remotes/origin 2>/dev/null | awk -v now="$(date +%s)" -v days="${stale_cutoff_days}" '{if (now-$2 > days*86400) print $1}' | head -50)" + if [ -n "${stale_branches}" ]; then + extended_findings+=("Stale remote branches detected (advisory)") + { + printf '%s\n' '### Git hygiene advisory' + printf '%s\n' "Branches with last commit older than ${stale_cutoff_days} days (sample up to 50):" + while IFS= read -r b; do [ -n "${b}" ] && printf '%s\n' "- ${b}"; done <<< "${stale_branches}" + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + fi + + { + printf '%s\n' '### Guardrails coverage matrix' + printf '%s\n' '| Domain | Status | Notes |' + printf '%s\n' '|---|---|---|' + printf '%s\n' '| Access control | OK | Admin-only execution gate |' + printf '%s\n' '| Release variables | OK | Repository variables validation |' + printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |' + printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |' + printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |' + if [ "${extended_enabled}" = 'true' ]; then + if [ "${#extended_findings[@]}" -gt 0 ]; then + printf '%s\n' '| Extended checks | Warning | See extended findings below |' + else + printf '%s\n' '| Extended checks | OK | No findings |' + fi + else + printf '%s\n' '| Extended checks | SKIPPED | EXTENDED_CHECKS disabled |' + fi + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + + if [ "${extended_enabled}" = 'true' ] && [ "${#extended_findings[@]}" -gt 0 ]; then + { + printf '%s\n' '### Extended findings (advisory)' + for f in "${extended_findings[@]}"; do printf '%s\n' "- ${f}"; done + printf '\n' + } >> "${GITHUB_STEP_SUMMARY}" + fi + + printf '%s\n' 'Repository health guardrails passed.' >> "${GITHUB_STEP_SUMMARY}" + + + site-health: + name: Site Health + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + + - name: Uptime check + if: env.URLS != '' + run: | + echo "$URLS" > /tmp/urls.txt + php monitoring/uptime-probe.php --urls /tmp/urls.txt --timeout 15 || echo "::warning::Some sites are down" + rm -f /tmp/urls.txt + env: + URLS: ${{ vars.MONITORED_URLS }} + + - name: SSL certificate check + if: env.DOMAINS != '' + run: | + echo "$DOMAINS" > /tmp/domains.txt + php monitoring/ssl-check.php --domains /tmp/domains.txt --warn-days 30 || echo "::warning::SSL certificates expiring soon" + rm -f /tmp/domains.txt + env: + DOMAINS: ${{ vars.MONITORED_DOMAINS }} + + - name: Summary + if: always() + run: | + echo "### Site Health" >> $GITHUB_STEP_SUMMARY + echo "Uptime and SSL checks completed." >> $GITHUB_STEP_SUMMARY + + # ═══════════════════════════════════════════════════════════════════════ + # Issue Reporter — file issues for failed gates + # ═══════════════════════════════════════════════════════════════════════ + report-issues: + name: "Report Issues" + runs-on: ubuntu-latest + needs: [access_check, release_config, scripts_governance, repo_health] + if: >- + always() && + (needs.release_config.result == 'failure' || + needs.scripts_governance.result == 'failure' || + needs.repo_health.result == 'failure') + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: automation/ci-issue-reporter.sh + sparse-checkout-cone-mode: false + + - name: "File issues for failed gates" + env: + GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + run: | + chmod +x automation/ci-issue-reporter.sh + REPORTER="./automation/ci-issue-reporter.sh" + WF="Repo Health" + + report_gate() { + local gate="$1" result="$2" details="$3" + if [ "$result" = "failure" ]; then + "$REPORTER" --gate "$gate" --details "$details" --workflow "$WF" --severity error + fi + } + + report_gate "Release Configuration" \ + "${{ needs.release_config.result }}" \ + "Required repository variables are missing (RS_FTP_PATH_SUFFIX). Check repository settings." + + report_gate "Scripts Governance" \ + "${{ needs.scripts_governance.result }}" \ + "Scripts directory policy violations detected. Review required and allowed directories." + + report_gate "Repository Health" \ + "${{ needs.repo_health.result }}" \ + "Repository health checks failed — missing required artifacts, disallowed files, or content warnings. Check the CI run summary." + diff --git a/CHANGELOG.md b/CHANGELOG.md index 4db115d..f228f01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ - Remote upload integrated into BackupEngine as Step 3 after archive creation - Option to delete local copy after successful remote upload (per-profile setting) - Restore engine with file restoration and database import -- Standalone Kickstart restore script (restore.php) — self-contained site restoration without Joomla, like Akeeba Kickstart +- MokoRestore standalone restore script (restore.php) — self-contained site restoration without Joomla, - "Include Restore Script" toggle per profile — wraps backup with restore.php + site-backup.zip - FileRestorer class with protected file handling (preserves configuration.php, .htaccess) - DatabaseImporter with streaming line-by-line SQL execution and error tolerance @@ -28,7 +28,7 @@ - RestoreEngine auto-detects JPA vs ZIP format - AES-256 archive encryption with per-profile password (#17) - Encrypted archive support in RestoreEngine (password parameter) -- Encrypted archive support in Kickstart restore.php (password field in UI) +- Encrypted archive support in MokoRestore restore.php (password field in UI) - SHA-256 checksum computed and stored after archive creation (#15) - "Verify Integrity" toolbar button re-computes hash and compares against stored checksum - S3-compatible remote storage: AWS S3, Wasabi, Backblaze B2, MinIO (#16) diff --git a/src/packages/com_mokobackup/forms/profile.xml b/src/packages/com_mokobackup/forms/profile.xml index f712686..69bef05 100644 --- a/src/packages/com_mokobackup/forms/profile.xml +++ b/src/packages/com_mokobackup/forms/profile.xml @@ -70,10 +70,10 @@ maxlength="512" /> diff --git a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini index 468be17..dd9c1d0 100644 --- a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini +++ b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini @@ -75,8 +75,8 @@ COM_MOKOBACKUP_FIELD_SPLIT_SIZE="Split Size (MB)" COM_MOKOBACKUP_FIELD_SPLIT_SIZE_DESC="Split archive into parts of this size in MB. 0 = no splitting." COM_MOKOBACKUP_FIELD_BACKUP_DIR="Backup Directory" COM_MOKOBACKUP_FIELD_BACKUP_DIR_DESC="Relative path from Joomla root where backup archives are stored" -COM_MOKOBACKUP_FIELD_INCLUDE_KICKSTART="Include Restore Script" -COM_MOKOBACKUP_FIELD_INCLUDE_KICKSTART_DESC="Include a standalone restore.php inside the backup archive. This creates a self-contained package that can restore the site on a blank server without Joomla installed — like Akeeba Kickstart." +COM_MOKOBACKUP_FIELD_INCLUDE_MOKORESTORE="Include Restore Script" +COM_MOKOBACKUP_FIELD_INCLUDE_MOKORESTORE_DESC="Include MokoRestore (standalone restore.php) inside the backup archive. Creates a self-contained package that can restore the site on a blank server without Joomla installed." ; Exclusion filter fields COM_MOKOBACKUP_FIELD_EXCLUDE_DIRS="Exclude Directories" diff --git a/src/packages/com_mokobackup/sql/install.mysql.sql b/src/packages/com_mokobackup/sql/install.mysql.sql index fe7c580..b565a01 100644 --- a/src/packages/com_mokobackup/sql/install.mysql.sql +++ b/src/packages/com_mokobackup/sql/install.mysql.sql @@ -30,7 +30,7 @@ CREATE TABLE IF NOT EXISTS `#__mokobackup_profiles` ( `s3_path` VARCHAR(512) NOT NULL DEFAULT '/backups', `remote_keep_local` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Keep local copy after upload', `encryption_password` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'AES-256 archive encryption password (blank = no encryption)', - `include_kickstart` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Include standalone restore.php in archive', + `include_mokorestore` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Include MokoRestore standalone restore script in archive', `notify_email` VARCHAR(512) NOT NULL DEFAULT '' COMMENT 'Comma-separated notification emails', `notify_on_success` TINYINT(1) NOT NULL DEFAULT 0, `notify_on_failure` TINYINT(1) NOT NULL DEFAULT 1, diff --git a/src/packages/com_mokobackup/src/Engine/AkeebaImporter.php b/src/packages/com_mokobackup/src/Engine/AkeebaImporter.php index cafa191..3edbe14 100644 --- a/src/packages/com_mokobackup/src/Engine/AkeebaImporter.php +++ b/src/packages/com_mokobackup/src/Engine/AkeebaImporter.php @@ -246,7 +246,7 @@ class AkeebaImporter 's3_bucket' => $config['engine.postproc.s3.bucket'] ?? '', 's3_path' => $config['engine.postproc.s3.directory'] ?? '/backups', 'remote_keep_local' => 1, - 'include_kickstart' => (int) (($config['akeeba.advanced.embedded_installer'] ?? 'none') !== 'none'), + 'include_mokorestore' => (int) (($config['akeeba.advanced.embedded_installer'] ?? 'none') !== 'none'), 'published' => 1, 'ordering' => (int) $akProfile->id, 'created' => $now, diff --git a/src/packages/com_mokobackup/src/Engine/BackupEngine.php b/src/packages/com_mokobackup/src/Engine/BackupEngine.php index 923c6d4..60e0910 100644 --- a/src/packages/com_mokobackup/src/Engine/BackupEngine.php +++ b/src/packages/com_mokobackup/src/Engine/BackupEngine.php @@ -187,21 +187,21 @@ class BackupEngine $this->log('Archive created: ' . $sizeHuman); $this->log('SHA-256: ' . ($checksum ?: 'N/A')); - // Step 2.5: Wrap with Kickstart restore script (if enabled) - $includeKickstart = (bool) ($profile->include_kickstart ?? false); + // Step 2.5: Wrap with MokoRestore script (if enabled) + $includeMokoRestore = (bool) ($profile->include_mokorestore ?? false); - if ($includeKickstart) { - $this->log('Wrapping with Kickstart restore script...'); - $kickstartName = str_replace('.zip', '-kickstart.zip', $archiveName); - $kickstartPath = $this->backupDir . '/' . $kickstartName; - Kickstart::wrap($archivePath, $kickstartPath); + if ($includeMokoRestore) { + $this->log('Wrapping with MokoRestore script...'); + $mokoRestoreName = str_replace('.zip', '-mokorestore.zip', $archiveName); + $mokoRestorePath = $this->backupDir . '/' . $mokoRestoreName; + MokoRestore::wrap($archivePath, $mokoRestorePath); // Replace the original archive with the wrapped one @unlink($archivePath); - rename($kickstartPath, $archivePath); + rename($mokoRestorePath, $archivePath); $totalSize = filesize($archivePath); $sizeHuman = number_format($totalSize / 1048576, 2) . ' MB'; - $this->log('Kickstart archive created: ' . $sizeHuman); + $this->log('MokoRestore archive created: ' . $sizeHuman); } $remoteFilename = ''; diff --git a/src/packages/com_mokobackup/src/Engine/Kickstart.php b/src/packages/com_mokobackup/src/Engine/MokoRestore.php similarity index 98% rename from src/packages/com_mokobackup/src/Engine/Kickstart.php rename to src/packages/com_mokobackup/src/Engine/MokoRestore.php index e230c10..38c2c0a 100644 --- a/src/packages/com_mokobackup/src/Engine/Kickstart.php +++ b/src/packages/com_mokobackup/src/Engine/MokoRestore.php @@ -9,7 +9,7 @@ * * Standalone restore script generator. * - * When "Include Kickstart" is enabled on a profile, the backup archive + * When "Include MokoRestore" is enabled on a profile, the backup archive * is wrapped: * * outer.zip @@ -17,14 +17,14 @@ * └── site-backup.zip ← The actual site backup * * Upload outer.zip to a blank server, extract, open restore.php in a - * browser, and it handles everything — just like Akeeba Kickstart. + * browser, and it handles everything — self-contained site restoration. */ namespace Joomla\Component\MokoBackup\Administrator\Engine; defined('_JEXEC') or die; -class Kickstart +class MokoRestore { /** * Wrap a backup archive with the standalone restore script. @@ -39,7 +39,7 @@ class Kickstart $zip = new \ZipArchive(); if ($zip->open($outputPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { - throw new \RuntimeException('Cannot create kickstart archive: ' . $outputPath); + throw new \RuntimeException('Cannot create MokoRestore archive: ' . $outputPath); } // Add the standalone restore script @@ -68,7 +68,7 @@ class Kickstart return <<<'RESTORE_PHP' excludeTables = $this->parseNewlineList($profile->exclude_tables ?? ''); $session->backupDir = $profile->backup_dir ?: 'administrator/components/com_mokobackup/backups'; $session->remoteStorage = $profile->remote_storage ?? 'none'; - $session->includeKickstart = (bool) ($profile->include_kickstart ?? false); + $session->includeMokoRestore = (bool) ($profile->include_mokorestore ?? false); $session->remoteKeepLocal = (bool) ($profile->remote_keep_local ?? true); // Build archive path @@ -288,7 +288,7 @@ class SteppedBackupEngine } /** - * Finalize phase: add database.sql to ZIP, apply kickstart wrapper. + * Finalize phase: add database.sql to ZIP, apply MokoRestore wrapper. */ private function stepFinalize(SteppedSession $session): void { @@ -314,15 +314,15 @@ class SteppedBackupEngine $totalSize = file_exists($session->archivePath) ? filesize($session->archivePath) : 0; - // Kickstart wrapper - if ($session->includeKickstart) { - $session->log('Wrapping with Kickstart restore script...'); - $kickstartPath = $session->archivePath . '.kickstart.zip'; - Kickstart::wrap($session->archivePath, $kickstartPath); + // MokoRestore wrapper + if ($session->includeMokoRestore) { + $session->log('Wrapping with MokoRestore script...'); + $mokoRestorePath = $session->archivePath . '.mokorestore.zip'; + MokoRestore::wrap($session->archivePath, $mokoRestorePath); @unlink($session->archivePath); - rename($kickstartPath, $session->archivePath); + rename($mokoRestorePath, $session->archivePath); $totalSize = filesize($session->archivePath); - $session->log('Kickstart archive created'); + $session->log('MokoRestore archive created'); } // Update record diff --git a/src/packages/com_mokobackup/src/Engine/SteppedSession.php b/src/packages/com_mokobackup/src/Engine/SteppedSession.php index b4f3002..76f000d 100644 --- a/src/packages/com_mokobackup/src/Engine/SteppedSession.php +++ b/src/packages/com_mokobackup/src/Engine/SteppedSession.php @@ -51,7 +51,7 @@ class SteppedSession public array $excludeFiles = []; public array $excludeTables = []; public string $remoteStorage = 'none'; - public bool $includeKickstart = false; + public bool $includeMokoRestore = false; public bool $remoteKeepLocal = true; // Progress -- 2.52.0 From e5ca71f2c58f39e2c953ce9f645a6de4fc02b15e Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 07:40:04 -0500 Subject: [PATCH 04/81] chore: sync workflows from moko-platform + fix SQL install path - Sync all universal workflows from moko-platform v09.23.00 - Add pre-release.yml for dev/alpha/beta/rc builds - Add auto-bump.yml, branch-cleanup.yml, issue-branch.yml, update-server.yml - Fix mokobackup.xml: include install/uninstall SQL files in section Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/workflows/auto-bump.yml | 66 +++++ .mokogitea/workflows/auto-release.yml | 15 +- .mokogitea/workflows/branch-cleanup.yml | 48 ++++ .mokogitea/workflows/cleanup.yml | 2 +- .mokogitea/workflows/gitleaks.yml | 2 +- .mokogitea/workflows/issue-branch.yml | 73 +++++ .mokogitea/workflows/notify.yml | 2 +- .mokogitea/workflows/pre-release.yml | 224 +++++++++++++++ .mokogitea/workflows/repo-health.yml | 3 +- .mokogitea/workflows/security-audit.yml | 2 +- .mokogitea/workflows/update-server.yml | 302 +++++++++++++++++++++ src/packages/com_mokobackup/mokobackup.xml | 3 +- 12 files changed, 721 insertions(+), 21 deletions(-) create mode 100644 .mokogitea/workflows/auto-bump.yml create mode 100644 .mokogitea/workflows/branch-cleanup.yml create mode 100644 .mokogitea/workflows/issue-branch.yml create mode 100644 .mokogitea/workflows/pre-release.yml create mode 100644 .mokogitea/workflows/update-server.yml diff --git a/.mokogitea/workflows/auto-bump.yml b/.mokogitea/workflows/auto-bump.yml new file mode 100644 index 0000000..33aff71 --- /dev/null +++ b/.mokogitea/workflows/auto-bump.yml @@ -0,0 +1,66 @@ +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /.mokogitea/workflows/auto-bump.yml +# VERSION: 09.23.00 +# BRIEF: Auto patch-bump version on every push to dev (skips merge commits) + +name: "Universal: Auto Version Bump" + +on: + push: + branches: + - dev + - rc + - 'feature/**' + - 'patch/**' + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + +permissions: + contents: write + +jobs: + bump: + name: Version Bump + runs-on: release + if: >- + !contains(github.event.head_commit.message, '[skip ci]') && + !contains(github.event.head_commit.message, '[skip bump]') && + !startsWith(github.event.head_commit.message, 'Merge pull request') + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 1 + + - name: Setup moko-platform tools + run: | + if ! command -v composer &> /dev/null; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + fi + if [ -d "/opt/moko-platform/cli" ]; then + echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV" + else + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet + echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV" + fi + + - name: Bump version + run: | + php ${MOKO_CLI}/version_auto_bump.php \ + --path . --branch "${GITHUB_REF_NAME}" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 78dec4b..6fb2b44 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -7,7 +7,7 @@ # INGROUP: moko-platform.Release # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # PATH: /templates/workflows/universal/auto-release.yml.template -# VERSION: 05.00.00 +# VERSION: 09.23.00 # BRIEF: Universal build & release � detects platform from manifest.xml # # +========================================================================+ @@ -131,19 +131,6 @@ jobs: git config --local user.name "gitea-actions[bot]" git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - name: Check for merge conflict markers - run: | - CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) - if [ -n "$CONFLICTS" ]; then - echo "::error::Merge conflict markers found — aborting release" - echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - exit 1 - fi - echo "No conflict markers found" - - name: Setup moko-platform tools env: MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} diff --git a/.mokogitea/workflows/branch-cleanup.yml b/.mokogitea/workflows/branch-cleanup.yml new file mode 100644 index 0000000..67a735f --- /dev/null +++ b/.mokogitea/workflows/branch-cleanup.yml @@ -0,0 +1,48 @@ +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: MokoPlatform.Universal +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /.mokogitea/workflows/branch-cleanup.yml +# VERSION: 09.23.00 +# BRIEF: Delete feature branches after PR merge + +name: "Branch Cleanup" + +on: + pull_request: + types: [closed] + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + cleanup: + name: Delete merged branch + runs-on: ubuntu-latest + if: >- + github.event.pull_request.merged == true && + github.event.pull_request.head.ref != 'dev' && + github.event.pull_request.head.ref != 'main' + + steps: + - name: Delete source branch + run: | + BRANCH="${{ github.event.pull_request.head.ref }}" + API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches" + ENCODED=$(php -r "echo rawurlencode('${BRANCH}');") + + STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \ + -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API}/${ENCODED}" 2>/dev/null || true) + + if [ "$STATUS" = "204" ]; then + echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + elif [ "$STATUS" = "404" ]; then + echo "Branch already deleted: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + else + echo "::warning::Failed to delete branch ${BRANCH} (HTTP ${STATUS})" + fi diff --git a/.mokogitea/workflows/cleanup.yml b/.mokogitea/workflows/cleanup.yml index 29ca4d4..70521b3 100644 --- a/.mokogitea/workflows/cleanup.yml +++ b/.mokogitea/workflows/cleanup.yml @@ -7,7 +7,7 @@ # INGROUP: moko-platform.Maintenance # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/cleanup.yml -# VERSION: 01.00.00 +# VERSION: 09.23.00 # BRIEF: Scheduled cleanup — delete merged branches and old workflow runs name: "Universal: Repository Cleanup" diff --git a/.mokogitea/workflows/gitleaks.yml b/.mokogitea/workflows/gitleaks.yml index e0fdd1d..9126c91 100644 --- a/.mokogitea/workflows/gitleaks.yml +++ b/.mokogitea/workflows/gitleaks.yml @@ -7,7 +7,7 @@ # INGROUP: moko-platform.Security # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # PATH: /templates/workflows/gitleaks.yml.template -# VERSION: 01.00.00 +# VERSION: 09.23.00 # BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens # # +========================================================================+ diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml new file mode 100644 index 0000000..3c44a06 --- /dev/null +++ b/.mokogitea/workflows/issue-branch.yml @@ -0,0 +1,73 @@ +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Automation +# VERSION: 09.23.00 +# BRIEF: Auto-create feature branch when an issue is opened + +name: "Universal: Issue Branch" + +on: + issues: + types: [opened] + +permissions: + contents: write + issues: write + +env: + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + +jobs: + create-branch: + name: Create feature branch + runs-on: ubuntu-latest + steps: + - name: Create branch and comment + run: | + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" + ISSUE_NUM="${{ github.event.issue.number }}" + ISSUE_TITLE="${{ github.event.issue.title }}" + + # Build slug from title: lowercase, replace non-alnum with dash, trim + SLUG=$(echo "${ISSUE_TITLE}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | cut -c1-40) + BRANCH="feature/${ISSUE_NUM}-${SLUG}" + + # Check dev branch exists + DEV_EXISTS=$(curl -sf -o /dev/null -w '%{http_code}' \ + -H "Authorization: token ${TOKEN}" \ + "${API}/branches/dev" 2>/dev/null || echo "000") + + if [ "${DEV_EXISTS}" != "200" ]; then + echo "No dev branch -- skipping" + exit 0 + fi + + # Create branch from dev + HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \ + -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${API}/branches" \ + -d "{\"new_branch_name\":\"${BRANCH}\",\"old_branch_name\":\"dev\"}" 2>/dev/null || echo "000") + + if [ "${HTTP}" = "201" ]; then + echo "Created branch: ${BRANCH}" + + # Comment on issue with branch link + REPO_URL="${GITEA_URL}/${{ github.repository }}" + BODY="Branch created: [\`${BRANCH}\`](${REPO_URL}/src/branch/${BRANCH})\n\n\`\`\`bash\ngit fetch origin\ngit checkout ${BRANCH}\n\`\`\`" + + curl -sf -X POST \ + -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${API}/issues/${ISSUE_NUM}/comments" \ + -d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1 + + echo "Commented on issue #${ISSUE_NUM}" + else + echo "Failed to create branch (HTTP ${HTTP}) -- may already exist" + fi diff --git a/.mokogitea/workflows/notify.yml b/.mokogitea/workflows/notify.yml index cde4541..c18b809 100644 --- a/.mokogitea/workflows/notify.yml +++ b/.mokogitea/workflows/notify.yml @@ -7,7 +7,7 @@ # INGROUP: moko-platform.Notifications # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/notify.yml -# VERSION: 01.00.00 +# VERSION: 09.23.00 # BRIEF: Push notifications via ntfy on release success or workflow failure name: "Universal: Notifications" diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml new file mode 100644 index 0000000..ff818ba --- /dev/null +++ b/.mokogitea/workflows/pre-release.yml @@ -0,0 +1,224 @@ +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /templates/workflows/universal/pre-release.yml.template +# VERSION: 09.23.00 +# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch + +name: "Universal: Pre-Release" + +on: + pull_request: + types: [closed] + branches: + - dev + workflow_dispatch: + inputs: + stability: + description: 'Pre-release channel' + required: true + type: choice + options: + - development + - alpha + - beta + - release-candidate + +permissions: + contents: write + +env: + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} + GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} + +jobs: + build: + name: "Build Pre-Release (${{ inputs.stability || 'development' }})" + runs-on: release + if: >- + github.event_name == 'workflow_dispatch' || + (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.MOKOGITEA_TOKEN }} + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + if ! command -v composer &> /dev/null; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + fi + # Always fetch latest CLI tools — never use stale cache from previous runs + rm -rf /tmp/moko-platform-api + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet + echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV" + + - name: Detect platform + id: platform + run: | + php ${MOKO_CLI}/manifest_read.php --path . --github-output + + - name: Resolve metadata and bump version + id: meta + run: | + STABILITY="${{ inputs.stability || 'development' }}" + + case "$STABILITY" in + development) TAG="development" ;; + alpha) TAG="alpha" ;; + beta) TAG="beta" ;; + release-candidate) TAG="release-candidate" ;; + esac + + # Set stability suffix, bump preserves it, fix consistency + php ${MOKO_CLI}/version_set_platform.php \ + --path . --version "$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo '00.00.01')" \ + --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true + php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true + + # Read final version (includes suffix, e.g. 01.02.15-dev) + VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) + [ -z "$VERSION" ] && VERSION="00.00.01" + + # Commit version bump + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + git add -A + git diff --cached --quiet || { + git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" + git push origin HEAD 2>&1 + } + + # Auto-detect element via manifest_element.php + php ${MOKO_CLI}/manifest_element.php \ + --path . --version "$VERSION" --stability "$STABILITY" \ + --repo "${GITEA_REPO}" --github-output + + # Read back element outputs + EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') + [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" + + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" + echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" + + echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION} ===" + + - name: Create release + id: release + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_create.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --branch dev --prerelease + + - name: Build package and upload + id: package + run: | + VERSION="${{ steps.meta.outputs.version }}" + TAG="${{ steps.meta.outputs.tag }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_package.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --output /tmp || true + + - name: Update updates.xml + if: steps.platform.outputs.platform == 'joomla' + run: | + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + SHA256="${{ steps.package.outputs.sha256_zip }}" + + if [ ! -f "updates.xml" ]; then + echo "No updates.xml -- skipping" + exit 0 + fi + + SHA_FLAG="" + [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" + + php ${MOKO_CLI}/updates_xml_build.php \ + --path . --version "${VERSION}" --stability "${STABILITY}" \ + --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ + ${SHA_FLAG} + + # Commit and push + if ! git diff --quiet updates.xml 2>/dev/null; then + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git add updates.xml + git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" + git push origin HEAD 2>&1 || echo "WARNING: push failed" + fi + + - name: "Sync updates.xml to all branches" + if: steps.platform.outputs.platform == 'joomla' + run: | + CURRENT_BRANCH="${{ github.ref_name }}" + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + + for BRANCH in main dev; do + [ "$BRANCH" = "$CURRENT_BRANCH" ] && continue + echo "Syncing updates.xml -> ${BRANCH}" + git fetch origin "${BRANCH}" 2>/dev/null || continue + git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue + git checkout "${CURRENT_BRANCH}" -- updates.xml + if ! git diff --quiet updates.xml 2>/dev/null; then + git add updates.xml + git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]" + git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed" + fi + git checkout "${CURRENT_BRANCH}" 2>/dev/null + done + + - name: "Delete lesser pre-release channels (cascade)" + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + php ${MOKO_CLI}/release_cascade.php \ + --stability "${{ steps.meta.outputs.stability }}" \ + --token "${TOKEN}" \ + --api-base "${API_BASE}" + + - name: Summary + if: always() + run: | + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + ZIP_NAME="${{ steps.meta.outputs.zip_name }}" + SHA256="${{ steps.package.outputs.sha256_zip }}" + echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY + echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY + echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY diff --git a/.mokogitea/workflows/repo-health.yml b/.mokogitea/workflows/repo-health.yml index 03290b0..e3653eb 100644 --- a/.mokogitea/workflows/repo-health.yml +++ b/.mokogitea/workflows/repo-health.yml @@ -41,8 +41,7 @@ permissions: env: # Release policy - Repository Variables Only - # RS_FTP_PATH_SUFFIX removed — MokoGitea handles all releases now - RELEASE_REQUIRED_REPO_VARS: + RELEASE_REQUIRED_REPO_VARS: RS_FTP_PATH_SUFFIX RELEASE_OPTIONAL_REPO_VARS: DEV_FTP_SUFFIX # Scripts governance policy diff --git a/.mokogitea/workflows/security-audit.yml b/.mokogitea/workflows/security-audit.yml index 714d407..1bd9470 100644 --- a/.mokogitea/workflows/security-audit.yml +++ b/.mokogitea/workflows/security-audit.yml @@ -7,7 +7,7 @@ # INGROUP: moko-platform.Security # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/security-audit.yml -# VERSION: 01.00.00 +# VERSION: 09.23.00 # BRIEF: Dependency vulnerability scanning for composer and npm packages name: "Universal: Security Audit" diff --git a/.mokogitea/workflows/update-server.yml b/.mokogitea/workflows/update-server.yml new file mode 100644 index 0000000..ac5c9a5 --- /dev/null +++ b/.mokogitea/workflows/update-server.yml @@ -0,0 +1,302 @@ +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Universal +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /templates/workflows/update-server.yml +# VERSION: 09.23.00 +# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches +# +# Thin wrapper around moko-platform CLI tools. +# Builds packages, updates updates.xml, and optionally deploys via SFTP. +# +# Joomla filters update entries by the user's "Minimum Stability" setting. + +name: "Update Server" + +on: + push: + branches: + - 'dev' + - 'dev/**' + - 'alpha/**' + - 'beta/**' + - 'rc/**' + paths: + - 'src/**' + - 'htdocs/**' + pull_request: + types: [closed] + branches: + - 'dev' + - 'dev/**' + - 'alpha/**' + - 'beta/**' + - 'rc/**' + paths: + - 'src/**' + - 'htdocs/**' + workflow_dispatch: + inputs: + stability: + description: 'Stability tag' + required: true + default: 'development' + type: choice + options: + - development + - alpha + - beta + - rc + - stable + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} + GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} + +permissions: + contents: write + +jobs: + update-xml: + name: Update Server + runs-on: release + if: >- + github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push' + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 0 + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}' + run: | + if ! command -v composer &> /dev/null; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + fi + # Always fetch latest CLI tools — never use stale cache from previous runs + rm -rf /tmp/moko-platform + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform 2>/dev/null || true + if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then + cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true + fi + echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV" + + - name: Detect platform + id: platform + run: php ${MOKO_CLI}/manifest_read.php --path . --github-output + + - name: Resolve stability and bump version + id: meta + run: | + BRANCH="${{ github.ref_name }}" + + # Configure git for bot pushes + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + + # Determine stability from branch or manual input + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + STABILITY="${{ inputs.stability }}" + elif [[ "$BRANCH" == rc/* ]]; then + STABILITY="rc" + elif [[ "$BRANCH" == beta/* ]]; then + STABILITY="beta" + elif [[ "$BRANCH" == alpha/* ]]; then + STABILITY="alpha" + else + STABILITY="development" + fi + + # Gitea release tag per stability + case "$STABILITY" in + development) TAG="development" ;; + alpha) TAG="alpha" ;; + beta) TAG="beta" ;; + rc) TAG="release-candidate" ;; + *) TAG="stable" ;; + esac + + # Bump patch, set platform suffix, fix consistency — version_bump preserves suffix + php ${MOKO_CLI}/version_set_platform.php \ + --path . --version "$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo '00.00.01')" \ + --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true + php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true + php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true + + # Read final version (includes suffix, e.g. 01.02.15-dev) + VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") + + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + + # Commit version bump if changed + git add -A + git diff --cached --quiet || { + git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \ + --author="gitea-actions[bot] " + git push + } + + - name: Create release and upload package + id: package + run: | + VERSION="${{ steps.meta.outputs.version }}" + TAG="${{ steps.meta.outputs.tag }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Create or update Gitea release + php ${MOKO_CLI}/release_create.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease + + # Build package and upload + php ${MOKO_CLI}/release_package.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --output /tmp || true + + - name: Update updates.xml + if: steps.platform.outputs.platform == 'joomla' + run: | + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + SHA256="${{ steps.package.outputs.sha256_zip }}" + + if [ ! -f "updates.xml" ]; then + echo "No updates.xml — skipping" + exit 0 + fi + + SHA_FLAG="" + [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" + + php ${MOKO_CLI}/updates_xml_build.php \ + --path . --version "${VERSION}" --stability "${STABILITY}" \ + --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ + ${SHA_FLAG} + + # Commit and push updates.xml + git add updates.xml + git diff --cached --quiet || { + git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" + git push + } + + - name: Sync updates.xml to main + if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla' + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \ + "${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true) + + if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then + python3 -c " + import base64, json, urllib.request, sys + with open('updates.xml', 'rb') as f: + content = base64.b64encode(f.read()).decode() + payload = json.dumps({ + 'content': content, + 'sha': '${FILE_SHA}', + 'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]', + 'branch': 'main' + }).encode() + req = urllib.request.Request( + '${API_BASE}/contents/updates.xml', + data=payload, method='PUT', + headers={ + 'Authorization': 'token ${GITEA_TOKEN}', + 'Content-Type': 'application/json' + }) + try: + urllib.request.urlopen(req) + print('updates.xml synced to main') + except Exception as e: + print(f'WARNING: sync to main failed: {e}', file=sys.stderr) + " + fi + + - name: SFTP deploy to dev server + if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev' + env: + DEV_HOST: ${{ vars.DEV_FTP_HOST }} + DEV_PATH: ${{ vars.DEV_FTP_PATH }} + DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} + DEV_USER: ${{ vars.DEV_FTP_USERNAME }} + DEV_PORT: ${{ vars.DEV_FTP_PORT }} + DEV_KEY: ${{ secrets.DEV_FTP_KEY }} + DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }} + run: | + # Permission check: admin or maintain role required + ACTOR="${{ github.actor }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \ + python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read") + case "$PERMISSION" in + admin|maintain|write) ;; + *) + echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write" + exit 0 + ;; + esac + + [ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; } + + SOURCE_DIR="src" + [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" + [ ! -d "$SOURCE_DIR" ] && exit 0 + + PORT="${DEV_PORT:-22}" + REMOTE="${DEV_PATH%/}" + [ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}" + + printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \ + "$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json + if [ -n "$DEV_KEY" ]; then + echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key + printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json + else + printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json + fi + + PLATFORM=$(php ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true) + if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then + php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json + elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then + php ${MOKO_CLI}/../deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json + fi + rm -f /tmp/deploy_key /tmp/sftp-config.json + echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY + + - name: Summary + if: always() + run: | + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + DISPLAY="${VERSION}" + echo "## Update Server" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 4711c60..a4ab7ec 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -64,7 +64,8 @@ profile - mysql + install.mysql.sql + uninstall.mysql.sql updates -- 2.52.0 From 1f225689babf3a269c15503dc067686e82c11cdf Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 12:40:59 +0000 Subject: [PATCH 05/81] chore(version): auto-bump 01.01.01-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- src/packages/com_mokobackup/mokobackup.xml | 2 +- src/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- src/packages/plg_system_mokobackup/mokobackup.xml | 2 +- src/packages/plg_task_mokobackup/mokobackup.xml | 2 +- src/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- src/pkg_mokobackup.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 5ef8494..1b87e71 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.00-dev + 01.01.01-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 3c44a06..c5182e8 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 09.23.00 +# VERSION: 01.01.01 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index ec51852..c999d0f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index a4ab7ec..3c2484a 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.00-dev + 01.01.01-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/src/packages/plg_quickicon_mokobackup/mokobackup.xml index 5a55e79..ae56eeb 100644 --- a/src/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/src/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.00-dev + 01.01.01-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/src/packages/plg_system_mokobackup/mokobackup.xml index 1f1b1eb..681ccd3 100644 --- a/src/packages/plg_system_mokobackup/mokobackup.xml +++ b/src/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.00-dev + 01.01.01-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml index ab99004..fc31566 100644 --- a/src/packages/plg_task_mokobackup/mokobackup.xml +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.00-dev + 01.01.01-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/src/packages/plg_webservices_mokobackup/mokobackup.xml index e54426a..f04a3a3 100644 --- a/src/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/src/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.00-dev + 01.01.01-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index 2de7a14..9ae5d7a 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.00-dev + 01.01.01-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 3d88281e72ee38c40740a8100d798f36b5834302 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 12:41:02 +0000 Subject: [PATCH 06/81] chore: update development channel 01.01.01-dev [skip ci] --- updates.xml | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/updates.xml b/updates.xml index 5ba8cf1..daf6dce 100644 --- a/updates.xml +++ b/updates.xml @@ -1,15 +1,27 @@ - + + + - - Package - MokoJoomBackup - Full-site backup and restore for Joomla - mokobackup - package - 01.00.00 - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/v01.00.00 - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/v01.00.00/pkg_mokobackup-01.00.00.zip - - - 8.1.0 - + + Package - MokoJoomBackup + Package - MokoJoomBackup development build. + pkg_mokobackup + package + site + 01.01.01-dev + 2026-06-04 + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/development + + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.01-dev.zip + + 4c2702d04479863bbffef705f55fe10f8ffbe01c598d9fb4b410bb117fac1ff2 + dev + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md + Moko Consulting + https://mokoconsulting.tech + + -- 2.52.0 From 6ed0eee4a1849e2fce3b8d4a7ad2d9e3aafbb590 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 08:01:05 -0500 Subject: [PATCH 07/81] feat: add update site notice on dashboard and post-install Link directly to the Joomla Update Sites record for pkg_mokobackup on the Backups dashboard and after install/update, so users can configure their download key without a license warning popup. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../language/en-GB/com_mokobackup.ini | 5 ++ .../language/en-US/com_mokobackup.ini | 3 ++ .../src/View/Backups/HtmlView.php | 43 ++++++++++++++++ src/script.php | 50 +++++++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini index dd9c1d0..0921f20 100644 --- a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini +++ b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini @@ -189,6 +189,11 @@ COM_MOKOBACKUP_FIELD_S3_PATH_DESC="Optional path prefix inside the bucket (e.g. COM_MOKOBACKUP_TOOLBAR_IMPORT_AKEEBA="Import from Akeeba" COM_MOKOBACKUP_AKEEBA_NOT_FOUND="Akeeba Backup tables not found. Is Akeeba Backup Pro installed?" +; Update site notice +COM_MOKOBACKUP_UPDATE_SITE_NOTICE="To receive automatic updates, configure your Update Site with your download key." +COM_MOKOBACKUP_UPDATE_SITE_MISSING="MokoJoomBackup update site not found. Reinstall the package to register the update server." +COM_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." + ; Errors COM_MOKOBACKUP_ERROR_FILE_NOT_FOUND="Backup archive file not found or has been deleted." COM_MOKOBACKUP_ERROR_NO_RECORD_SELECTED="No backup record selected for restore." diff --git a/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini b/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini index 2f000f1..309264d 100644 --- a/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini +++ b/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini @@ -13,3 +13,6 @@ COM_MOKOBACKUP_PROFILES_TITLE="Backup Profiles" COM_MOKOBACKUP_TOOLBAR_BACKUP_NOW="Backup Now" COM_MOKOBACKUP_NO_BACKUPS="No backups found. Click 'Backup Now' to create your first backup." COM_MOKOBACKUP_NO_PROFILES="No backup profiles found." +COM_MOKOBACKUP_UPDATE_SITE_NOTICE="To receive automatic updates, configure your Update Site with your download key." +COM_MOKOBACKUP_UPDATE_SITE_MISSING="MokoJoomBackup update site not found. Reinstall the package to register the update server." +COM_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." diff --git a/src/packages/com_mokobackup/src/View/Backups/HtmlView.php b/src/packages/com_mokobackup/src/View/Backups/HtmlView.php index 1dde363..b6fdf9f 100644 --- a/src/packages/com_mokobackup/src/View/Backups/HtmlView.php +++ b/src/packages/com_mokobackup/src/View/Backups/HtmlView.php @@ -15,6 +15,7 @@ defined('_JEXEC') or die; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use Joomla\CMS\Router\Route; use Joomla\CMS\Toolbar\ToolbarHelper; class HtmlView extends BaseHtmlView @@ -44,11 +45,53 @@ class HtmlView extends BaseHtmlView $db->setQuery($query); $this->profiles = $db->loadObjectList() ?: []; + $this->checkUpdateSite(); $this->addToolbar(); parent::display($tpl); } + /** + * Show an info notice linking to the update site record so the user + * can configure their download key for automatic updates. + */ + protected function checkUpdateSite(): void + { + $db = Factory::getDbo(); + + // Find the update site ID linked to pkg_mokobackup + $query = $db->getQuery(true) + ->select($db->quoteName('us.update_site_id')) + ->from($db->quoteName('#__update_sites', 'us')) + ->join( + 'INNER', + $db->quoteName('#__update_sites_extensions', 'use') + . ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id') + ) + ->join( + 'INNER', + $db->quoteName('#__extensions', 'e') + . ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id') + ) + ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokobackup')) + ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) + ->setLimit(1); + + $db->setQuery($query); + $updateSiteId = (int) $db->loadResult(); + + if ($updateSiteId > 0) { + $editUrl = Route::_( + 'index.php?option=com_installer&view=updatesites&task=updatesite.edit&id=' . $updateSiteId + ); + + Factory::getApplication()->enqueueMessage( + Text::sprintf('COM_MOKOBACKUP_UPDATE_SITE_NOTICE', $editUrl), + 'info' + ); + } + } + protected function addToolbar(): void { ToolbarHelper::title(Text::_('COM_MOKOBACKUP_BACKUPS_TITLE'), 'database'); diff --git a/src/script.php b/src/script.php index ed0db68..ea6ed40 100644 --- a/src/script.php +++ b/src/script.php @@ -12,6 +12,7 @@ defined('_JEXEC') or die; use Joomla\CMS\Factory; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Text; +use Joomla\CMS\Router\Route; class Pkg_MokoBackupInstallerScript { @@ -118,5 +119,54 @@ class Pkg_MokoBackupInstallerScript file_put_contents($backupDir . '/index.html', ''); } } + + // Show update site link after install or update + $this->showUpdateSiteNotice(); + } + + /** + * Show an info message linking directly to the update site record + * so the user can configure their download key. + * + * @return void + */ + private function showUpdateSiteNotice(): void + { + try { + $db = Factory::getDbo(); + + $query = $db->getQuery(true) + ->select($db->quoteName('us.update_site_id')) + ->from($db->quoteName('#__update_sites', 'us')) + ->join( + 'INNER', + $db->quoteName('#__update_sites_extensions', 'use') + . ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id') + ) + ->join( + 'INNER', + $db->quoteName('#__extensions', 'e') + . ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id') + ) + ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokobackup')) + ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) + ->setLimit(1); + + $db->setQuery($query); + $updateSiteId = (int) $db->loadResult(); + + if ($updateSiteId > 0) { + $editUrl = Route::_( + 'index.php?option=com_installer&view=updatesites&task=updatesite.edit&id=' . $updateSiteId + ); + + Factory::getApplication()->enqueueMessage( + Text::sprintf('COM_MOKOBACKUP_POSTINSTALL_UPDATE_SITE', $editUrl), + 'info' + ); + } + } catch (\Throwable $e) { + // Non-critical — silently ignore + } } } -- 2.52.0 From 44e309c57f7690cbc76e43a815fee3b7e51a0d82 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 13:02:04 +0000 Subject: [PATCH 08/81] chore(version): auto-bump 01.01.02-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- src/packages/com_mokobackup/mokobackup.xml | 2 +- src/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- src/packages/plg_system_mokobackup/mokobackup.xml | 2 +- src/packages/plg_task_mokobackup/mokobackup.xml | 2 +- src/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- src/pkg_mokobackup.xml | 2 +- updates.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 1b87e71..d603390 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.01-dev + 01.01.02-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index c5182e8..96d0c2a 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.01 +# VERSION: 01.01.02 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index c999d0f..edb0f31 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 3c2484a..12c60e3 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.01-dev + 01.01.02-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/src/packages/plg_quickicon_mokobackup/mokobackup.xml index ae56eeb..4f7b287 100644 --- a/src/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/src/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.01-dev + 01.01.02-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/src/packages/plg_system_mokobackup/mokobackup.xml index 681ccd3..776b919 100644 --- a/src/packages/plg_system_mokobackup/mokobackup.xml +++ b/src/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.01-dev + 01.01.02-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml index fc31566..85c68af 100644 --- a/src/packages/plg_task_mokobackup/mokobackup.xml +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.01-dev + 01.01.02-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/src/packages/plg_webservices_mokobackup/mokobackup.xml index f04a3a3..31cabb6 100644 --- a/src/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/src/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.01-dev + 01.01.02-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index 9ae5d7a..9aec17f 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.01-dev + 01.01.02-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/updates.xml b/updates.xml index daf6dce..6fb1ca9 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ -- 2.52.0 From 5ee4f7a578c628eb58fb8ee9e4349062bea0787d Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 13:02:06 +0000 Subject: [PATCH 09/81] chore: update development channel 01.01.02-dev [skip ci] --- updates.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/updates.xml b/updates.xml index 6fb1ca9..deb391e 100644 --- a/updates.xml +++ b/updates.xml @@ -11,13 +11,13 @@ pkg_mokobackup package site - 01.01.01-dev + 01.01.02-dev 2026-06-04 https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.01-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.02-dev.zip - 4c2702d04479863bbffef705f55fe10f8ffbe01c598d9fb4b410bb117fac1ff2 + 9b4c337c41439dfb8292d6817951b2bb51a8015e208afdbe92444a0cb52214c0 dev https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md Moko Consulting -- 2.52.0 From 6bab1ad5fa788f01ccad67189758a9ec7bbc1a31 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 08:15:31 -0500 Subject: [PATCH 10/81] fix: add SQL update migration and error handling for PR review - Add sql/updates/mysql/01.01.01.sql to rename include_kickstart column to include_mokorestore for existing installations - Wrap checkUpdateSite() in try/catch to prevent dashboard crash - Use COM_MOKOBACKUP_UPDATE_SITE_MISSING when no update site found Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../sql/updates/mysql/01.01.01.sql | 1 + .../src/View/Backups/HtmlView.php | 65 +++++++++++-------- 2 files changed, 38 insertions(+), 28 deletions(-) create mode 100644 src/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql diff --git a/src/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql b/src/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql new file mode 100644 index 0000000..ef33f11 --- /dev/null +++ b/src/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql @@ -0,0 +1 @@ +ALTER TABLE `#__mokobackup_profiles` CHANGE `include_kickstart` `include_mokorestore` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Include MokoRestore standalone restore script in archive'; diff --git a/src/packages/com_mokobackup/src/View/Backups/HtmlView.php b/src/packages/com_mokobackup/src/View/Backups/HtmlView.php index b6fdf9f..f1a224c 100644 --- a/src/packages/com_mokobackup/src/View/Backups/HtmlView.php +++ b/src/packages/com_mokobackup/src/View/Backups/HtmlView.php @@ -57,38 +57,47 @@ class HtmlView extends BaseHtmlView */ protected function checkUpdateSite(): void { - $db = Factory::getDbo(); + try { + $db = Factory::getDbo(); - // Find the update site ID linked to pkg_mokobackup - $query = $db->getQuery(true) - ->select($db->quoteName('us.update_site_id')) - ->from($db->quoteName('#__update_sites', 'us')) - ->join( - 'INNER', - $db->quoteName('#__update_sites_extensions', 'use') - . ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id') - ) - ->join( - 'INNER', - $db->quoteName('#__extensions', 'e') - . ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id') - ) - ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokobackup')) - ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) - ->setLimit(1); + // Find the update site ID linked to pkg_mokobackup + $query = $db->getQuery(true) + ->select($db->quoteName('us.update_site_id')) + ->from($db->quoteName('#__update_sites', 'us')) + ->join( + 'INNER', + $db->quoteName('#__update_sites_extensions', 'use') + . ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id') + ) + ->join( + 'INNER', + $db->quoteName('#__extensions', 'e') + . ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id') + ) + ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokobackup')) + ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) + ->setLimit(1); - $db->setQuery($query); - $updateSiteId = (int) $db->loadResult(); + $db->setQuery($query); + $updateSiteId = (int) $db->loadResult(); - if ($updateSiteId > 0) { - $editUrl = Route::_( - 'index.php?option=com_installer&view=updatesites&task=updatesite.edit&id=' . $updateSiteId - ); + if ($updateSiteId > 0) { + $editUrl = Route::_( + 'index.php?option=com_installer&view=updatesites&task=updatesite.edit&id=' . $updateSiteId + ); - Factory::getApplication()->enqueueMessage( - Text::sprintf('COM_MOKOBACKUP_UPDATE_SITE_NOTICE', $editUrl), - 'info' - ); + Factory::getApplication()->enqueueMessage( + Text::sprintf('COM_MOKOBACKUP_UPDATE_SITE_NOTICE', $editUrl), + 'info' + ); + } else { + Factory::getApplication()->enqueueMessage( + Text::_('COM_MOKOBACKUP_UPDATE_SITE_MISSING'), + 'warning' + ); + } + } catch (\Throwable $e) { + // Non-critical — silently ignore } } -- 2.52.0 From 2aee667d00d84f05d5a55376adedef4175bcaaac Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 13:16:01 +0000 Subject: [PATCH 11/81] chore(version): auto-bump 01.01.03-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- src/packages/com_mokobackup/mokobackup.xml | 2 +- src/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- src/packages/plg_system_mokobackup/mokobackup.xml | 2 +- src/packages/plg_task_mokobackup/mokobackup.xml | 2 +- src/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- src/pkg_mokobackup.xml | 2 +- updates.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index d603390..07f0731 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.02-dev + 01.01.03-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 96d0c2a..cc5f056 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.02 +# VERSION: 01.01.03 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index edb0f31..5a5b12d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 12c60e3..43a744f 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.02-dev + 01.01.03-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/src/packages/plg_quickicon_mokobackup/mokobackup.xml index 4f7b287..eaa988a 100644 --- a/src/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/src/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.02-dev + 01.01.03-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/src/packages/plg_system_mokobackup/mokobackup.xml index 776b919..a8d2c3b 100644 --- a/src/packages/plg_system_mokobackup/mokobackup.xml +++ b/src/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.02-dev + 01.01.03-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml index 85c68af..063445a 100644 --- a/src/packages/plg_task_mokobackup/mokobackup.xml +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.02-dev + 01.01.03-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/src/packages/plg_webservices_mokobackup/mokobackup.xml index 31cabb6..376fba1 100644 --- a/src/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/src/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.02-dev + 01.01.03-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index 9aec17f..c206ba4 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.02-dev + 01.01.03-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/updates.xml b/updates.xml index deb391e..55c0958 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ -- 2.52.0 From 70df427cfeb49dceb967b6d9af3eb70023f1f9e4 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 13:16:02 +0000 Subject: [PATCH 12/81] chore: update development channel 01.01.03-dev [skip ci] --- updates.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/updates.xml b/updates.xml index 55c0958..8dd2da5 100644 --- a/updates.xml +++ b/updates.xml @@ -11,13 +11,13 @@ pkg_mokobackup package site - 01.01.02-dev + 01.01.03-dev 2026-06-04 https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.02-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.03-dev.zip - 9b4c337c41439dfb8292d6817951b2bb51a8015e208afdbe92444a0cb52214c0 + 3c28f5726d85769c0aa6ac7b686f1ae2a5c3eacdf72fd4dbb5d6c722d93a47fc dev https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md Moko Consulting -- 2.52.0 From 8eab341c1ea5ff03492b5b4bc368e8cf7b2f14db Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 13:17:49 +0000 Subject: [PATCH 13/81] chore(version): pre-release bump to 01.01.03-dev [skip ci] --- src/packages/com_mokobackup/mokobackup.xml | 2 +- src/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- src/packages/plg_system_mokobackup/mokobackup.xml | 2 +- src/packages/plg_task_mokobackup/mokobackup.xml | 2 +- src/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- src/pkg_mokobackup.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 43a744f..82d67b6 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.03-dev + 01.01.03-rc 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/src/packages/plg_quickicon_mokobackup/mokobackup.xml index eaa988a..2d055ef 100644 --- a/src/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/src/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.03-dev + 01.01.03-rc 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/src/packages/plg_system_mokobackup/mokobackup.xml index a8d2c3b..0c51af3 100644 --- a/src/packages/plg_system_mokobackup/mokobackup.xml +++ b/src/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.03-dev + 01.01.03-rc 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml index 063445a..a27fa48 100644 --- a/src/packages/plg_task_mokobackup/mokobackup.xml +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.03-dev + 01.01.03-rc 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/src/packages/plg_webservices_mokobackup/mokobackup.xml index 376fba1..d5073ba 100644 --- a/src/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/src/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.03-dev + 01.01.03-rc 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index c206ba4..3c10770 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.03-dev + 01.01.03-rc 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 119379bb16304a8dddde965f00aa7e3c081503a8 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 13:17:50 +0000 Subject: [PATCH 14/81] chore: update release-candidate channel 01.01.03-dev [skip ci] --- updates.xml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/updates.xml b/updates.xml index 8dd2da5..ccf5f1d 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ @@ -13,15 +13,34 @@ site 01.01.03-dev 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/development + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.03-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.03-dev.zip 3c28f5726d85769c0aa6ac7b686f1ae2a5c3eacdf72fd4dbb5d6c722d93a47fc dev https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md Moko Consulting https://mokoconsulting.tech + + + + Package - MokoJoomBackup + Package - MokoJoomBackup release-candidate build. + pkg_mokobackup + package + site + 01.01.03 + 2026-06-04 + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/release-candidate + + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/release-candidate/pkg_mokobackup-01.01.03.zip + + 507c98657d666a66b112eb86d4a29b2ea993d1452cd7aeba9f58c6f911f34c9f + release-candidate + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md + Moko Consulting + https://mokoconsulting.tech -- 2.52.0 From 3ded91608a7101b2ee07e308fdf918219fd96173 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 08:57:26 -0500 Subject: [PATCH 15/81] fix: remove orphaned scriptfile from component manifest The component manifest declared script.php but no script.php exists in the component sub-package. The install script belongs at the package level (pkg_mokobackup.xml) only. This caused Joomla to abort component installation before copying files or running SQL, producing the "SQL File not found" error. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/packages/com_mokobackup/mokobackup.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 82d67b6..89c2bb9 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -19,8 +19,6 @@ Joomla\Component\MokoBackup - script.php - sql/install.mysql.sql -- 2.52.0 From a34b715cffadcb5bc08aca4a361a256778008131 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 13:57:46 +0000 Subject: [PATCH 16/81] chore(version): auto-bump 01.01.04-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- src/packages/com_mokobackup/mokobackup.xml | 2 +- src/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- src/packages/plg_system_mokobackup/mokobackup.xml | 2 +- src/packages/plg_task_mokobackup/mokobackup.xml | 2 +- src/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- src/pkg_mokobackup.xml | 2 +- updates.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 07f0731..3e76d1d 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.03-dev + 01.01.04-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index cc5f056..413ef84 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.03 +# VERSION: 01.01.04 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index 5a5b12d..ca6b7b9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 89c2bb9..ae6bc92 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.03-rc + 01.01.04-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/src/packages/plg_quickicon_mokobackup/mokobackup.xml index 2d055ef..2856a6b 100644 --- a/src/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/src/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.03-rc + 01.01.04-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/src/packages/plg_system_mokobackup/mokobackup.xml index 0c51af3..1746c42 100644 --- a/src/packages/plg_system_mokobackup/mokobackup.xml +++ b/src/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.03-rc + 01.01.04-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml index a27fa48..1b72d8d 100644 --- a/src/packages/plg_task_mokobackup/mokobackup.xml +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.03-rc + 01.01.04-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/src/packages/plg_webservices_mokobackup/mokobackup.xml index d5073ba..f7ebd45 100644 --- a/src/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/src/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.03-rc + 01.01.04-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index 3c10770..2b3d7cd 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.03-rc + 01.01.04-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/updates.xml b/updates.xml index ccf5f1d..152906b 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ -- 2.52.0 From c3cbad1a00f57fc24a95431dc64611bed1191c70 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 13:57:48 +0000 Subject: [PATCH 17/81] chore: update development channel 01.01.04-dev [skip ci] --- updates.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/updates.xml b/updates.xml index 152906b..dd2a367 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ @@ -11,18 +11,18 @@ pkg_mokobackup package site - 01.01.03-dev + 01.01.04-dev 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/development + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.03-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.04-dev.zip - 3c28f5726d85769c0aa6ac7b686f1ae2a5c3eacdf72fd4dbb5d6c722d93a47fc + c3163a6abdd8cfb63982e40ce8750a8743bd8e153d8537c144ccc534ddaa2bf3 dev https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md Moko Consulting https://mokoconsulting.tech - + Package - MokoJoomBackup @@ -32,15 +32,15 @@ site 01.01.03 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/release-candidate + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/release-candidate - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/release-candidate/pkg_mokobackup-01.01.03.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/release-candidate/pkg_mokobackup-01.01.03.zip 507c98657d666a66b112eb86d4a29b2ea993d1452cd7aeba9f58c6f911f34c9f release-candidate https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md Moko Consulting https://mokoconsulting.tech - + -- 2.52.0 From e495c786fbaaf4cb72d9b0145b92b52d486fdc1d Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 14:02:49 +0000 Subject: [PATCH 18/81] chore: update development channel 01.01.04-dev [skip ci] --- updates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updates.xml b/updates.xml index dd2a367..2fb784d 100644 --- a/updates.xml +++ b/updates.xml @@ -17,7 +17,7 @@ https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.04-dev.zip - c3163a6abdd8cfb63982e40ce8750a8743bd8e153d8537c144ccc534ddaa2bf3 + 73b6dedf4bf295697a289f6baf1d0f680d0c8ab4f07ffaa2761be04469f97fa4 dev https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md Moko Consulting -- 2.52.0 From f884314e282cb169ecbc6a7ac449300494df686b Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 09:20:51 -0500 Subject: [PATCH 19/81] fix: consolidate admin files into single files block Joomla core components use a single block with entries for all subdirectories. Our manifest had multiple separate blocks which may not be fully processed by the component installer. Consolidate to match the Joomla standard pattern using with folder entries. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/packages/com_mokobackup/mokobackup.xml | 47 ++++++---------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index ae6bc92..1dcc977 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -38,46 +38,23 @@ - - provider.php - - - Controller - Engine - Extension - Model - Table - View - - - backup.xml - profile.xml - filter_backups.xml - filter_profiles.xml - - - backups - backup - profiles - profile - - - install.mysql.sql - uninstall.mysql.sql - updates - - - mokobackup.php - - - en-GB/com_mokobackup.ini - en-GB/com_mokobackup.sys.ini - COM_MOKOBACKUP COM_MOKOBACKUP_SUBMENU_BACKUPS COM_MOKOBACKUP_SUBMENU_PROFILES + + cli + forms + services + sql + src + tmpl + + + en-GB/com_mokobackup.ini + en-GB/com_mokobackup.sys.ini + -- 2.52.0 From 8e51abee5422c8395492fbe9c1f4f5aed67ddff6 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 14:21:21 +0000 Subject: [PATCH 20/81] chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/auto-release.yml | 555 +++++++++++++------------- 1 file changed, 285 insertions(+), 270 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 6fb2b44..44a2d64 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -1,270 +1,285 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform -# PATH: /templates/workflows/universal/auto-release.yml.template -# VERSION: 09.23.00 -# BRIEF: Universal build & release � detects platform from manifest.xml -# -# +========================================================================+ -# | UNIVERSAL BUILD & RELEASE PIPELINE | -# +========================================================================+ -# | | -# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | -# | | -# | Platform-specific: | -# | joomla: XML manifest, updates.xml, type-prefixed packages | -# | dolibarr: mod*.class.php, update.txt, dev version reset | -# | generic: README-only, no update stream | -# | | -# +========================================================================+ - -name: "Universal: Build & Release" - -on: - pull_request: - types: [opened, closed] - branches: - - main - workflow_dispatch: - inputs: - action: - description: 'Action to perform' - required: false - type: choice - default: release - options: - - release - - promote-rc - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -permissions: - contents: write - -jobs: - # ── PR Opened → Rename branch to RC and build RC release ───────────────────── - promote-rc: - name: Promote to RC - runs-on: release - if: >- - (github.event.action == 'opened' && github.event.pull_request.merged != true) || - (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 1 - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - run: | - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - - - name: Rename branch to rc - run: | - php /tmp/moko-platform-api/cli/branch_rename.php \ - --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ - --pr "${{ github.event.pull_request.number }}" - - - name: Checkout rc and configure git - run: | - git fetch origin rc - git checkout rc - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - - name: Publish RC release - run: | - php /tmp/moko-platform-api/cli/release_publish.php \ - --path . --stability rc --bump minor --branch rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" - - - name: Summary - if: always() - run: | - echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - echo "Branch renamed to rc, minor bump, RC + lesser stream releases built, updates.xml synced" >> $GITHUB_STEP_SUMMARY - - # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── - release: - name: Build & Release Pipeline - runs-on: release - if: >- - github.event.pull_request.merged == true || - (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 0 - - - name: Configure git for bot pushes - run: | - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' - run: | - # Ensure PHP + Composer are available - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - - - - name: "Publish stable release" - run: | - php /tmp/moko-platform-api/cli/release_publish.php \ - --path . --stability stable --bump minor --branch main \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" - - # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - - name: "Step 9: Mirror release to GitHub" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_mirror.php \ - --version "$VERSION" --tag "$RELEASE_TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ - --branch main 2>&1 || true - echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY - - # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- - - name: "Step 10: Push main to GitHub mirror" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) - GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) - git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ - git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" - git fetch origin main --depth=1 - git push github origin/main:refs/heads/main --force 2>/dev/null \ - && echo "main branch pushed to GitHub mirror" \ - || echo "WARNING: GitHub mirror push failed" - - - name: "Step 11: Delete rc branch and recreate dev from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - # Delete rc branch (ephemeral — created by promote-rc) - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/rc" 2>/dev/null \ - && echo "Deleted rc branch" || echo "rc branch not found" - - # Delete dev branch - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" - - # Recreate dev from main (now includes version bump + changelog promotion) - curl -sf -X POST -H "Authorization: token ${TOKEN}" \ - -H "Content-Type: application/json" \ - "${API_BASE}/branches" \ - -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" - - echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY - - - name: "Step 12: Create version branch from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - BRANCH_NAME="version/${VERSION}" - MAIN_SHA=$(git rev-parse HEAD) - - # Delete old version branch if it exists (same version re-release) - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" - - # Create version/XX.YY.ZZ from main - curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" - - echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY - - - - # -- Dolibarr post-release: Reset dev version ----------------------------- - - name: "Post-release: Reset dev version" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/version_reset_dev.php \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ - --branch dev --path . 2>&1 || true - - # -- Summary -------------------------------------------------------------- - - name: Pipeline Summary - if: always() - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - PLATFORM="${{ steps.platform.outputs.platform }}" - if [ "${{ steps.version.outputs.skip }}" = "true" ]; then - echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY - echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY - elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then - echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY - echo "|------|--------|" >> $GITHUB_STEP_SUMMARY - echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY - fi +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform +# PATH: /templates/workflows/universal/auto-release.yml.template +# VERSION: 05.00.00 +# BRIEF: Universal build & release � detects platform from manifest.xml +# +# +========================================================================+ +# | UNIVERSAL BUILD & RELEASE PIPELINE | +# +========================================================================+ +# | | +# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | +# | | +# | Platform-specific: | +# | joomla: XML manifest, updates.xml, type-prefixed packages | +# | dolibarr: mod*.class.php, update.txt, dev version reset | +# | generic: README-only, no update stream | +# | | +# +========================================================================+ + +name: "Universal: Build & Release" + +on: + pull_request: + types: [opened, closed] + branches: + - main + workflow_dispatch: + inputs: + action: + description: 'Action to perform' + required: false + type: choice + default: release + options: + - release + - promote-rc + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} + GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} + +permissions: + contents: write + +jobs: + # ── PR Opened → Rename branch to RC and build RC release ───────────────────── + promote-rc: + name: Promote to RC + runs-on: release + if: >- + (github.event.action == 'opened' && github.event.pull_request.merged != true) || + (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 1 + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + if ! command -v composer &> /dev/null; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + fi + # Always fetch latest CLI tools — never use stale cache from previous runs + rm -rf /tmp/moko-platform-api + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + + - name: Rename branch to rc + run: | + php /tmp/moko-platform-api/cli/branch_rename.php \ + --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ + --pr "${{ github.event.pull_request.number }}" + + - name: Checkout rc and configure git + run: | + git fetch origin rc + git checkout rc + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + + - name: Publish RC release + run: | + php /tmp/moko-platform-api/cli/release_publish.php \ + --path . --stability rc --bump minor --branch rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --skip-update-stream + + - name: Summary + if: always() + run: | + echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY + echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY + + # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── + release: + name: Build & Release Pipeline + runs-on: release + if: >- + github.event.pull_request.merged == true || + (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 0 + + - name: Configure git for bot pushes + run: | + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + + - name: Check for merge conflict markers + run: | + CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) + if [ -n "$CONFLICTS" ]; then + echo "::error::Merge conflict markers found — aborting release" + echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "No conflict markers found" + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' + run: | + # Ensure PHP + Composer are available + if ! command -v composer &> /dev/null; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + fi + # Always fetch latest CLI tools — never use stale cache from previous runs + rm -rf /tmp/moko-platform-api + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + + + - name: "Publish stable release" + run: | + php /tmp/moko-platform-api/cli/release_publish.php \ + --path . --stability stable --bump minor --branch main \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --skip-update-stream + + # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- + - name: "Step 9: Mirror release to GitHub" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + RELEASE_TAG="${{ steps.version.outputs.release_tag }}" + GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php /tmp/moko-platform-api/cli/release_mirror.php \ + --version "$VERSION" --tag "$RELEASE_TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ + --branch main 2>&1 || true + echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY + + # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- + - name: "Step 10: Push main to GitHub mirror" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" + GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) + GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) + git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ + git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" + git fetch origin main --depth=1 + git push github origin/main:refs/heads/main --force 2>/dev/null \ + && echo "main branch pushed to GitHub mirror" \ + || echo "WARNING: GitHub mirror push failed" + + - name: "Step 11: Delete rc branch and recreate dev from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + # Delete rc branch (ephemeral — created by promote-rc) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/rc" 2>/dev/null \ + && echo "Deleted rc branch" || echo "rc branch not found" + + # Delete dev branch + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" + + # Recreate dev from main (now includes version bump + changelog promotion) + curl -sf -X POST -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${API_BASE}/branches" \ + -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" + + echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY + + - name: "Step 12: Create version branch from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + BRANCH_NAME="version/${VERSION}" + MAIN_SHA=$(git rev-parse HEAD) + + # Delete old version branch if it exists (same version re-release) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" + + # Create version/XX.YY.ZZ from main + curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" + + echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY + + + + # -- Dolibarr post-release: Reset dev version ----------------------------- + - name: "Post-release: Reset dev version" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php /tmp/moko-platform-api/cli/version_reset_dev.php \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ + --branch dev --path . 2>&1 || true + + # -- Summary -------------------------------------------------------------- + - name: Pipeline Summary + if: always() + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + PLATFORM="${{ steps.platform.outputs.platform }}" + if [ "${{ steps.version.outputs.skip }}" = "true" ]; then + echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY + echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY + elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then + echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY + echo "|------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY + fi -- 2.52.0 From 4f14009003af1e6fcdcf8f23b352a8c5bc880f05 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 14:34:10 +0000 Subject: [PATCH 21/81] feat(update): migrate update server URL to Gitea Pages [skip ci] --- src/pkg_mokobackup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index 2b3d7cd..d02c244 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -32,6 +32,6 @@ - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/updates.xml + https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/updates.xml -- 2.52.0 From 41c1bb5d68ee561dcd70a86b5d25961c6dbddff3 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 14:44:22 +0000 Subject: [PATCH 22/81] chore(version): pre-release bump to 01.01.04-dev [skip ci] --- .mokogitea/workflows/auto-release.yml | 570 +++++++++++++------------- 1 file changed, 285 insertions(+), 285 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 44a2d64..141fdcc 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -1,285 +1,285 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform -# PATH: /templates/workflows/universal/auto-release.yml.template -# VERSION: 05.00.00 -# BRIEF: Universal build & release � detects platform from manifest.xml -# -# +========================================================================+ -# | UNIVERSAL BUILD & RELEASE PIPELINE | -# +========================================================================+ -# | | -# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | -# | | -# | Platform-specific: | -# | joomla: XML manifest, updates.xml, type-prefixed packages | -# | dolibarr: mod*.class.php, update.txt, dev version reset | -# | generic: README-only, no update stream | -# | | -# +========================================================================+ - -name: "Universal: Build & Release" - -on: - pull_request: - types: [opened, closed] - branches: - - main - workflow_dispatch: - inputs: - action: - description: 'Action to perform' - required: false - type: choice - default: release - options: - - release - - promote-rc - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -permissions: - contents: write - -jobs: - # ── PR Opened → Rename branch to RC and build RC release ───────────────────── - promote-rc: - name: Promote to RC - runs-on: release - if: >- - (github.event.action == 'opened' && github.event.pull_request.merged != true) || - (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 1 - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - run: | - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - - - name: Rename branch to rc - run: | - php /tmp/moko-platform-api/cli/branch_rename.php \ - --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ - --pr "${{ github.event.pull_request.number }}" - - - name: Checkout rc and configure git - run: | - git fetch origin rc - git checkout rc - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - - name: Publish RC release - run: | - php /tmp/moko-platform-api/cli/release_publish.php \ - --path . --stability rc --bump minor --branch rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --skip-update-stream - - - name: Summary - if: always() - run: | - echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY - - # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── - release: - name: Build & Release Pipeline - runs-on: release - if: >- - github.event.pull_request.merged == true || - (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 0 - - - name: Configure git for bot pushes - run: | - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - - name: Check for merge conflict markers - run: | - CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) - if [ -n "$CONFLICTS" ]; then - echo "::error::Merge conflict markers found — aborting release" - echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - exit 1 - fi - echo "No conflict markers found" - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' - run: | - # Ensure PHP + Composer are available - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - - - - name: "Publish stable release" - run: | - php /tmp/moko-platform-api/cli/release_publish.php \ - --path . --stability stable --bump minor --branch main \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --skip-update-stream - - # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - - name: "Step 9: Mirror release to GitHub" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_mirror.php \ - --version "$VERSION" --tag "$RELEASE_TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ - --branch main 2>&1 || true - echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY - - # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- - - name: "Step 10: Push main to GitHub mirror" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) - GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) - git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ - git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" - git fetch origin main --depth=1 - git push github origin/main:refs/heads/main --force 2>/dev/null \ - && echo "main branch pushed to GitHub mirror" \ - || echo "WARNING: GitHub mirror push failed" - - - name: "Step 11: Delete rc branch and recreate dev from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - # Delete rc branch (ephemeral — created by promote-rc) - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/rc" 2>/dev/null \ - && echo "Deleted rc branch" || echo "rc branch not found" - - # Delete dev branch - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" - - # Recreate dev from main (now includes version bump + changelog promotion) - curl -sf -X POST -H "Authorization: token ${TOKEN}" \ - -H "Content-Type: application/json" \ - "${API_BASE}/branches" \ - -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" - - echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY - - - name: "Step 12: Create version branch from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - BRANCH_NAME="version/${VERSION}" - MAIN_SHA=$(git rev-parse HEAD) - - # Delete old version branch if it exists (same version re-release) - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" - - # Create version/XX.YY.ZZ from main - curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" - - echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY - - - - # -- Dolibarr post-release: Reset dev version ----------------------------- - - name: "Post-release: Reset dev version" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/version_reset_dev.php \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ - --branch dev --path . 2>&1 || true - - # -- Summary -------------------------------------------------------------- - - name: Pipeline Summary - if: always() - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - PLATFORM="${{ steps.platform.outputs.platform }}" - if [ "${{ steps.version.outputs.skip }}" = "true" ]; then - echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY - echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY - elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then - echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY - echo "|------|--------|" >> $GITHUB_STEP_SUMMARY - echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY - fi +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform +# PATH: /templates/workflows/universal/auto-release.yml.template +# VERSION: 05.00.00 +# BRIEF: Universal build & release � detects platform from manifest.xml +# +# +========================================================================+ +# | UNIVERSAL BUILD & RELEASE PIPELINE | +# +========================================================================+ +# | | +# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | +# | | +# | Platform-specific: | +# | joomla: XML manifest, updates.xml, type-prefixed packages | +# | dolibarr: mod*.class.php, update.txt, dev version reset | +# | generic: README-only, no update stream | +# | | +# +========================================================================+ + +name: "Universal: Build & Release" + +on: + pull_request: + types: [opened, closed] + branches: + - main + workflow_dispatch: + inputs: + action: + description: 'Action to perform' + required: false + type: choice + default: release + options: + - release + - promote-rc + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} + GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} + +permissions: + contents: write + +jobs: + # ── PR Opened → Rename branch to RC and build RC release ───────────────────── + promote-rc: + name: Promote to RC + runs-on: release + if: >- + (github.event.action == 'opened' && github.event.pull_request.merged != true) || + (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 1 + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + if ! command -v composer &> /dev/null; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + fi + # Always fetch latest CLI tools — never use stale cache from previous runs + rm -rf /tmp/moko-platform-api + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + + - name: Rename branch to rc + run: | + php /tmp/moko-platform-api/cli/branch_rename.php \ + --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ + --pr "${{ github.event.pull_request.number }}" + + - name: Checkout rc and configure git + run: | + git fetch origin rc + git checkout rc + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + + - name: Publish RC release + run: | + php /tmp/moko-platform-api/cli/release_publish.php \ + --path . --stability rc --bump minor --branch rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --skip-update-stream + + - name: Summary + if: always() + run: | + echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY + echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY + + # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── + release: + name: Build & Release Pipeline + runs-on: release + if: >- + github.event.pull_request.merged == true || + (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 0 + + - name: Configure git for bot pushes + run: | + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + + - name: Check for merge conflict markers + run: | + CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) + if [ -n "$CONFLICTS" ]; then + echo "::error::Merge conflict markers found — aborting release" + echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "No conflict markers found" + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' + run: | + # Ensure PHP + Composer are available + if ! command -v composer &> /dev/null; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + fi + # Always fetch latest CLI tools — never use stale cache from previous runs + rm -rf /tmp/moko-platform-api + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + + + - name: "Publish stable release" + run: | + php /tmp/moko-platform-api/cli/release_publish.php \ + --path . --stability stable --bump minor --branch main \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --skip-update-stream + + # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- + - name: "Step 9: Mirror release to GitHub" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + RELEASE_TAG="${{ steps.version.outputs.release_tag }}" + GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php /tmp/moko-platform-api/cli/release_mirror.php \ + --version "$VERSION" --tag "$RELEASE_TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ + --branch main 2>&1 || true + echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY + + # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- + - name: "Step 10: Push main to GitHub mirror" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" + GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) + GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) + git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ + git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" + git fetch origin main --depth=1 + git push github origin/main:refs/heads/main --force 2>/dev/null \ + && echo "main branch pushed to GitHub mirror" \ + || echo "WARNING: GitHub mirror push failed" + + - name: "Step 11: Delete rc branch and recreate dev from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + # Delete rc branch (ephemeral — created by promote-rc) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/rc" 2>/dev/null \ + && echo "Deleted rc branch" || echo "rc branch not found" + + # Delete dev branch + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" + + # Recreate dev from main (now includes version bump + changelog promotion) + curl -sf -X POST -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${API_BASE}/branches" \ + -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" + + echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY + + - name: "Step 12: Create version branch from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + BRANCH_NAME="version/${VERSION}" + MAIN_SHA=$(git rev-parse HEAD) + + # Delete old version branch if it exists (same version re-release) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" + + # Create version/XX.YY.ZZ from main + curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" + + echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY + + + + # -- Dolibarr post-release: Reset dev version ----------------------------- + - name: "Post-release: Reset dev version" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php /tmp/moko-platform-api/cli/version_reset_dev.php \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ + --branch dev --path . 2>&1 || true + + # -- Summary -------------------------------------------------------------- + - name: Pipeline Summary + if: always() + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + PLATFORM="${{ steps.platform.outputs.platform }}" + if [ "${{ steps.version.outputs.skip }}" = "true" ]; then + echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY + echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY + elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then + echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY + echo "|------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY + fi -- 2.52.0 From 8d082de47f499d93abe73d5fae767d51ee3dc999 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 14:44:23 +0000 Subject: [PATCH 23/81] chore: update development channel 01.01.04-dev [skip ci] --- updates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updates.xml b/updates.xml index 2fb784d..ec21878 100644 --- a/updates.xml +++ b/updates.xml @@ -17,7 +17,7 @@ https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.04-dev.zip - 73b6dedf4bf295697a289f6baf1d0f680d0c8ab4f07ffaa2761be04469f97fa4 + 45ff0812d06f9e48738c5a2854c274ad9cd5fd8f1a508d54e0847167c4446b10 dev https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md Moko Consulting -- 2.52.0 From 4c15f1242679a3e4da62066a4f97ee4eccc0e405 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:14:33 +0000 Subject: [PATCH 24/81] chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] --- .mokogitea/workflows/pr-check.yml | 636 +++++++++++++++++------------- 1 file changed, 372 insertions(+), 264 deletions(-) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index bf72613..9d0cb35 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -1,264 +1,372 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.CI -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform -# PATH: /templates/workflows/universal/pr-check.yml.template -# VERSION: 09.23.00 -# BRIEF: PR gate — branch policy + code validation before merge - -name: "Universal: PR Check" - -on: - pull_request: - types: [opened, synchronize, reopened, edited] - -permissions: - contents: read - pull-requests: write - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - # ── Branch Policy ────────────────────────────────────────────────────── - branch-policy: - name: Branch Policy - runs-on: ubuntu-latest - steps: - - name: Check branch merge target - run: | - HEAD="${{ github.head_ref }}" - BASE="${{ github.base_ref }}" - - echo "PR: ${HEAD} → ${BASE}" - - ALLOWED=true - REASON="" - - case "$HEAD" in - feature/*|feat/*) - if [ "$BASE" != "dev" ]; then - ALLOWED=false - REASON="Feature branches must target 'dev', not '${BASE}'" - fi - ;; - fix/*|bugfix/*) - if [ "$BASE" != "dev" ]; then - ALLOWED=false - REASON="Fix branches must target 'dev', not '${BASE}'" - fi - ;; - patch/*) - if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then - ALLOWED=false - REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'" - fi - ;; - hotfix/*) - if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then - ALLOWED=false - REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'" - fi - ;; - rc) - if [ "$BASE" != "main" ]; then - ALLOWED=false - REASON="RC branch can only merge into 'main', not '${BASE}'" - fi - ;; - dev) - if [ "$BASE" != "main" ]; then - ALLOWED=false - REASON="Dev branch can only merge into 'main', not '${BASE}'" - fi - ;; - esac - - if [ "$ALLOWED" = false ]; then - echo "::error::${REASON}" - echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "${REASON}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY - echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY - echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY - echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY - echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY - echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY - exit 1 - fi - - echo "Branch policy: OK (${HEAD} → ${BASE})" - echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY - - # ── Code Validation ──────────────────────────────────────────────────── - validate: - name: Validate PR - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Detect platform - id: platform - run: | - # Read platform from XML manifest ( tag) or plain text fallback - PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1) - [ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]') - [ -z "$PLATFORM" ] && PLATFORM="generic" - echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" - - - name: Setup PHP - if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' - run: | - if ! command -v php &> /dev/null; then - sudo apt-get update -qq - sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1 - fi - - - name: PHP syntax check - if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' - run: | - ERRORS=0 - while IFS= read -r -d '' file; do - if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then - ERRORS=$((ERRORS + 1)) - fi - done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -print0) - echo "PHP lint: ${ERRORS} error(s)" - [ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; } - - - name: Validate platform manifest - run: | - PLATFORM="${{ steps.platform.outputs.platform }}" - case "$PLATFORM" in - joomla) - MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) - if [ -z "$MANIFEST" ]; then - echo "::warning::No Joomla manifest found (WaaS site)" - exit 0 - fi - echo "Manifest: ${MANIFEST}" - if command -v php &> /dev/null; then - php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$MANIFEST'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::Manifest XML is malformed"; exit 1; } - fi - for ELEMENT in name version description; do - grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; } - done - echo "Joomla manifest valid" - ;; - dolibarr) - MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1) - if [ -z "$MOD_FILE" ]; then - echo "::error::No mod*.class.php found" - exit 1 - fi - echo "Dolibarr module: ${MOD_FILE}" - ;; - *) - echo "Generic platform — no manifest validation" - ;; - esac - - - name: Check update stream format - run: | - PLATFORM="${{ steps.platform.outputs.platform }}" - case "$PLATFORM" in - joomla) - if [ -f "updates.xml" ]; then - if command -v php &> /dev/null; then - php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('updates.xml'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::updates.xml is malformed"; exit 1; } - fi - echo "updates.xml valid" - fi - ;; - dolibarr) - [ -f "update.txt" ] && echo "update.txt present" || echo "::warning::No update.txt" - ;; - esac - - - name: Check changelog has unreleased entry - run: | - if [ ! -f "CHANGELOG.md" ]; then - echo "::warning::No CHANGELOG.md found" - exit 0 - fi - # Check for content under [Unreleased] section - if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then - echo "::error::CHANGELOG.md missing [Unreleased] section" - exit 1 - fi - # Check there's at least one entry (Added/Changed/Fixed/Removed) under Unreleased - UNRELEASED_CONTENT=$(sed -n '/## \[Unreleased\]/,/## \[/p' CHANGELOG.md | grep -cE '^\s*-\s' || true) - if [ "$UNRELEASED_CONTENT" -eq 0 ]; then - echo "::error::CHANGELOG.md [Unreleased] section has no entries. Add a changelog entry describing your changes." - echo "## Changelog Check: Failed" >> $GITHUB_STEP_SUMMARY - echo "The \`[Unreleased]\` section in CHANGELOG.md has no entries." >> $GITHUB_STEP_SUMMARY - echo "Add a line like \`- Description of your change\` under a heading (\`### Added\`, \`### Changed\`, \`### Fixed\`, etc.)" >> $GITHUB_STEP_SUMMARY - exit 1 - fi - echo "Changelog: ${UNRELEASED_CONTENT} entry/entries in [Unreleased]" - - - name: Verify package source - run: | - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - if [ ! -d "$SOURCE_DIR" ]; then - echo "::warning::No src/ or htdocs/ directory" - exit 0 - fi - FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l) - echo "Source: ${FILE_COUNT} files" - [ "$FILE_COUNT" -gt 0 ] || { echo "::error::Source directory is empty"; exit 1; } - - # ── Pre-Release RC Build ───────────────────────────────────────────────── - pre-release: - name: Build RC Package - runs-on: ubuntu-latest - needs: [branch-policy, validate] - - steps: - - name: Trigger RC pre-release - env: - GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - REPO: ${{ github.repository }} - BRANCH: ${{ github.head_ref }} - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - run: | - curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}" - echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY - echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY - - # ── Issue Reporter ────────────────────────────────────────────────────── - report-issues: - name: Report Issues - runs-on: ubuntu-latest - needs: [branch-policy, validate] - if: >- - always() && - needs.validate.result == 'failure' - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - sparse-checkout: automation/ci-issue-reporter.sh - sparse-checkout-cone-mode: false - - - name: "File issue for PR validation failure" - env: - GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - run: | - chmod +x automation/ci-issue-reporter.sh - ./automation/ci-issue-reporter.sh \ - --gate "PR Validation" \ - --workflow "PR Check" \ - --severity error \ - --details "PR validation failed (syntax, manifest, changelog, or source checks). See the CI run for the specific check that failed." +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.CI +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform +# PATH: /templates/workflows/universal/pr-check.yml.template +# VERSION: 09.23.00 +# BRIEF: PR gate — branch policy + code validation before merge + +name: "Universal: PR Check" + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + +permissions: + contents: read + pull-requests: write + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + # ── Branch Policy ────────────────────────────────────────────────────── + branch-policy: + name: Branch Policy + runs-on: ubuntu-latest + steps: + - name: Check branch merge target + run: | + HEAD="${{ github.head_ref }}" + BASE="${{ github.base_ref }}" + + echo "PR: ${HEAD} → ${BASE}" + + ALLOWED=true + REASON="" + + case "$HEAD" in + feature/*|feat/*) + if [ "$BASE" != "dev" ]; then + ALLOWED=false + REASON="Feature branches must target 'dev', not '${BASE}'" + fi + ;; + fix/*|bugfix/*) + if [ "$BASE" != "dev" ]; then + ALLOWED=false + REASON="Fix branches must target 'dev', not '${BASE}'" + fi + ;; + patch/*) + if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then + ALLOWED=false + REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'" + fi + ;; + hotfix/*) + if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then + ALLOWED=false + REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'" + fi + ;; + rc) + if [ "$BASE" != "main" ]; then + ALLOWED=false + REASON="RC branch can only merge into 'main', not '${BASE}'" + fi + ;; + dev) + if [ "$BASE" != "main" ]; then + ALLOWED=false + REASON="Dev branch can only merge into 'main', not '${BASE}'" + fi + ;; + esac + + if [ "$ALLOWED" = false ]; then + echo "::error::${REASON}" + echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "${REASON}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY + echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY + echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY + echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY + echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY + echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + echo "Branch policy: OK (${HEAD} → ${BASE})" + echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY + + # ── Code Validation ──────────────────────────────────────────────────── + validate: + name: Validate PR + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for merge conflict markers + run: | + CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) + if [ -n "$CONFLICTS" ]; then + echo "::error::Merge conflict markers found in source files" + echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "No conflict markers found" + + - name: Detect platform + id: platform + run: | + # Read platform from XML manifest ( tag) or plain text fallback + PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1) + [ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]') + [ -z "$PLATFORM" ] && PLATFORM="generic" + echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" + + - name: Setup PHP + if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' + run: | + if ! command -v php &> /dev/null; then + sudo apt-get update -qq + sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1 + fi + + - name: PHP syntax check + if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' + run: | + ERRORS=0 + while IFS= read -r -d '' file; do + if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -print0) + echo "PHP lint: ${ERRORS} error(s)" + [ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; } + + - name: Validate platform manifest + run: | + PLATFORM="${{ steps.platform.outputs.platform }}" + case "$PLATFORM" in + joomla) + MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) + if [ -z "$MANIFEST" ]; then + echo "::warning::No Joomla manifest found (WaaS site)" + exit 0 + fi + echo "Manifest: ${MANIFEST}" + if command -v php &> /dev/null; then + php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$MANIFEST'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::Manifest XML is malformed"; exit 1; } + fi + for ELEMENT in name version description; do + grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; } + done + echo "Joomla manifest valid" + ;; + dolibarr) + MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1) + if [ -z "$MOD_FILE" ]; then + echo "::error::No mod*.class.php found" + exit 1 + fi + echo "Dolibarr module: ${MOD_FILE}" + ;; + *) + echo "Generic platform — no manifest validation" + ;; + esac + + - name: Check update stream format + run: | + PLATFORM="${{ steps.platform.outputs.platform }}" + case "$PLATFORM" in + joomla) + if [ -f "updates.xml" ]; then + if command -v php &> /dev/null; then + php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('updates.xml'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::updates.xml is malformed"; exit 1; } + fi + echo "updates.xml valid" + fi + ;; + dolibarr) + [ -f "update.txt" ] && echo "update.txt present" || echo "::warning::No update.txt" + ;; + esac + + - name: Validate Joomla language files + if: steps.platform.outputs.platform == 'joomla' + run: | + ERRORS=0 + WARNINGS=0 + + # Find all .ini language files + INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null) + if [ -z "$INI_FILES" ]; then + echo "No .ini language files found — skipping" + exit 0 + fi + + echo "Found $(echo "$INI_FILES" | wc -l) language file(s)" + + for FILE in $INI_FILES; do + FNAME=$(basename "$FILE") + LINENUM=0 + SEEN_KEYS="" + + while IFS= read -r line || [ -n "$line" ]; do + LINENUM=$((LINENUM + 1)) + + # Skip empty lines and comments + [ -z "$line" ] && continue + echo "$line" | grep -qE '^\s*;' && continue + echo "$line" | grep -qE '^\s*$' && continue + + # Must match KEY="VALUE" format + if ! echo "$line" | grep -qE '^[A-Z_][A-Z0-9_]*=".*"$'; then + echo "::error file=${FILE},line=${LINENUM}::Malformed line: ${line}" + ERRORS=$((ERRORS + 1)) + continue + fi + + # Extract key and check for duplicates + KEY=$(echo "$line" | sed 's/=.*//') + if echo "$SEEN_KEYS" | grep -qx "$KEY"; then + echo "::error file=${FILE},line=${LINENUM}::Duplicate key: ${KEY}" + ERRORS=$((ERRORS + 1)) + fi + SEEN_KEYS="${SEEN_KEYS} + ${KEY}" + done < "$FILE" + + echo " ${FILE}: checked ${LINENUM} lines" + done + + # Cross-check en-GB vs en-US key consistency + GB_DIR=$(find . -path "*/language/en-GB" -type d -not -path "./.git/*" 2>/dev/null | head -1) + US_DIR=$(find . -path "*/language/en-US" -type d -not -path "./.git/*" 2>/dev/null | head -1) + + if [ -n "$GB_DIR" ] && [ -n "$US_DIR" ]; then + for GB_FILE in "$GB_DIR"/*.ini; do + [ ! -f "$GB_FILE" ] && continue + FNAME=$(basename "$GB_FILE") + US_FILE="$US_DIR/$FNAME" + [ ! -f "$US_FILE" ] && continue + + GB_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$GB_FILE" 2>/dev/null | sort) + US_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$US_FILE" 2>/dev/null | sort) + + # Keys in en-GB but not en-US + MISSING_US=$(comm -23 <(echo "$GB_KEYS") <(echo "$US_KEYS")) + if [ -n "$MISSING_US" ]; then + echo "::warning::Keys in en-GB/$FNAME but missing from en-US/$FNAME:" + echo "$MISSING_US" | while read -r k; do echo " - $k"; done + WARNINGS=$((WARNINGS + 1)) + fi + + # Keys in en-US but not en-GB + MISSING_GB=$(comm -13 <(echo "$GB_KEYS") <(echo "$US_KEYS")) + if [ -n "$MISSING_GB" ]; then + echo "::warning::Keys in en-US/$FNAME but missing from en-GB/$FNAME:" + echo "$MISSING_GB" | while read -r k; do echo " - $k"; done + WARNINGS=$((WARNINGS + 1)) + fi + done + fi + + { + echo "### Language File Validation" + echo "| Metric | Count |" + echo "|---|---|" + echo "| Files checked | $(echo "$INI_FILES" | wc -l) |" + echo "| Errors | ${ERRORS} |" + echo "| Warnings | ${WARNINGS} |" + } >> $GITHUB_STEP_SUMMARY + + if [ "$ERRORS" -gt 0 ]; then + echo "::error::Language validation failed with ${ERRORS} error(s)" + exit 1 + fi + echo "Language files: OK (${WARNINGS} warning(s))" + + - name: Check changelog has unreleased entry + run: | + if [ ! -f "CHANGELOG.md" ]; then + echo "::warning::No CHANGELOG.md found" + exit 0 + fi + # Check for content under [Unreleased] section + if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then + echo "::error::CHANGELOG.md missing [Unreleased] section" + exit 1 + fi + # Check there's at least one entry (Added/Changed/Fixed/Removed) under Unreleased + UNRELEASED_CONTENT=$(sed -n '/## \[Unreleased\]/,/## \[/p' CHANGELOG.md | grep -cE '^\s*-\s' || true) + if [ "$UNRELEASED_CONTENT" -eq 0 ]; then + echo "::error::CHANGELOG.md [Unreleased] section has no entries. Add a changelog entry describing your changes." + echo "## Changelog Check: Failed" >> $GITHUB_STEP_SUMMARY + echo "The \`[Unreleased]\` section in CHANGELOG.md has no entries." >> $GITHUB_STEP_SUMMARY + echo "Add a line like \`- Description of your change\` under a heading (\`### Added\`, \`### Changed\`, \`### Fixed\`, etc.)" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "Changelog: ${UNRELEASED_CONTENT} entry/entries in [Unreleased]" + + - name: Verify package source + run: | + SOURCE_DIR="src" + [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" + if [ ! -d "$SOURCE_DIR" ]; then + echo "::warning::No src/ or htdocs/ directory" + exit 0 + fi + FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l) + echo "Source: ${FILE_COUNT} files" + [ "$FILE_COUNT" -gt 0 ] || { echo "::error::Source directory is empty"; exit 1; } + + # ── Pre-Release RC Build ───────────────────────────────────────────────── + pre-release: + name: Build RC Package + runs-on: ubuntu-latest + needs: [branch-policy, validate] + + steps: + - name: Trigger RC pre-release + env: + GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + REPO: ${{ github.repository }} + BRANCH: ${{ github.head_ref }} + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + run: | + curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}" + echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY + echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY + + # ── Issue Reporter ────────────────────────────────────────────────────── + report-issues: + name: Report Issues + runs-on: ubuntu-latest + needs: [branch-policy, validate] + if: >- + always() && + needs.validate.result == 'failure' + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: automation/ci-issue-reporter.sh + sparse-checkout-cone-mode: false + + - name: "File issue for PR validation failure" + env: + GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + run: | + chmod +x automation/ci-issue-reporter.sh + ./automation/ci-issue-reporter.sh \ + --gate "PR Validation" \ + --workflow "PR Check" \ + --severity error \ + --details "PR validation failed (syntax, manifest, changelog, or source checks). See the CI run for the specific check that failed." -- 2.52.0 From e9d7889417055596bf2b4f443469edfce3aaff68 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:27:07 +0000 Subject: [PATCH 25/81] chore: remove updates.xml [skip ci] --- updates.xml | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 updates.xml diff --git a/updates.xml b/updates.xml deleted file mode 100644 index ec21878..0000000 --- a/updates.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - Package - MokoJoomBackup - Package - MokoJoomBackup development build. - pkg_mokobackup - package - site - 01.01.04-dev - 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/development - - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/development/pkg_mokobackup-01.01.04-dev.zip - - 45ff0812d06f9e48738c5a2854c274ad9cd5fd8f1a508d54e0847167c4446b10 - dev - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md - Moko Consulting - https://mokoconsulting.tech - - - - Package - MokoJoomBackup - Package - MokoJoomBackup release-candidate build. - pkg_mokobackup - package - site - 01.01.03 - 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/tag/release-candidate - - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/releases/download/release-candidate/pkg_mokobackup-01.01.03.zip - - 507c98657d666a66b112eb86d4a29b2ea993d1452cd7aeba9f58c6f911f34c9f - release-candidate - https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/raw/branch/main/CHANGELOG.md - Moko Consulting - https://mokoconsulting.tech - - - -- 2.52.0 From 8c5ed1ed768c4e01e84302433dff6634b891a6be Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:30:34 +0000 Subject: [PATCH 26/81] chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] --- .mokogitea/workflows/pr-check.yml | 39 ++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 9d0cb35..473eeb2 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -202,10 +202,47 @@ jobs: ERRORS=0 WARNINGS=0 + # Require both en-GB and en-US language directories + LANG_ROOT=$(find . -path "*/language" -type d -not -path "./.git/*" 2>/dev/null | head -1) + if [ -z "$LANG_ROOT" ]; then + echo "No language/ directory found — skipping" + exit 0 + fi + + if [ ! -d "$LANG_ROOT/en-GB" ]; then + echo "::error::Missing en-GB language directory (${LANG_ROOT}/en-GB)" + ERRORS=$((ERRORS + 1)) + fi + if [ ! -d "$LANG_ROOT/en-US" ]; then + echo "::error::Missing en-US language directory (${LANG_ROOT}/en-US)" + ERRORS=$((ERRORS + 1)) + fi + + # Check that en-GB and en-US have matching .ini files + if [ -d "$LANG_ROOT/en-GB" ] && [ -d "$LANG_ROOT/en-US" ]; then + for GB_INI in "$LANG_ROOT/en-GB"/*.ini; do + [ ! -f "$GB_INI" ] && continue + US_INI="$LANG_ROOT/en-US/$(basename "$GB_INI")" + if [ ! -f "$US_INI" ]; then + echo "::error::$(basename "$GB_INI") exists in en-GB but missing from en-US" + ERRORS=$((ERRORS + 1)) + fi + done + for US_INI in "$LANG_ROOT/en-US"/*.ini; do + [ ! -f "$US_INI" ] && continue + GB_INI="$LANG_ROOT/en-GB/$(basename "$US_INI")" + if [ ! -f "$GB_INI" ]; then + echo "::error::$(basename "$US_INI") exists in en-US but missing from en-GB" + ERRORS=$((ERRORS + 1)) + fi + done + fi + # Find all .ini language files INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null) if [ -z "$INI_FILES" ]; then - echo "No .ini language files found — skipping" + echo "No .ini language files found" + [ "$ERRORS" -gt 0 ] && exit 1 exit 0 fi -- 2.52.0 From 61abf724374c00447447889d45c4fcfdf0eabb3a Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:39:23 +0000 Subject: [PATCH 27/81] chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] --- .mokogitea/workflows/pr-check.yml | 92 +++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 473eeb2..3dd7540 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -147,6 +147,98 @@ jobs: echo "PHP lint: ${ERRORS} error(s)" [ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; } + - name: Joomla JEXEC guard check + if: steps.platform.outputs.platform == 'joomla' + run: | + ERRORS=0 + while IFS= read -r -d '' file; do + # Skip vendor, node_modules, and index.html stub files + case "$file" in ./vendor/*|./node_modules/*) continue ;; esac + # Check first 10 lines for JEXEC or JPATH guard + if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then + echo "::error file=${file}::Missing JEXEC guard: ${file}" + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0) + if [ "$ERRORS" -gt 0 ]; then + echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard" + echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY + echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "JEXEC guard: OK" + + - name: Joomla directory listing protection + if: steps.platform.outputs.platform == 'joomla' + run: | + MISSING=0 + SOURCE_DIR="src" + [ ! -d "$SOURCE_DIR" ] && exit 0 + while IFS= read -r dir; do + if [ ! -f "${dir}/index.html" ]; then + echo "::warning::Missing index.html in ${dir} (directory listing protection)" + MISSING=$((MISSING + 1)) + fi + done < <(find "$SOURCE_DIR" -type d -not -path "./.git/*" -not -path "*/vendor/*" -not -path "*/node_modules/*") + if [ "$MISSING" -gt 0 ]; then + echo "## Directory Protection" >> $GITHUB_STEP_SUMMARY + echo "${MISSING} director(ies) missing index.html" >> $GITHUB_STEP_SUMMARY + fi + echo "Directory protection: ${MISSING} missing (advisory)" + + - name: Joomla script file and asset checks + if: steps.platform.outputs.platform == 'joomla' + run: | + ERRORS=0 + MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) + [ -z "$MANIFEST" ] && exit 0 + MANIFEST_DIR=$(dirname "$MANIFEST") + + # Check scriptfile exists if declared + SCRIPTFILE=$(sed -n 's/.*\([^<]*\)<\/scriptfile>.*/\1/p' "$MANIFEST" 2>/dev/null) + if [ -n "$SCRIPTFILE" ]; then + if [ ! -f "${MANIFEST_DIR}/${SCRIPTFILE}" ]; then + echo "::error::Manifest declares ${SCRIPTFILE} but file not found at ${MANIFEST_DIR}/${SCRIPTFILE}" + ERRORS=$((ERRORS + 1)) + else + echo "Script file: ${MANIFEST_DIR}/${SCRIPTFILE} (OK)" + fi + fi + + # Require joomla.asset.json and validate it + ASSET_JSON=$(find "$MANIFEST_DIR" -name "joomla.asset.json" -not -path "./.git/*" 2>/dev/null | head -1) + if [ -z "$ASSET_JSON" ]; then + echo "::error::joomla.asset.json not found — Joomla asset system is required" + ERRORS=$((ERRORS + 1)) + else + if command -v php &> /dev/null; then + php -r "json_decode(file_get_contents('$ASSET_JSON')); if(json_last_error()!==JSON_ERROR_NONE){echo json_last_error_msg();exit(1);}" 2>&1 || { + echo "::error::joomla.asset.json is not valid JSON" + ERRORS=$((ERRORS + 1)) + } + fi + echo "joomla.asset.json: valid" + fi + + # Validate all XML files in src/ are well-formed + XML_ERRORS=0 + if command -v php &> /dev/null; then + while IFS= read -r -d '' xmlfile; do + if ! php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$xmlfile'); if(!\$x){foreach(libxml_get_errors() as \$e) echo trim(\$e->message) . ' in $xmlfile'; exit(1);}" 2>&1; then + XML_ERRORS=$((XML_ERRORS + 1)) + fi + done < <(find "$MANIFEST_DIR" -name "*.xml" -not -path "./.git/*" -print0) + fi + if [ "$XML_ERRORS" -gt 0 ]; then + echo "::error::${XML_ERRORS} XML file(s) are malformed" + ERRORS=$((ERRORS + 1)) + else + echo "XML well-formedness: OK" + fi + + [ "$ERRORS" -gt 0 ] && exit 1 + echo "Joomla asset checks: OK" + - name: Validate platform manifest run: | PLATFORM="${{ steps.platform.outputs.platform }}" -- 2.52.0 From 25e06fd08c431ae2c616a0d3c26aee5f9d3b56b8 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 15:56:54 +0000 Subject: [PATCH 28/81] chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] --- .mokogitea/workflows/pr-check.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 3dd7540..4d78d7a 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -256,6 +256,13 @@ jobs: for ELEMENT in name version description; do grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; } done + # Block legacy raw/branch update server URLs on MokoGitea + RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogitea\|git\.mokoconsulting\.tech' || true) + if [ -n "$RAW_URLS" ]; then + echo "::error::Manifest contains legacy raw/branch update server URL on MokoGitea. Use the Gitea Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)" + echo "$RAW_URLS" + exit 1 + fi echo "Joomla manifest valid" ;; dolibarr) -- 2.52.0 From c1521cb2354c32b8cf735fb3867739fcb5cfc3ac Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 11:20:35 -0500 Subject: [PATCH 29/81] feat: add dashboard view and console, content, actionlog plugins (#24, #25, #26, #27) Dashboard view becomes the default landing page with status cards, quick actions (backup now w/ profile selector), and system health checks. Three new plugins round out the package: - plg_console_mokobackup: CLI commands (run, list, profiles, restore, cleanup) - plg_content_mokobackup: auto-backup before extension install/update - plg_actionlog_mokobackup: logs backup and profile actions to User Action Logs BackupEngine now dispatches onMokoBackupAfterRun for plugin listeners. Package manifest and install script updated to include and auto-enable the new plugins. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../language/en-GB/com_mokobackup.ini | 15 + .../language/en-US/com_mokobackup.ini | 13 + src/packages/com_mokobackup/mokobackup.xml | 1 + .../src/Controller/DisplayController.php | 2 +- .../src/Engine/BackupEngine.php | 29 ++ .../src/Model/DashboardModel.php | 163 +++++++++++ .../src/View/Dashboard/HtmlView.php | 48 ++++ .../com_mokobackup/tmpl/dashboard/default.php | 265 ++++++++++++++++++ .../en-GB/plg_actionlog_mokobackup.ini | 9 + .../en-GB/plg_actionlog_mokobackup.sys.ini | 3 + .../en-US/plg_actionlog_mokobackup.ini | 9 + .../en-US/plg_actionlog_mokobackup.sys.ini | 3 + .../plg_actionlog_mokobackup/mokobackup.php | 11 + .../plg_actionlog_mokobackup/mokobackup.xml | 32 +++ .../services/provider.php | 37 +++ .../src/Extension/MokoBackupActionlog.php | 174 ++++++++++++ .../language/en-GB/plg_console_mokobackup.ini | 3 + .../en-GB/plg_console_mokobackup.sys.ini | 3 + .../language/en-US/plg_console_mokobackup.ini | 3 + .../en-US/plg_console_mokobackup.sys.ini | 3 + .../plg_console_mokobackup/mokobackup.php | 11 + .../plg_console_mokobackup/mokobackup.xml | 32 +++ .../services/provider.php | 37 +++ .../src/Command/CleanupCommand.php | 125 +++++++++ .../src/Command/ListCommand.php | 87 ++++++ .../src/Command/ProfilesCommand.php | 68 +++++ .../src/Command/RestoreCommand.php | 101 +++++++ .../src/Command/RunCommand.php | 68 +++++ .../src/Extension/MokoBackupConsole.php | 45 +++ .../language/en-GB/plg_content_mokobackup.ini | 9 + .../en-GB/plg_content_mokobackup.sys.ini | 3 + .../language/en-US/plg_content_mokobackup.ini | 9 + .../en-US/plg_content_mokobackup.sys.ini | 3 + .../plg_content_mokobackup/mokobackup.php | 11 + .../plg_content_mokobackup/mokobackup.xml | 71 +++++ .../services/provider.php | 37 +++ .../src/Extension/MokoBackupContent.php | 95 +++++++ src/pkg_mokobackup.xml | 3 + src/script.php | 33 +++ 39 files changed, 1673 insertions(+), 1 deletion(-) create mode 100644 src/packages/com_mokobackup/src/Model/DashboardModel.php create mode 100644 src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php create mode 100644 src/packages/com_mokobackup/tmpl/dashboard/default.php create mode 100644 src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini create mode 100644 src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini create mode 100644 src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini create mode 100644 src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini create mode 100644 src/packages/plg_actionlog_mokobackup/mokobackup.php create mode 100644 src/packages/plg_actionlog_mokobackup/mokobackup.xml create mode 100644 src/packages/plg_actionlog_mokobackup/services/provider.php create mode 100644 src/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php create mode 100644 src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini create mode 100644 src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini create mode 100644 src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini create mode 100644 src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini create mode 100644 src/packages/plg_console_mokobackup/mokobackup.php create mode 100644 src/packages/plg_console_mokobackup/mokobackup.xml create mode 100644 src/packages/plg_console_mokobackup/services/provider.php create mode 100644 src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php create mode 100644 src/packages/plg_console_mokobackup/src/Command/ListCommand.php create mode 100644 src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php create mode 100644 src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php create mode 100644 src/packages/plg_console_mokobackup/src/Command/RunCommand.php create mode 100644 src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php create mode 100644 src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini create mode 100644 src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini create mode 100644 src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini create mode 100644 src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini create mode 100644 src/packages/plg_content_mokobackup/mokobackup.php create mode 100644 src/packages/plg_content_mokobackup/mokobackup.xml create mode 100644 src/packages/plg_content_mokobackup/services/provider.php create mode 100644 src/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php diff --git a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini index 0921f20..442c06a 100644 --- a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini +++ b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini @@ -8,9 +8,24 @@ COM_MOKOBACKUP="MokoJoomBackup" COM_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla" ; Submenu +COM_MOKOBACKUP_SUBMENU_DASHBOARD="Dashboard" COM_MOKOBACKUP_SUBMENU_BACKUPS="Backup Records" COM_MOKOBACKUP_SUBMENU_PROFILES="Backup Profiles" +; Dashboard view +COM_MOKOBACKUP_DASHBOARD_TITLE="MokoJoomBackup Dashboard" +COM_MOKOBACKUP_DASHBOARD_LAST_BACKUP="Last Backup" +COM_MOKOBACKUP_DASHBOARD_NO_BACKUPS="No backups yet" +COM_MOKOBACKUP_DASHBOARD_NEXT_SCHEDULED="Next Scheduled" +COM_MOKOBACKUP_DASHBOARD_NO_SCHEDULED="No tasks scheduled" +COM_MOKOBACKUP_DASHBOARD_TOTAL_BACKUPS="Total Backups" +COM_MOKOBACKUP_DASHBOARD_STORAGE="Storage Used" +COM_MOKOBACKUP_DASHBOARD_FAILURES_7D="%d failures (7 days)" +COM_MOKOBACKUP_DASHBOARD_QUICK_ACTIONS="Quick Actions" +COM_MOKOBACKUP_DASHBOARD_SCHEDULED_TASKS="Scheduled Tasks" +COM_MOKOBACKUP_DASHBOARD_UPDATE_SITE="Update Site" +COM_MOKOBACKUP_DASHBOARD_SYSTEM_HEALTH="System Health" + ; Backups view COM_MOKOBACKUP_BACKUPS_TITLE="Backup Records" COM_MOKOBACKUP_BACKUPS_TABLE_CAPTION="Table of backup records" diff --git a/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini b/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini index 309264d..577580b 100644 --- a/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini +++ b/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini @@ -6,8 +6,21 @@ COM_MOKOBACKUP="MokoJoomBackup" COM_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla" +COM_MOKOBACKUP_SUBMENU_DASHBOARD="Dashboard" COM_MOKOBACKUP_SUBMENU_BACKUPS="Backup Records" COM_MOKOBACKUP_SUBMENU_PROFILES="Backup Profiles" +COM_MOKOBACKUP_DASHBOARD_TITLE="MokoJoomBackup Dashboard" +COM_MOKOBACKUP_DASHBOARD_LAST_BACKUP="Last Backup" +COM_MOKOBACKUP_DASHBOARD_NO_BACKUPS="No backups yet" +COM_MOKOBACKUP_DASHBOARD_NEXT_SCHEDULED="Next Scheduled" +COM_MOKOBACKUP_DASHBOARD_NO_SCHEDULED="No tasks scheduled" +COM_MOKOBACKUP_DASHBOARD_TOTAL_BACKUPS="Total Backups" +COM_MOKOBACKUP_DASHBOARD_STORAGE="Storage Used" +COM_MOKOBACKUP_DASHBOARD_FAILURES_7D="%d failures (7 days)" +COM_MOKOBACKUP_DASHBOARD_QUICK_ACTIONS="Quick Actions" +COM_MOKOBACKUP_DASHBOARD_SCHEDULED_TASKS="Scheduled Tasks" +COM_MOKOBACKUP_DASHBOARD_UPDATE_SITE="Update Site" +COM_MOKOBACKUP_DASHBOARD_SYSTEM_HEALTH="System Health" COM_MOKOBACKUP_BACKUPS_TITLE="Backup Records" COM_MOKOBACKUP_PROFILES_TITLE="Backup Profiles" COM_MOKOBACKUP_TOOLBAR_BACKUP_NOW="Backup Now" diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 1dcc977..f715793 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -40,6 +40,7 @@ COM_MOKOBACKUP + COM_MOKOBACKUP_SUBMENU_DASHBOARD COM_MOKOBACKUP_SUBMENU_BACKUPS COM_MOKOBACKUP_SUBMENU_PROFILES diff --git a/src/packages/com_mokobackup/src/Controller/DisplayController.php b/src/packages/com_mokobackup/src/Controller/DisplayController.php index 5e4ec11..5425324 100644 --- a/src/packages/com_mokobackup/src/Controller/DisplayController.php +++ b/src/packages/com_mokobackup/src/Controller/DisplayController.php @@ -16,5 +16,5 @@ use Joomla\CMS\MVC\Controller\BaseController; class DisplayController extends BaseController { - protected $default_view = 'backups'; + protected $default_view = 'dashboard'; } diff --git a/src/packages/com_mokobackup/src/Engine/BackupEngine.php b/src/packages/com_mokobackup/src/Engine/BackupEngine.php index 60e0910..cf456cd 100644 --- a/src/packages/com_mokobackup/src/Engine/BackupEngine.php +++ b/src/packages/com_mokobackup/src/Engine/BackupEngine.php @@ -13,6 +13,7 @@ namespace Joomla\Component\MokoBackup\Administrator\Engine; defined('_JEXEC') or die; use Joomla\CMS\Factory; +use Joomla\Event\Event; class BackupEngine { @@ -250,6 +251,9 @@ class BackupEngine // Send success notification NotificationSender::send($profile, $update, true, implode("\n", $this->log)); + // Dispatch event for actionlog and other listeners + $this->dispatchAfterRun(true, $recordId, $description, $profileId, $origin); + return [ 'success' => true, 'message' => 'Backup complete: ' . $archiveName . ' (' . $sizeHuman . ')', @@ -275,6 +279,9 @@ class BackupEngine // Send failure notification NotificationSender::send($profile, $update, false, implode("\n", $this->log)); + // Dispatch event for actionlog and other listeners + $this->dispatchAfterRun(false, $recordId, $description, $profileId, $origin); + return ['success' => false, 'message' => 'Backup failed: ' . $e->getMessage(), 'record_id' => $recordId]; } } @@ -445,6 +452,28 @@ class BackupEngine )); } + /** + * Dispatch the onMokoBackupAfterRun event so plugins (actionlog, etc.) can react. + */ + private function dispatchAfterRun(bool $success, int $recordId, string $description, int $profileId, string $origin): void + { + try { + $app = Factory::getApplication(); + + $event = new Event('onMokoBackupAfterRun', [ + 'success' => $success, + 'record_id' => $recordId, + 'description' => $description, + 'profile_id' => $profileId, + 'origin' => $origin, + ]); + + $app->getDispatcher()->dispatch('onMokoBackupAfterRun', $event); + } catch (\Throwable $e) { + // Never let a listener failure break the backup result + } + } + private function log(string $message): void { $this->log[] = '[' . date('H:i:s') . '] ' . $message; diff --git a/src/packages/com_mokobackup/src/Model/DashboardModel.php b/src/packages/com_mokobackup/src/Model/DashboardModel.php new file mode 100644 index 0000000..d10a2b2 --- /dev/null +++ b/src/packages/com_mokobackup/src/Model/DashboardModel.php @@ -0,0 +1,163 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Component\MokoBackup\Administrator\Model; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\MVC\Model\BaseDatabaseModel; + +class DashboardModel extends BaseDatabaseModel +{ + /** + * Get the most recent completed backup record. + * + * @return object|null + */ + public function getLastBackup(): ?object + { + $db = $this->getDatabase(); + $query = $db->getQuery(true) + ->select('r.*, p.title AS profile_title') + ->from($db->quoteName('#__mokobackup_records', 'r')) + ->join('LEFT', $db->quoteName('#__mokobackup_profiles', 'p') . ' ON p.id = r.profile_id') + ->where($db->quoteName('r.status') . ' = ' . $db->quote('complete')) + ->order($db->quoteName('r.backupend') . ' DESC'); + $db->setQuery($query, 0, 1); + + return $db->loadObject() ?: null; + } + + /** + * Query com_scheduler for the next scheduled MokoBackup task. + * + * @return object|null Object with next_execution and title, or null + */ + public function getNextScheduled(): ?object + { + $db = $this->getDatabase(); + + try { + $query = $db->getQuery(true) + ->select($db->quoteName(['t.next_execution', 't.title'])) + ->from($db->quoteName('#__scheduler_tasks', 't')) + ->where($db->quoteName('t.type') . ' = ' . $db->quote('mokobackup.run_profile')) + ->where($db->quoteName('t.state') . ' = 1') + ->order($db->quoteName('t.next_execution') . ' ASC'); + $db->setQuery($query, 0, 1); + + return $db->loadObject() ?: null; + } catch (\Throwable $e) { + return null; + } + } + + /** + * Get backup statistics. + * + * @return object Object with total_count, total_size, fail_count_7d + */ + public function getStats(): object + { + $db = $this->getDatabase(); + + // Total completed backups and storage + $query = $db->getQuery(true) + ->select('COUNT(*) AS total_count') + ->select('COALESCE(SUM(' . $db->quoteName('total_size') . '), 0) AS total_size') + ->from($db->quoteName('#__mokobackup_records')) + ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); + $db->setQuery($query); + $stats = $db->loadObject(); + + // Failures in last 7 days + $cutoff = date('Y-m-d H:i:s', strtotime('-7 days')); + $query = $db->getQuery(true) + ->select('COUNT(*) AS fail_count') + ->from($db->quoteName('#__mokobackup_records')) + ->where($db->quoteName('status') . ' = ' . $db->quote('fail')) + ->where($db->quoteName('backupstart') . ' >= ' . $db->quote($cutoff)); + $db->setQuery($query); + $stats->fail_count_7d = (int) $db->loadResult(); + + return $stats; + } + + /** + * Check system health for backup readiness. + * + * @return array Array of check results [{label, status, detail}] + */ + public function getSystemHealth(): array + { + $checks = []; + + // PHP version + $checks[] = (object) [ + 'label' => 'PHP Version', + 'status' => version_compare(PHP_VERSION, '8.1.0', '>='), + 'detail' => PHP_VERSION, + ]; + + // ZipArchive extension + $checks[] = (object) [ + 'label' => 'ZipArchive', + 'status' => extension_loaded('zip'), + 'detail' => extension_loaded('zip') ? 'Loaded' : 'Not loaded', + ]; + + // AES-256 encryption support + $aesSupport = defined('ZipArchive::EM_AES_256'); + $checks[] = (object) [ + 'label' => 'AES-256 Encryption', + 'status' => $aesSupport, + 'detail' => $aesSupport ? 'Available' : 'Requires libzip 1.2.0+', + ]; + + // Backup directory writable + $backupDir = JPATH_ADMINISTRATOR . '/components/com_mokobackup/backups'; + $writable = is_dir($backupDir) && is_writable($backupDir); + $checks[] = (object) [ + 'label' => 'Backup Directory', + 'status' => $writable, + 'detail' => $writable ? 'Writable' : 'Not writable or missing', + ]; + + // Disk space + $freeSpace = @disk_free_space($backupDir ?: JPATH_ROOT); + $freeGB = $freeSpace ? round($freeSpace / 1073741824, 1) : 0; + $checks[] = (object) [ + 'label' => 'Free Disk Space', + 'status' => $freeGB >= 1.0, + 'detail' => $freeGB . ' GB free', + ]; + + return $checks; + } + + /** + * Get published backup profiles for the quick-action selector. + * + * @return array + */ + public function getProfiles(): array + { + $db = $this->getDatabase(); + $query = $db->getQuery(true) + ->select($db->quoteName(['id', 'title', 'backup_type'])) + ->from($db->quoteName('#__mokobackup_profiles')) + ->where($db->quoteName('published') . ' = 1') + ->order($db->quoteName('ordering') . ' ASC'); + $db->setQuery($query); + + return $db->loadObjectList() ?: []; + } +} diff --git a/src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php b/src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php new file mode 100644 index 0000000..bbfa660 --- /dev/null +++ b/src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php @@ -0,0 +1,48 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Component\MokoBackup\Administrator\View\Dashboard; + +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 +{ + public ?object $lastBackup = null; + public ?object $nextScheduled = null; + public object $stats; + public array $systemHealth = []; + public array $profiles = []; + + public function display($tpl = null): void + { + /** @var \Joomla\Component\MokoBackup\Administrator\Model\DashboardModel $model */ + $model = $this->getModel(); + + $this->lastBackup = $model->getLastBackup(); + $this->nextScheduled = $model->getNextScheduled(); + $this->stats = $model->getStats(); + $this->systemHealth = $model->getSystemHealth(); + $this->profiles = $model->getProfiles(); + + $this->addToolbar(); + + parent::display($tpl); + } + + protected function addToolbar(): void + { + ToolbarHelper::title(Text::_('COM_MOKOBACKUP_DASHBOARD_TITLE'), 'archive'); + ToolbarHelper::preferences('com_mokobackup'); + } +} diff --git a/src/packages/com_mokobackup/tmpl/dashboard/default.php b/src/packages/com_mokobackup/tmpl/dashboard/default.php new file mode 100644 index 0000000..8d7d43c --- /dev/null +++ b/src/packages/com_mokobackup/tmpl/dashboard/default.php @@ -0,0 +1,265 @@ + + * @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\HTML\HTMLHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Session\Session; + +$ajaxToken = Session::getFormToken(); +$ajaxUrl = Route::_('index.php?option=com_mokobackup&format=json', false); +?> +
+ +
+
+
+ +
+ lastBackup) : ?> +

+ lastBackup->backupend, Text::_('DATE_FORMAT_LC4')); ?> +

+ + escape($this->lastBackup->profile_title); ?> + — + lastBackup->total_size); ?> + + +

+ +
+
+
+ +
+
+
+ +
+ nextScheduled) : ?> +

+ nextScheduled->next_execution, Text::_('DATE_FORMAT_LC4')); ?> +

+ escape($this->nextScheduled->title); ?> + +

+ +
+
+
+ +
+
+
+ +
+

stats->total_count; ?>

+
+
+
+ +
+
+
+ +
+

+ stats->total_size); ?> +

+ stats->fail_count_7d > 0) : ?> + + stats->fail_count_7d); ?> + + +
+
+
+
+ + +
+
+
+
+
+
+
+ profiles)) : ?> +
+ + +
+ + + +
+
+
+ + +
+
+
+
+
+
+ + + systemHealth as $check) : ?> + + + + + + + +
+ status) : ?> + + + + + escape($check->label); ?>escape($check->detail); ?>
+
+
+
+
+ + + + + diff --git a/src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini b/src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini new file mode 100644 index 0000000..6997740 --- /dev/null +++ b/src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — Actionlog Plugin language file (en-GB) +PLG_ACTIONLOG_MOKOBACKUP="Action Log - MokoJoomBackup" +PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." +PLG_ACTIONLOG_MOKOBACKUP_PROFILE_CREATED="User {username} created backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOBACKUP_PROFILE_UPDATED="User {username} updated backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOBACKUP_PROFILE_DELETED="User {username} deleted backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOBACKUP_RECORD_DELETED="User {username} deleted backup record "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOBACKUP_BACKUP_COMPLETE="Backup completed: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" +PLG_ACTIONLOG_MOKOBACKUP_BACKUP_FAILED="Backup FAILED: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" diff --git a/src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini b/src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini new file mode 100644 index 0000000..3e1c655 --- /dev/null +++ b/src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Actionlog Plugin system language file (en-GB) +PLG_ACTIONLOG_MOKOBACKUP="Action Log - MokoJoomBackup" +PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." diff --git a/src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini b/src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini new file mode 100644 index 0000000..27cf1d6 --- /dev/null +++ b/src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — Actionlog Plugin language file (en-US) +PLG_ACTIONLOG_MOKOBACKUP="Action Log - MokoJoomBackup" +PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." +PLG_ACTIONLOG_MOKOBACKUP_PROFILE_CREATED="User {username} created backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOBACKUP_PROFILE_UPDATED="User {username} updated backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOBACKUP_PROFILE_DELETED="User {username} deleted backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOBACKUP_RECORD_DELETED="User {username} deleted backup record "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOBACKUP_BACKUP_COMPLETE="Backup completed: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" +PLG_ACTIONLOG_MOKOBACKUP_BACKUP_FAILED="Backup FAILED: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" diff --git a/src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini b/src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini new file mode 100644 index 0000000..1737124 --- /dev/null +++ b/src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Actionlog Plugin system language file (en-US) +PLG_ACTIONLOG_MOKOBACKUP="Action Log - MokoJoomBackup" +PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." diff --git a/src/packages/plg_actionlog_mokobackup/mokobackup.php b/src/packages/plg_actionlog_mokobackup/mokobackup.php new file mode 100644 index 0000000..2a4226a --- /dev/null +++ b/src/packages/plg_actionlog_mokobackup/mokobackup.php @@ -0,0 +1,11 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; diff --git a/src/packages/plg_actionlog_mokobackup/mokobackup.xml b/src/packages/plg_actionlog_mokobackup/mokobackup.xml new file mode 100644 index 0000000..b5fe605 --- /dev/null +++ b/src/packages/plg_actionlog_mokobackup/mokobackup.xml @@ -0,0 +1,32 @@ + + + + plg_actionlog_mokobackup + 01.01.04-dev + 2026-06-04 + Moko Consulting + hello@mokoconsulting.tech + https://mokoconsulting.tech + Copyright (C) 2026 Moko Consulting. All rights reserved. + GPL-3.0-or-later + PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION + + Joomla\Plugin\Actionlog\MokoBackup + + + mokobackup.php + services + src + + + + language/en-GB/plg_actionlog_mokobackup.ini + language/en-GB/plg_actionlog_mokobackup.sys.ini + + diff --git a/src/packages/plg_actionlog_mokobackup/services/provider.php b/src/packages/plg_actionlog_mokobackup/services/provider.php new file mode 100644 index 0000000..b13a445 --- /dev/null +++ b/src/packages/plg_actionlog_mokobackup/services/provider.php @@ -0,0 +1,37 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\Actionlog\MokoBackup\Extension\MokoBackupActionlog; + +return new class () implements ServiceProviderInterface { + public function register(Container $container): void + { + $container->set( + PluginInterface::class, + function (Container $container) { + $plugin = new MokoBackupActionlog( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('actionlog', 'mokobackup') + ); + $plugin->setApplication(Factory::getApplication()); + + return $plugin; + } + ); + } +}; diff --git a/src/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php b/src/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php new file mode 100644 index 0000000..2cb97e7 --- /dev/null +++ b/src/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php @@ -0,0 +1,174 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Plugin\Actionlog\MokoBackup\Extension; + +defined('_JEXEC') or die; + +use Joomla\CMS\Event\Model; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper; +use Joomla\Event\Event; +use Joomla\Event\SubscriberInterface; + +final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface +{ + protected $autoloadLanguage = true; + + public static function getSubscribedEvents(): array + { + return [ + 'onContentAfterSave' => 'onContentAfterSave', + 'onContentAfterDelete' => 'onContentAfterDelete', + 'onMokoBackupAfterRun' => 'onMokoBackupAfterRun', + ]; + } + + /** + * Log when a backup profile is saved (created or updated). + */ + public function onContentAfterSave(Event $event): void + { + [$context, $table, $isNew] = array_values($event->getArguments()); + + if ($context !== 'com_mokobackup.profile') { + return; + } + + $messageKey = $isNew + ? 'PLG_ACTIONLOG_MOKOBACKUP_PROFILE_CREATED' + : 'PLG_ACTIONLOG_MOKOBACKUP_PROFILE_UPDATED'; + + $this->addLog( + [ + $messageKey, + 'id' => $table->id, + 'title' => $table->title, + 'userid' => $this->getCurrentUserId(), + 'username' => $this->getCurrentUserName(), + ], + $messageKey, + 'com_mokobackup.profile', + $this->getCurrentUserId() + ); + } + + /** + * Log when a backup profile or record is deleted. + */ + public function onContentAfterDelete(Event $event): void + { + [$context, $table] = array_values($event->getArguments()); + + if ($context === 'com_mokobackup.profile') { + $this->addLog( + [ + 'PLG_ACTIONLOG_MOKOBACKUP_PROFILE_DELETED', + 'id' => $table->id, + 'title' => $table->title ?? '', + 'userid' => $this->getCurrentUserId(), + 'username' => $this->getCurrentUserName(), + ], + 'PLG_ACTIONLOG_MOKOBACKUP_PROFILE_DELETED', + 'com_mokobackup.profile', + $this->getCurrentUserId() + ); + } elseif ($context === 'com_mokobackup.backup') { + $this->addLog( + [ + 'PLG_ACTIONLOG_MOKOBACKUP_RECORD_DELETED', + 'id' => $table->id, + 'title' => $table->description ?? 'Backup #' . $table->id, + 'userid' => $this->getCurrentUserId(), + 'username' => $this->getCurrentUserName(), + ], + 'PLG_ACTIONLOG_MOKOBACKUP_RECORD_DELETED', + 'com_mokobackup.backup', + $this->getCurrentUserId() + ); + } + } + + /** + * Log when a backup completes or fails. + * This event should be dispatched from BackupEngine. + */ + public function onMokoBackupAfterRun(Event $event): void + { + $args = $event->getArguments(); + + $success = $args['success'] ?? false; + $recordId = $args['record_id'] ?? 0; + $description = $args['description'] ?? ''; + $profileId = $args['profile_id'] ?? 0; + $origin = $args['origin'] ?? 'backend'; + + $messageKey = $success + ? 'PLG_ACTIONLOG_MOKOBACKUP_BACKUP_COMPLETE' + : 'PLG_ACTIONLOG_MOKOBACKUP_BACKUP_FAILED'; + + $this->addLog( + [ + $messageKey, + 'id' => $recordId, + 'title' => $description ?: 'Backup #' . $recordId, + 'profile_id' => $profileId, + 'origin' => $origin, + 'userid' => $this->getCurrentUserId(), + 'username' => $this->getCurrentUserName(), + ], + $messageKey, + 'com_mokobackup.backup', + $this->getCurrentUserId() + ); + } + + /** + * Write an action log entry. + */ + private function addLog(array $message, string $messageLanguageKey, string $context, int $userId): void + { + $params = [ + 'message_language_key' => $messageLanguageKey, + 'message' => json_encode($message), + 'date' => date('Y-m-d H:i:s'), + 'extension' => 'com_mokobackup', + 'user_id' => $userId, + 'ip_address' => ActionlogsHelper::getIp(), + 'item_id' => $message['id'] ?? 0, + ]; + + try { + $db = Factory::getDbo(); + $db->insertObject('#__action_logs', (object) $params); + } catch (\Throwable $e) { + // Non-critical — don't break the operation + } + } + + private function getCurrentUserId(): int + { + try { + return (int) Factory::getApplication()->getIdentity()->id; + } catch (\Throwable $e) { + return 0; + } + } + + private function getCurrentUserName(): string + { + try { + return Factory::getApplication()->getIdentity()->username ?: 'system'; + } catch (\Throwable $e) { + return 'system'; + } + } +} diff --git a/src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini b/src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini new file mode 100644 index 0000000..4b87bca --- /dev/null +++ b/src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Console Plugin language file (en-GB) +PLG_CONSOLE_MOKOBACKUP="Console - MokoJoomBackup" +PLG_CONSOLE_MOKOBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini b/src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini new file mode 100644 index 0000000..02fb8d8 --- /dev/null +++ b/src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Console Plugin system language file (en-GB) +PLG_CONSOLE_MOKOBACKUP="Console - MokoJoomBackup" +PLG_CONSOLE_MOKOBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini b/src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini new file mode 100644 index 0000000..9fa5c15 --- /dev/null +++ b/src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Console Plugin language file (en-US) +PLG_CONSOLE_MOKOBACKUP="Console - MokoJoomBackup" +PLG_CONSOLE_MOKOBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini b/src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini new file mode 100644 index 0000000..d22c08c --- /dev/null +++ b/src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Console Plugin system language file (en-US) +PLG_CONSOLE_MOKOBACKUP="Console - MokoJoomBackup" +PLG_CONSOLE_MOKOBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/src/packages/plg_console_mokobackup/mokobackup.php b/src/packages/plg_console_mokobackup/mokobackup.php new file mode 100644 index 0000000..724a1bb --- /dev/null +++ b/src/packages/plg_console_mokobackup/mokobackup.php @@ -0,0 +1,11 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; diff --git a/src/packages/plg_console_mokobackup/mokobackup.xml b/src/packages/plg_console_mokobackup/mokobackup.xml new file mode 100644 index 0000000..c641770 --- /dev/null +++ b/src/packages/plg_console_mokobackup/mokobackup.xml @@ -0,0 +1,32 @@ + + + + plg_console_mokobackup + 01.01.04-dev + 2026-06-04 + Moko Consulting + hello@mokoconsulting.tech + https://mokoconsulting.tech + Copyright (C) 2026 Moko Consulting. All rights reserved. + GPL-3.0-or-later + PLG_CONSOLE_MOKOBACKUP_DESCRIPTION + + Joomla\Plugin\Console\MokoBackup + + + mokobackup.php + services + src + + + + language/en-GB/plg_console_mokobackup.ini + language/en-GB/plg_console_mokobackup.sys.ini + + diff --git a/src/packages/plg_console_mokobackup/services/provider.php b/src/packages/plg_console_mokobackup/services/provider.php new file mode 100644 index 0000000..3bacb2f --- /dev/null +++ b/src/packages/plg_console_mokobackup/services/provider.php @@ -0,0 +1,37 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\Console\MokoBackup\Extension\MokoBackupConsole; + +return new class () implements ServiceProviderInterface { + public function register(Container $container): void + { + $container->set( + PluginInterface::class, + function (Container $container) { + $plugin = new MokoBackupConsole( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('console', 'mokobackup') + ); + $plugin->setApplication(Factory::getApplication()); + + return $plugin; + } + ); + } +}; diff --git a/src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php b/src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php new file mode 100644 index 0000000..b26a2a5 --- /dev/null +++ b/src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php @@ -0,0 +1,125 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Plugin\Console\MokoBackup\Command; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\Console\Command\AbstractCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class CleanupCommand extends AbstractCommand +{ + protected static $defaultName = 'mokobackup:cleanup'; + + protected function configure(): void + { + $this->setDescription('Clean up old backup records and archive files'); + $this->addOption('max-age', null, InputOption::VALUE_REQUIRED, 'Max age in days', '30'); + $this->addOption('max-count', null, InputOption::VALUE_REQUIRED, 'Max number of backups to keep', '10'); + $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Show what would be deleted without deleting'); + } + + protected function doExecute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $maxAge = (int) $input->getOption('max-age'); + $maxCount = (int) $input->getOption('max-count'); + $dryRun = $input->getOption('dry-run'); + + $io->title('MokoJoomBackup — Cleanup'); + + if ($dryRun) { + $io->note('Dry run — no files will be deleted.'); + } + + $db = Factory::getDbo(); + $deleted = 0; + + // Delete by age + $cutoff = date('Y-m-d H:i:s', strtotime("-{$maxAge} days")); + $query = $db->getQuery(true) + ->select('id, absolute_path, description, backupstart') + ->from($db->quoteName('#__mokobackup_records')) + ->where($db->quoteName('backupstart') . ' < ' . $db->quote($cutoff)) + ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); + $db->setQuery($query); + $expired = $db->loadObjectList(); + + foreach ($expired as $record) { + $io->text('Expired: #' . $record->id . ' — ' . $record->backupstart . ' — ' . ($record->description ?: 'no description')); + + if (!$dryRun) { + if (!empty($record->absolute_path) && is_file($record->absolute_path)) { + @unlink($record->absolute_path); + } + + $db->setQuery( + $db->getQuery(true) + ->delete($db->quoteName('#__mokobackup_records')) + ->where($db->quoteName('id') . ' = ' . (int) $record->id) + ); + $db->execute(); + } + + $deleted++; + } + + // Enforce max count + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($db->quoteName('#__mokobackup_records')) + ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); + $db->setQuery($query); + $totalCount = (int) $db->loadResult(); + + if ($totalCount > $maxCount) { + $excess = $totalCount - $maxCount; + $query = $db->getQuery(true) + ->select('id, absolute_path, description, backupstart') + ->from($db->quoteName('#__mokobackup_records')) + ->where($db->quoteName('status') . ' = ' . $db->quote('complete')) + ->order($db->quoteName('backupstart') . ' ASC'); + $db->setQuery($query, 0, $excess); + $oldest = $db->loadObjectList(); + + foreach ($oldest as $record) { + $io->text('Over limit: #' . $record->id . ' — ' . $record->backupstart); + + if (!$dryRun) { + if (!empty($record->absolute_path) && is_file($record->absolute_path)) { + @unlink($record->absolute_path); + } + + $db->setQuery( + $db->getQuery(true) + ->delete($db->quoteName('#__mokobackup_records')) + ->where($db->quoteName('id') . ' = ' . (int) $record->id) + ); + $db->execute(); + } + + $deleted++; + } + } + + if ($deleted === 0) { + $io->success('No backups to clean up.'); + } else { + $io->success(($dryRun ? 'Would delete ' : 'Deleted ') . $deleted . ' backup record(s).'); + } + + return self::SUCCESS; + } +} diff --git a/src/packages/plg_console_mokobackup/src/Command/ListCommand.php b/src/packages/plg_console_mokobackup/src/Command/ListCommand.php new file mode 100644 index 0000000..8fcdddd --- /dev/null +++ b/src/packages/plg_console_mokobackup/src/Command/ListCommand.php @@ -0,0 +1,87 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Plugin\Console\MokoBackup\Command; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\Console\Command\AbstractCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class ListCommand extends AbstractCommand +{ + protected static $defaultName = 'mokobackup:list'; + + protected function configure(): void + { + $this->setDescription('List backup records'); + $this->addOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Number of records to show', '20'); + $this->addOption('status', 's', InputOption::VALUE_OPTIONAL, 'Filter by status (complete, fail, running)'); + } + + protected function doExecute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $limit = (int) $input->getOption('limit'); + $status = $input->getOption('status'); + + $io->title('MokoJoomBackup — Backup Records'); + + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select('r.id, r.description, r.status, r.origin, r.backup_type, r.total_size, r.backupstart, r.backupend') + ->select($db->quoteName('p.title', 'profile_title')) + ->from($db->quoteName('#__mokobackup_records', 'r')) + ->join('LEFT', $db->quoteName('#__mokobackup_profiles', 'p') . ' ON p.id = r.profile_id') + ->order($db->quoteName('r.backupstart') . ' DESC'); + + if ($status) { + $query->where($db->quoteName('r.status') . ' = ' . $db->quote($status)); + } + + $db->setQuery($query, 0, $limit); + $records = $db->loadObjectList(); + + if (empty($records)) { + $io->info('No backup records found.'); + + return self::SUCCESS; + } + + $rows = []; + + foreach ($records as $record) { + $size = $record->total_size > 0 + ? round($record->total_size / 1048576, 2) . ' MB' + : '—'; + + $rows[] = [ + $record->id, + $record->profile_title ?: '—', + $record->status, + $record->backup_type, + $size, + $record->origin, + $record->backupstart, + ]; + } + + $io->table( + ['ID', 'Profile', 'Status', 'Type', 'Size', 'Origin', 'Started'], + $rows + ); + + return self::SUCCESS; + } +} diff --git a/src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php b/src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php new file mode 100644 index 0000000..4d81616 --- /dev/null +++ b/src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php @@ -0,0 +1,68 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Plugin\Console\MokoBackup\Command; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\Console\Command\AbstractCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class ProfilesCommand extends AbstractCommand +{ + protected static $defaultName = 'mokobackup:profiles'; + + protected function configure(): void + { + $this->setDescription('List available backup profiles'); + } + + protected function doExecute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $io->title('MokoJoomBackup — Backup Profiles'); + + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select('id, title, backup_type, published, ordering') + ->from($db->quoteName('#__mokobackup_profiles')) + ->order($db->quoteName('ordering') . ' ASC'); + $db->setQuery($query); + $profiles = $db->loadObjectList(); + + if (empty($profiles)) { + $io->info('No backup profiles found.'); + + return self::SUCCESS; + } + + $rows = []; + + foreach ($profiles as $profile) { + $rows[] = [ + $profile->id, + $profile->title, + $profile->backup_type, + $profile->published ? 'Yes' : 'No', + ]; + } + + $io->table( + ['ID', 'Title', 'Type', 'Published'], + $rows + ); + + return self::SUCCESS; + } +} diff --git a/src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php b/src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php new file mode 100644 index 0000000..fb857d4 --- /dev/null +++ b/src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php @@ -0,0 +1,101 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Plugin\Console\MokoBackup\Command; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\Component\MokoBackup\Administrator\Engine\RestoreEngine; +use Joomla\Console\Command\AbstractCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class RestoreCommand extends AbstractCommand +{ + protected static $defaultName = 'mokobackup:restore'; + + protected function configure(): void + { + $this->setDescription('Restore a backup by record ID'); + $this->addArgument('id', InputArgument::REQUIRED, 'Backup record ID to restore'); + } + + protected function doExecute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $recordId = (int) $input->getArgument('id'); + + $io->title('MokoJoomBackup — Restore Backup'); + + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__mokobackup_records')) + ->where($db->quoteName('id') . ' = ' . $recordId); + $db->setQuery($query); + $record = $db->loadObject(); + + if (!$record) { + $io->error('Backup record not found: ' . $recordId); + + return self::FAILURE; + } + + if ($record->status !== 'complete') { + $io->error('Cannot restore — backup status is: ' . $record->status); + + return self::FAILURE; + } + + if (empty($record->absolute_path) || !is_file($record->absolute_path)) { + $io->error('Backup archive not found: ' . ($record->absolute_path ?: 'no path')); + + return self::FAILURE; + } + + $io->warning('This will overwrite the current site files and/or database.'); + $io->text('Archive: ' . $record->absolute_path); + $io->text('Type: ' . $record->backup_type); + + if (!$io->confirm('Are you sure you want to continue?', false)) { + $io->info('Restore cancelled.'); + + return self::SUCCESS; + } + + $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokobackup/src/Engine/RestoreEngine.php'; + + if (!file_exists($engineFile)) { + $io->error('RestoreEngine not found. Is the component fully installed?'); + + return self::FAILURE; + } + + if (!class_exists(RestoreEngine::class)) { + require_once $engineFile; + } + + $engine = new RestoreEngine(); + $result = $engine->restore($record->absolute_path, $record->backup_type); + + if ($result['success']) { + $io->success($result['message']); + + return self::SUCCESS; + } + + $io->error($result['message']); + + return self::FAILURE; + } +} diff --git a/src/packages/plg_console_mokobackup/src/Command/RunCommand.php b/src/packages/plg_console_mokobackup/src/Command/RunCommand.php new file mode 100644 index 0000000..52185b9 --- /dev/null +++ b/src/packages/plg_console_mokobackup/src/Command/RunCommand.php @@ -0,0 +1,68 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Plugin\Console\MokoBackup\Command; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\Component\MokoBackup\Administrator\Engine\BackupEngine; +use Joomla\Console\Command\AbstractCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class RunCommand extends AbstractCommand +{ + protected static $defaultName = 'mokobackup:run'; + + protected function configure(): void + { + $this->setDescription('Run a backup using a specified profile'); + $this->addOption('profile', 'p', InputOption::VALUE_REQUIRED, 'Profile ID to use', '1'); + $this->addOption('description', 'd', InputOption::VALUE_OPTIONAL, 'Backup description', ''); + } + + protected function doExecute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $profileId = (int) $input->getOption('profile'); + $desc = $input->getOption('description') ?: ''; + + $io->title('MokoJoomBackup — Run Backup'); + $io->text('Profile ID: ' . $profileId); + + $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokobackup/src/Engine/BackupEngine.php'; + + if (!file_exists($engineFile)) { + $io->error('MokoJoomBackup component not installed.'); + + return self::FAILURE; + } + + if (!class_exists(BackupEngine::class)) { + require_once $engineFile; + } + + $engine = new BackupEngine(); + $result = $engine->run($profileId, $desc ?: 'CLI backup', 'cli'); + + if ($result['success']) { + $io->success($result['message']); + + return self::SUCCESS; + } + + $io->error($result['message']); + + return self::FAILURE; + } +} diff --git a/src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php b/src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php new file mode 100644 index 0000000..5c2ee4f --- /dev/null +++ b/src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php @@ -0,0 +1,45 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Plugin\Console\MokoBackup\Extension; + +defined('_JEXEC') or die; + +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Event\Event; +use Joomla\Event\SubscriberInterface; +use Joomla\Plugin\Console\MokoBackup\Command\CleanupCommand; +use Joomla\Plugin\Console\MokoBackup\Command\ListCommand; +use Joomla\Plugin\Console\MokoBackup\Command\ProfilesCommand; +use Joomla\Plugin\Console\MokoBackup\Command\RestoreCommand; +use Joomla\Plugin\Console\MokoBackup\Command\RunCommand; + +final class MokoBackupConsole extends CMSPlugin implements SubscriberInterface +{ + protected $autoloadLanguage = true; + + public static function getSubscribedEvents(): array + { + return [ + \Joomla\Application\Event\ApplicationEvents::BEFORE_EXECUTE => 'registerCommands', + ]; + } + + public function registerCommands(Event $event): void + { + $app = $this->getApplication(); + + $app->addCommand(new RunCommand()); + $app->addCommand(new ListCommand()); + $app->addCommand(new ProfilesCommand()); + $app->addCommand(new RestoreCommand()); + $app->addCommand(new CleanupCommand()); + } +} diff --git a/src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini b/src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini new file mode 100644 index 0000000..5f23262 --- /dev/null +++ b/src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — Content Plugin language file (en-GB) +PLG_CONTENT_MOKOBACKUP="Content - MokoJoomBackup" +PLG_CONTENT_MOKOBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." +PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_INSTALL="Backup Before Install" +PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_INSTALL_DESC="Run an automatic backup before a new extension is installed." +PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_UPDATE="Backup Before Update" +PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_UPDATE_DESC="Run an automatic backup before an extension is updated." +PLG_CONTENT_MOKOBACKUP_FIELD_PROFILE="Backup Profile" +PLG_CONTENT_MOKOBACKUP_FIELD_PROFILE_DESC="Which backup profile to use for automatic backups." diff --git a/src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini b/src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini new file mode 100644 index 0000000..3d79871 --- /dev/null +++ b/src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Content Plugin system language file (en-GB) +PLG_CONTENT_MOKOBACKUP="Content - MokoJoomBackup" +PLG_CONTENT_MOKOBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." diff --git a/src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini b/src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini new file mode 100644 index 0000000..1bac9a8 --- /dev/null +++ b/src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — Content Plugin language file (en-US) +PLG_CONTENT_MOKOBACKUP="Content - MokoJoomBackup" +PLG_CONTENT_MOKOBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." +PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_INSTALL="Backup Before Install" +PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_INSTALL_DESC="Run an automatic backup before a new extension is installed." +PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_UPDATE="Backup Before Update" +PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_UPDATE_DESC="Run an automatic backup before an extension is updated." +PLG_CONTENT_MOKOBACKUP_FIELD_PROFILE="Backup Profile" +PLG_CONTENT_MOKOBACKUP_FIELD_PROFILE_DESC="Which backup profile to use for automatic backups." diff --git a/src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini b/src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini new file mode 100644 index 0000000..7a612b3 --- /dev/null +++ b/src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Content Plugin system language file (en-US) +PLG_CONTENT_MOKOBACKUP="Content - MokoJoomBackup" +PLG_CONTENT_MOKOBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." diff --git a/src/packages/plg_content_mokobackup/mokobackup.php b/src/packages/plg_content_mokobackup/mokobackup.php new file mode 100644 index 0000000..2dd15e4 --- /dev/null +++ b/src/packages/plg_content_mokobackup/mokobackup.php @@ -0,0 +1,11 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; diff --git a/src/packages/plg_content_mokobackup/mokobackup.xml b/src/packages/plg_content_mokobackup/mokobackup.xml new file mode 100644 index 0000000..a337b10 --- /dev/null +++ b/src/packages/plg_content_mokobackup/mokobackup.xml @@ -0,0 +1,71 @@ + + + + plg_content_mokobackup + 01.01.04-dev + 2026-06-04 + Moko Consulting + hello@mokoconsulting.tech + https://mokoconsulting.tech + Copyright (C) 2026 Moko Consulting. All rights reserved. + GPL-3.0-or-later + PLG_CONTENT_MOKOBACKUP_DESCRIPTION + + Joomla\Plugin\Content\MokoBackup + + + mokobackup.php + services + src + + + + language/en-GB/plg_content_mokobackup.ini + language/en-GB/plg_content_mokobackup.sys.ini + + + + +
+ + + + + + + + + + + +
+
+
+
diff --git a/src/packages/plg_content_mokobackup/services/provider.php b/src/packages/plg_content_mokobackup/services/provider.php new file mode 100644 index 0000000..4635162 --- /dev/null +++ b/src/packages/plg_content_mokobackup/services/provider.php @@ -0,0 +1,37 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\Content\MokoBackup\Extension\MokoBackupContent; + +return new class () implements ServiceProviderInterface { + public function register(Container $container): void + { + $container->set( + PluginInterface::class, + function (Container $container) { + $plugin = new MokoBackupContent( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('content', 'mokobackup') + ); + $plugin->setApplication(Factory::getApplication()); + + return $plugin; + } + ); + } +}; diff --git a/src/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php b/src/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php new file mode 100644 index 0000000..b27d119 --- /dev/null +++ b/src/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php @@ -0,0 +1,95 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Plugin\Content\MokoBackup\Extension; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Component\MokoBackup\Administrator\Engine\BackupEngine; +use Joomla\Event\Event; +use Joomla\Event\SubscriberInterface; + +final class MokoBackupContent extends CMSPlugin implements SubscriberInterface +{ + protected $autoloadLanguage = true; + + public static function getSubscribedEvents(): array + { + return [ + 'onExtensionBeforeInstall' => 'onExtensionBeforeInstall', + 'onExtensionBeforeUpdate' => 'onExtensionBeforeUpdate', + ]; + } + + /** + * Trigger a backup before a new extension is installed. + */ + public function onExtensionBeforeInstall(Event $event): void + { + if (!(int) $this->params->get('backup_before_install', 0)) { + return; + } + + $this->triggerAutoBackup('Pre-install backup'); + } + + /** + * Trigger a backup before an extension is updated. + */ + public function onExtensionBeforeUpdate(Event $event): void + { + if (!(int) $this->params->get('backup_before_update', 1)) { + return; + } + + $this->triggerAutoBackup('Pre-update backup'); + } + + /** + * Run a backup using the configured profile. + */ + private function triggerAutoBackup(string $description): void + { + $profileId = (int) $this->params->get('profile_id', 1); + + // Throttle: only one auto-backup per hour via session + $session = Factory::getSession(); + $lastRun = $session->get('mokobackup.content_last_autobackup', 0); + + if (time() - $lastRun < 3600) { + return; + } + + $session->set('mokobackup.content_last_autobackup', time()); + + $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokobackup/src/Engine/BackupEngine.php'; + + if (!file_exists($engineFile)) { + return; + } + + if (!class_exists(BackupEngine::class)) { + require_once $engineFile; + } + + try { + $engine = new BackupEngine(); + $engine->run($profileId, $description, 'backend'); + } catch (\Throwable $e) { + // Non-fatal — log and continue with the install/update + Factory::getApplication()->enqueueMessage( + 'MokoJoomBackup auto-backup failed: ' . $e->getMessage(), + 'warning' + ); + } + } +} diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index d02c244..b370006 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -25,6 +25,9 @@ plg_task_mokobackup.zip plg_quickicon_mokobackup.zip plg_webservices_mokobackup.zip + plg_console_mokobackup.zip + plg_content_mokobackup.zip + plg_actionlog_mokobackup.zip
diff --git a/src/script.php b/src/script.php index ea6ed40..409a88c 100644 --- a/src/script.php +++ b/src/script.php @@ -108,6 +108,39 @@ class Pkg_MokoBackupInstallerScript $db->setQuery($query); $db->execute(); + // Enable the console plugin automatically + $query = $db->getQuery(true) + ->update($db->quoteName('#__extensions')) + ->set($db->quoteName('enabled') . ' = 1') + ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) + ->where($db->quoteName('folder') . ' = ' . $db->quote('console')) + ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + + $db->setQuery($query); + $db->execute(); + + // Enable the content plugin automatically + $query = $db->getQuery(true) + ->update($db->quoteName('#__extensions')) + ->set($db->quoteName('enabled') . ' = 1') + ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) + ->where($db->quoteName('folder') . ' = ' . $db->quote('content')) + ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + + $db->setQuery($query); + $db->execute(); + + // Enable the actionlog plugin automatically + $query = $db->getQuery(true) + ->update($db->quoteName('#__extensions')) + ->set($db->quoteName('enabled') . ' = 1') + ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) + ->where($db->quoteName('folder') . ' = ' . $db->quote('actionlog')) + ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + + $db->setQuery($query); + $db->execute(); + // Create default backup directory $backupDir = JPATH_ADMINISTRATOR . '/components/com_mokobackup/backups'; -- 2.52.0 From 5ab6296ad5ec4ae43394a070557d1c4755812f16 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 16:29:55 +0000 Subject: [PATCH 30/81] chore(version): auto-bump 01.01.05-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- .mokogitea/workflows/pr-check.yml | 1016 ++++++++--------- README.md | 2 +- src/packages/com_mokobackup/mokobackup.xml | 2 +- .../plg_actionlog_mokobackup/mokobackup.xml | 2 +- .../plg_console_mokobackup/mokobackup.xml | 2 +- .../plg_content_mokobackup/mokobackup.xml | 2 +- .../plg_quickicon_mokobackup/mokobackup.xml | 2 +- .../plg_system_mokobackup/mokobackup.xml | 2 +- .../plg_task_mokobackup/mokobackup.xml | 2 +- .../plg_webservices_mokobackup/mokobackup.xml | 2 +- src/pkg_mokobackup.xml | 2 +- 13 files changed, 520 insertions(+), 520 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 3e76d1d..512869a 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.04-dev + 01.01.05-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 413ef84..09a1a30 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.04 +# VERSION: 01.01.05 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 4d78d7a..6625857 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -1,508 +1,508 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.CI -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform -# PATH: /templates/workflows/universal/pr-check.yml.template -# VERSION: 09.23.00 -# BRIEF: PR gate — branch policy + code validation before merge - -name: "Universal: PR Check" - -on: - pull_request: - types: [opened, synchronize, reopened, edited] - -permissions: - contents: read - pull-requests: write - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - # ── Branch Policy ────────────────────────────────────────────────────── - branch-policy: - name: Branch Policy - runs-on: ubuntu-latest - steps: - - name: Check branch merge target - run: | - HEAD="${{ github.head_ref }}" - BASE="${{ github.base_ref }}" - - echo "PR: ${HEAD} → ${BASE}" - - ALLOWED=true - REASON="" - - case "$HEAD" in - feature/*|feat/*) - if [ "$BASE" != "dev" ]; then - ALLOWED=false - REASON="Feature branches must target 'dev', not '${BASE}'" - fi - ;; - fix/*|bugfix/*) - if [ "$BASE" != "dev" ]; then - ALLOWED=false - REASON="Fix branches must target 'dev', not '${BASE}'" - fi - ;; - patch/*) - if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then - ALLOWED=false - REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'" - fi - ;; - hotfix/*) - if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then - ALLOWED=false - REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'" - fi - ;; - rc) - if [ "$BASE" != "main" ]; then - ALLOWED=false - REASON="RC branch can only merge into 'main', not '${BASE}'" - fi - ;; - dev) - if [ "$BASE" != "main" ]; then - ALLOWED=false - REASON="Dev branch can only merge into 'main', not '${BASE}'" - fi - ;; - esac - - if [ "$ALLOWED" = false ]; then - echo "::error::${REASON}" - echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "${REASON}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY - echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY - echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY - echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY - echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY - echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY - exit 1 - fi - - echo "Branch policy: OK (${HEAD} → ${BASE})" - echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY - - # ── Code Validation ──────────────────────────────────────────────────── - validate: - name: Validate PR - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Check for merge conflict markers - run: | - CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) - if [ -n "$CONFLICTS" ]; then - echo "::error::Merge conflict markers found in source files" - echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - exit 1 - fi - echo "No conflict markers found" - - - name: Detect platform - id: platform - run: | - # Read platform from XML manifest ( tag) or plain text fallback - PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1) - [ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]') - [ -z "$PLATFORM" ] && PLATFORM="generic" - echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" - - - name: Setup PHP - if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' - run: | - if ! command -v php &> /dev/null; then - sudo apt-get update -qq - sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1 - fi - - - name: PHP syntax check - if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' - run: | - ERRORS=0 - while IFS= read -r -d '' file; do - if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then - ERRORS=$((ERRORS + 1)) - fi - done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -print0) - echo "PHP lint: ${ERRORS} error(s)" - [ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; } - - - name: Joomla JEXEC guard check - if: steps.platform.outputs.platform == 'joomla' - run: | - ERRORS=0 - while IFS= read -r -d '' file; do - # Skip vendor, node_modules, and index.html stub files - case "$file" in ./vendor/*|./node_modules/*) continue ;; esac - # Check first 10 lines for JEXEC or JPATH guard - if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then - echo "::error file=${file}::Missing JEXEC guard: ${file}" - ERRORS=$((ERRORS + 1)) - fi - done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0) - if [ "$ERRORS" -gt 0 ]; then - echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard" - echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY - echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY - exit 1 - fi - echo "JEXEC guard: OK" - - - name: Joomla directory listing protection - if: steps.platform.outputs.platform == 'joomla' - run: | - MISSING=0 - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && exit 0 - while IFS= read -r dir; do - if [ ! -f "${dir}/index.html" ]; then - echo "::warning::Missing index.html in ${dir} (directory listing protection)" - MISSING=$((MISSING + 1)) - fi - done < <(find "$SOURCE_DIR" -type d -not -path "./.git/*" -not -path "*/vendor/*" -not -path "*/node_modules/*") - if [ "$MISSING" -gt 0 ]; then - echo "## Directory Protection" >> $GITHUB_STEP_SUMMARY - echo "${MISSING} director(ies) missing index.html" >> $GITHUB_STEP_SUMMARY - fi - echo "Directory protection: ${MISSING} missing (advisory)" - - - name: Joomla script file and asset checks - if: steps.platform.outputs.platform == 'joomla' - run: | - ERRORS=0 - MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) - [ -z "$MANIFEST" ] && exit 0 - MANIFEST_DIR=$(dirname "$MANIFEST") - - # Check scriptfile exists if declared - SCRIPTFILE=$(sed -n 's/.*\([^<]*\)<\/scriptfile>.*/\1/p' "$MANIFEST" 2>/dev/null) - if [ -n "$SCRIPTFILE" ]; then - if [ ! -f "${MANIFEST_DIR}/${SCRIPTFILE}" ]; then - echo "::error::Manifest declares ${SCRIPTFILE} but file not found at ${MANIFEST_DIR}/${SCRIPTFILE}" - ERRORS=$((ERRORS + 1)) - else - echo "Script file: ${MANIFEST_DIR}/${SCRIPTFILE} (OK)" - fi - fi - - # Require joomla.asset.json and validate it - ASSET_JSON=$(find "$MANIFEST_DIR" -name "joomla.asset.json" -not -path "./.git/*" 2>/dev/null | head -1) - if [ -z "$ASSET_JSON" ]; then - echo "::error::joomla.asset.json not found — Joomla asset system is required" - ERRORS=$((ERRORS + 1)) - else - if command -v php &> /dev/null; then - php -r "json_decode(file_get_contents('$ASSET_JSON')); if(json_last_error()!==JSON_ERROR_NONE){echo json_last_error_msg();exit(1);}" 2>&1 || { - echo "::error::joomla.asset.json is not valid JSON" - ERRORS=$((ERRORS + 1)) - } - fi - echo "joomla.asset.json: valid" - fi - - # Validate all XML files in src/ are well-formed - XML_ERRORS=0 - if command -v php &> /dev/null; then - while IFS= read -r -d '' xmlfile; do - if ! php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$xmlfile'); if(!\$x){foreach(libxml_get_errors() as \$e) echo trim(\$e->message) . ' in $xmlfile'; exit(1);}" 2>&1; then - XML_ERRORS=$((XML_ERRORS + 1)) - fi - done < <(find "$MANIFEST_DIR" -name "*.xml" -not -path "./.git/*" -print0) - fi - if [ "$XML_ERRORS" -gt 0 ]; then - echo "::error::${XML_ERRORS} XML file(s) are malformed" - ERRORS=$((ERRORS + 1)) - else - echo "XML well-formedness: OK" - fi - - [ "$ERRORS" -gt 0 ] && exit 1 - echo "Joomla asset checks: OK" - - - name: Validate platform manifest - run: | - PLATFORM="${{ steps.platform.outputs.platform }}" - case "$PLATFORM" in - joomla) - MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) - if [ -z "$MANIFEST" ]; then - echo "::warning::No Joomla manifest found (WaaS site)" - exit 0 - fi - echo "Manifest: ${MANIFEST}" - if command -v php &> /dev/null; then - php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$MANIFEST'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::Manifest XML is malformed"; exit 1; } - fi - for ELEMENT in name version description; do - grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; } - done - # Block legacy raw/branch update server URLs on MokoGitea - RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogitea\|git\.mokoconsulting\.tech' || true) - if [ -n "$RAW_URLS" ]; then - echo "::error::Manifest contains legacy raw/branch update server URL on MokoGitea. Use the Gitea Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)" - echo "$RAW_URLS" - exit 1 - fi - echo "Joomla manifest valid" - ;; - dolibarr) - MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1) - if [ -z "$MOD_FILE" ]; then - echo "::error::No mod*.class.php found" - exit 1 - fi - echo "Dolibarr module: ${MOD_FILE}" - ;; - *) - echo "Generic platform — no manifest validation" - ;; - esac - - - name: Check update stream format - run: | - PLATFORM="${{ steps.platform.outputs.platform }}" - case "$PLATFORM" in - joomla) - if [ -f "updates.xml" ]; then - if command -v php &> /dev/null; then - php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('updates.xml'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::updates.xml is malformed"; exit 1; } - fi - echo "updates.xml valid" - fi - ;; - dolibarr) - [ -f "update.txt" ] && echo "update.txt present" || echo "::warning::No update.txt" - ;; - esac - - - name: Validate Joomla language files - if: steps.platform.outputs.platform == 'joomla' - run: | - ERRORS=0 - WARNINGS=0 - - # Require both en-GB and en-US language directories - LANG_ROOT=$(find . -path "*/language" -type d -not -path "./.git/*" 2>/dev/null | head -1) - if [ -z "$LANG_ROOT" ]; then - echo "No language/ directory found — skipping" - exit 0 - fi - - if [ ! -d "$LANG_ROOT/en-GB" ]; then - echo "::error::Missing en-GB language directory (${LANG_ROOT}/en-GB)" - ERRORS=$((ERRORS + 1)) - fi - if [ ! -d "$LANG_ROOT/en-US" ]; then - echo "::error::Missing en-US language directory (${LANG_ROOT}/en-US)" - ERRORS=$((ERRORS + 1)) - fi - - # Check that en-GB and en-US have matching .ini files - if [ -d "$LANG_ROOT/en-GB" ] && [ -d "$LANG_ROOT/en-US" ]; then - for GB_INI in "$LANG_ROOT/en-GB"/*.ini; do - [ ! -f "$GB_INI" ] && continue - US_INI="$LANG_ROOT/en-US/$(basename "$GB_INI")" - if [ ! -f "$US_INI" ]; then - echo "::error::$(basename "$GB_INI") exists in en-GB but missing from en-US" - ERRORS=$((ERRORS + 1)) - fi - done - for US_INI in "$LANG_ROOT/en-US"/*.ini; do - [ ! -f "$US_INI" ] && continue - GB_INI="$LANG_ROOT/en-GB/$(basename "$US_INI")" - if [ ! -f "$GB_INI" ]; then - echo "::error::$(basename "$US_INI") exists in en-US but missing from en-GB" - ERRORS=$((ERRORS + 1)) - fi - done - fi - - # Find all .ini language files - INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null) - if [ -z "$INI_FILES" ]; then - echo "No .ini language files found" - [ "$ERRORS" -gt 0 ] && exit 1 - exit 0 - fi - - echo "Found $(echo "$INI_FILES" | wc -l) language file(s)" - - for FILE in $INI_FILES; do - FNAME=$(basename "$FILE") - LINENUM=0 - SEEN_KEYS="" - - while IFS= read -r line || [ -n "$line" ]; do - LINENUM=$((LINENUM + 1)) - - # Skip empty lines and comments - [ -z "$line" ] && continue - echo "$line" | grep -qE '^\s*;' && continue - echo "$line" | grep -qE '^\s*$' && continue - - # Must match KEY="VALUE" format - if ! echo "$line" | grep -qE '^[A-Z_][A-Z0-9_]*=".*"$'; then - echo "::error file=${FILE},line=${LINENUM}::Malformed line: ${line}" - ERRORS=$((ERRORS + 1)) - continue - fi - - # Extract key and check for duplicates - KEY=$(echo "$line" | sed 's/=.*//') - if echo "$SEEN_KEYS" | grep -qx "$KEY"; then - echo "::error file=${FILE},line=${LINENUM}::Duplicate key: ${KEY}" - ERRORS=$((ERRORS + 1)) - fi - SEEN_KEYS="${SEEN_KEYS} - ${KEY}" - done < "$FILE" - - echo " ${FILE}: checked ${LINENUM} lines" - done - - # Cross-check en-GB vs en-US key consistency - GB_DIR=$(find . -path "*/language/en-GB" -type d -not -path "./.git/*" 2>/dev/null | head -1) - US_DIR=$(find . -path "*/language/en-US" -type d -not -path "./.git/*" 2>/dev/null | head -1) - - if [ -n "$GB_DIR" ] && [ -n "$US_DIR" ]; then - for GB_FILE in "$GB_DIR"/*.ini; do - [ ! -f "$GB_FILE" ] && continue - FNAME=$(basename "$GB_FILE") - US_FILE="$US_DIR/$FNAME" - [ ! -f "$US_FILE" ] && continue - - GB_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$GB_FILE" 2>/dev/null | sort) - US_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$US_FILE" 2>/dev/null | sort) - - # Keys in en-GB but not en-US - MISSING_US=$(comm -23 <(echo "$GB_KEYS") <(echo "$US_KEYS")) - if [ -n "$MISSING_US" ]; then - echo "::warning::Keys in en-GB/$FNAME but missing from en-US/$FNAME:" - echo "$MISSING_US" | while read -r k; do echo " - $k"; done - WARNINGS=$((WARNINGS + 1)) - fi - - # Keys in en-US but not en-GB - MISSING_GB=$(comm -13 <(echo "$GB_KEYS") <(echo "$US_KEYS")) - if [ -n "$MISSING_GB" ]; then - echo "::warning::Keys in en-US/$FNAME but missing from en-GB/$FNAME:" - echo "$MISSING_GB" | while read -r k; do echo " - $k"; done - WARNINGS=$((WARNINGS + 1)) - fi - done - fi - - { - echo "### Language File Validation" - echo "| Metric | Count |" - echo "|---|---|" - echo "| Files checked | $(echo "$INI_FILES" | wc -l) |" - echo "| Errors | ${ERRORS} |" - echo "| Warnings | ${WARNINGS} |" - } >> $GITHUB_STEP_SUMMARY - - if [ "$ERRORS" -gt 0 ]; then - echo "::error::Language validation failed with ${ERRORS} error(s)" - exit 1 - fi - echo "Language files: OK (${WARNINGS} warning(s))" - - - name: Check changelog has unreleased entry - run: | - if [ ! -f "CHANGELOG.md" ]; then - echo "::warning::No CHANGELOG.md found" - exit 0 - fi - # Check for content under [Unreleased] section - if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then - echo "::error::CHANGELOG.md missing [Unreleased] section" - exit 1 - fi - # Check there's at least one entry (Added/Changed/Fixed/Removed) under Unreleased - UNRELEASED_CONTENT=$(sed -n '/## \[Unreleased\]/,/## \[/p' CHANGELOG.md | grep -cE '^\s*-\s' || true) - if [ "$UNRELEASED_CONTENT" -eq 0 ]; then - echo "::error::CHANGELOG.md [Unreleased] section has no entries. Add a changelog entry describing your changes." - echo "## Changelog Check: Failed" >> $GITHUB_STEP_SUMMARY - echo "The \`[Unreleased]\` section in CHANGELOG.md has no entries." >> $GITHUB_STEP_SUMMARY - echo "Add a line like \`- Description of your change\` under a heading (\`### Added\`, \`### Changed\`, \`### Fixed\`, etc.)" >> $GITHUB_STEP_SUMMARY - exit 1 - fi - echo "Changelog: ${UNRELEASED_CONTENT} entry/entries in [Unreleased]" - - - name: Verify package source - run: | - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - if [ ! -d "$SOURCE_DIR" ]; then - echo "::warning::No src/ or htdocs/ directory" - exit 0 - fi - FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l) - echo "Source: ${FILE_COUNT} files" - [ "$FILE_COUNT" -gt 0 ] || { echo "::error::Source directory is empty"; exit 1; } - - # ── Pre-Release RC Build ───────────────────────────────────────────────── - pre-release: - name: Build RC Package - runs-on: ubuntu-latest - needs: [branch-policy, validate] - - steps: - - name: Trigger RC pre-release - env: - GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - REPO: ${{ github.repository }} - BRANCH: ${{ github.head_ref }} - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - run: | - curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}" - echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY - echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY - - # ── Issue Reporter ────────────────────────────────────────────────────── - report-issues: - name: Report Issues - runs-on: ubuntu-latest - needs: [branch-policy, validate] - if: >- - always() && - needs.validate.result == 'failure' - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - sparse-checkout: automation/ci-issue-reporter.sh - sparse-checkout-cone-mode: false - - - name: "File issue for PR validation failure" - env: - GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - run: | - chmod +x automation/ci-issue-reporter.sh - ./automation/ci-issue-reporter.sh \ - --gate "PR Validation" \ - --workflow "PR Check" \ - --severity error \ - --details "PR validation failed (syntax, manifest, changelog, or source checks). See the CI run for the specific check that failed." +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.CI +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform +# PATH: /templates/workflows/universal/pr-check.yml.template +# VERSION: 09.23.00 +# BRIEF: PR gate — branch policy + code validation before merge + +name: "Universal: PR Check" + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + +permissions: + contents: read + pull-requests: write + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + # ── Branch Policy ────────────────────────────────────────────────────── + branch-policy: + name: Branch Policy + runs-on: ubuntu-latest + steps: + - name: Check branch merge target + run: | + HEAD="${{ github.head_ref }}" + BASE="${{ github.base_ref }}" + + echo "PR: ${HEAD} → ${BASE}" + + ALLOWED=true + REASON="" + + case "$HEAD" in + feature/*|feat/*) + if [ "$BASE" != "dev" ]; then + ALLOWED=false + REASON="Feature branches must target 'dev', not '${BASE}'" + fi + ;; + fix/*|bugfix/*) + if [ "$BASE" != "dev" ]; then + ALLOWED=false + REASON="Fix branches must target 'dev', not '${BASE}'" + fi + ;; + patch/*) + if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then + ALLOWED=false + REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'" + fi + ;; + hotfix/*) + if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then + ALLOWED=false + REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'" + fi + ;; + rc) + if [ "$BASE" != "main" ]; then + ALLOWED=false + REASON="RC branch can only merge into 'main', not '${BASE}'" + fi + ;; + dev) + if [ "$BASE" != "main" ]; then + ALLOWED=false + REASON="Dev branch can only merge into 'main', not '${BASE}'" + fi + ;; + esac + + if [ "$ALLOWED" = false ]; then + echo "::error::${REASON}" + echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "${REASON}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY + echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY + echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY + echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY + echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY + echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + echo "Branch policy: OK (${HEAD} → ${BASE})" + echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY + + # ── Code Validation ──────────────────────────────────────────────────── + validate: + name: Validate PR + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check for merge conflict markers + run: | + CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) + if [ -n "$CONFLICTS" ]; then + echo "::error::Merge conflict markers found in source files" + echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "No conflict markers found" + + - name: Detect platform + id: platform + run: | + # Read platform from XML manifest ( tag) or plain text fallback + PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1) + [ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]') + [ -z "$PLATFORM" ] && PLATFORM="generic" + echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" + + - name: Setup PHP + if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' + run: | + if ! command -v php &> /dev/null; then + sudo apt-get update -qq + sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1 + fi + + - name: PHP syntax check + if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' + run: | + ERRORS=0 + while IFS= read -r -d '' file; do + if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -print0) + echo "PHP lint: ${ERRORS} error(s)" + [ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; } + + - name: Joomla JEXEC guard check + if: steps.platform.outputs.platform == 'joomla' + run: | + ERRORS=0 + while IFS= read -r -d '' file; do + # Skip vendor, node_modules, and index.html stub files + case "$file" in ./vendor/*|./node_modules/*) continue ;; esac + # Check first 10 lines for JEXEC or JPATH guard + if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then + echo "::error file=${file}::Missing JEXEC guard: ${file}" + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0) + if [ "$ERRORS" -gt 0 ]; then + echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard" + echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY + echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "JEXEC guard: OK" + + - name: Joomla directory listing protection + if: steps.platform.outputs.platform == 'joomla' + run: | + MISSING=0 + SOURCE_DIR="src" + [ ! -d "$SOURCE_DIR" ] && exit 0 + while IFS= read -r dir; do + if [ ! -f "${dir}/index.html" ]; then + echo "::warning::Missing index.html in ${dir} (directory listing protection)" + MISSING=$((MISSING + 1)) + fi + done < <(find "$SOURCE_DIR" -type d -not -path "./.git/*" -not -path "*/vendor/*" -not -path "*/node_modules/*") + if [ "$MISSING" -gt 0 ]; then + echo "## Directory Protection" >> $GITHUB_STEP_SUMMARY + echo "${MISSING} director(ies) missing index.html" >> $GITHUB_STEP_SUMMARY + fi + echo "Directory protection: ${MISSING} missing (advisory)" + + - name: Joomla script file and asset checks + if: steps.platform.outputs.platform == 'joomla' + run: | + ERRORS=0 + MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) + [ -z "$MANIFEST" ] && exit 0 + MANIFEST_DIR=$(dirname "$MANIFEST") + + # Check scriptfile exists if declared + SCRIPTFILE=$(sed -n 's/.*\([^<]*\)<\/scriptfile>.*/\1/p' "$MANIFEST" 2>/dev/null) + if [ -n "$SCRIPTFILE" ]; then + if [ ! -f "${MANIFEST_DIR}/${SCRIPTFILE}" ]; then + echo "::error::Manifest declares ${SCRIPTFILE} but file not found at ${MANIFEST_DIR}/${SCRIPTFILE}" + ERRORS=$((ERRORS + 1)) + else + echo "Script file: ${MANIFEST_DIR}/${SCRIPTFILE} (OK)" + fi + fi + + # Require joomla.asset.json and validate it + ASSET_JSON=$(find "$MANIFEST_DIR" -name "joomla.asset.json" -not -path "./.git/*" 2>/dev/null | head -1) + if [ -z "$ASSET_JSON" ]; then + echo "::error::joomla.asset.json not found — Joomla asset system is required" + ERRORS=$((ERRORS + 1)) + else + if command -v php &> /dev/null; then + php -r "json_decode(file_get_contents('$ASSET_JSON')); if(json_last_error()!==JSON_ERROR_NONE){echo json_last_error_msg();exit(1);}" 2>&1 || { + echo "::error::joomla.asset.json is not valid JSON" + ERRORS=$((ERRORS + 1)) + } + fi + echo "joomla.asset.json: valid" + fi + + # Validate all XML files in src/ are well-formed + XML_ERRORS=0 + if command -v php &> /dev/null; then + while IFS= read -r -d '' xmlfile; do + if ! php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$xmlfile'); if(!\$x){foreach(libxml_get_errors() as \$e) echo trim(\$e->message) . ' in $xmlfile'; exit(1);}" 2>&1; then + XML_ERRORS=$((XML_ERRORS + 1)) + fi + done < <(find "$MANIFEST_DIR" -name "*.xml" -not -path "./.git/*" -print0) + fi + if [ "$XML_ERRORS" -gt 0 ]; then + echo "::error::${XML_ERRORS} XML file(s) are malformed" + ERRORS=$((ERRORS + 1)) + else + echo "XML well-formedness: OK" + fi + + [ "$ERRORS" -gt 0 ] && exit 1 + echo "Joomla asset checks: OK" + + - name: Validate platform manifest + run: | + PLATFORM="${{ steps.platform.outputs.platform }}" + case "$PLATFORM" in + joomla) + MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) + if [ -z "$MANIFEST" ]; then + echo "::warning::No Joomla manifest found (WaaS site)" + exit 0 + fi + echo "Manifest: ${MANIFEST}" + if command -v php &> /dev/null; then + php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$MANIFEST'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::Manifest XML is malformed"; exit 1; } + fi + for ELEMENT in name version description; do + grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; } + done + # Block legacy raw/branch update server URLs on MokoGitea + RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogitea\|git\.mokoconsulting\.tech' || true) + if [ -n "$RAW_URLS" ]; then + echo "::error::Manifest contains legacy raw/branch update server URL on MokoGitea. Use the Gitea Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)" + echo "$RAW_URLS" + exit 1 + fi + echo "Joomla manifest valid" + ;; + dolibarr) + MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1) + if [ -z "$MOD_FILE" ]; then + echo "::error::No mod*.class.php found" + exit 1 + fi + echo "Dolibarr module: ${MOD_FILE}" + ;; + *) + echo "Generic platform — no manifest validation" + ;; + esac + + - name: Check update stream format + run: | + PLATFORM="${{ steps.platform.outputs.platform }}" + case "$PLATFORM" in + joomla) + if [ -f "updates.xml" ]; then + if command -v php &> /dev/null; then + php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('updates.xml'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::updates.xml is malformed"; exit 1; } + fi + echo "updates.xml valid" + fi + ;; + dolibarr) + [ -f "update.txt" ] && echo "update.txt present" || echo "::warning::No update.txt" + ;; + esac + + - name: Validate Joomla language files + if: steps.platform.outputs.platform == 'joomla' + run: | + ERRORS=0 + WARNINGS=0 + + # Require both en-GB and en-US language directories + LANG_ROOT=$(find . -path "*/language" -type d -not -path "./.git/*" 2>/dev/null | head -1) + if [ -z "$LANG_ROOT" ]; then + echo "No language/ directory found — skipping" + exit 0 + fi + + if [ ! -d "$LANG_ROOT/en-GB" ]; then + echo "::error::Missing en-GB language directory (${LANG_ROOT}/en-GB)" + ERRORS=$((ERRORS + 1)) + fi + if [ ! -d "$LANG_ROOT/en-US" ]; then + echo "::error::Missing en-US language directory (${LANG_ROOT}/en-US)" + ERRORS=$((ERRORS + 1)) + fi + + # Check that en-GB and en-US have matching .ini files + if [ -d "$LANG_ROOT/en-GB" ] && [ -d "$LANG_ROOT/en-US" ]; then + for GB_INI in "$LANG_ROOT/en-GB"/*.ini; do + [ ! -f "$GB_INI" ] && continue + US_INI="$LANG_ROOT/en-US/$(basename "$GB_INI")" + if [ ! -f "$US_INI" ]; then + echo "::error::$(basename "$GB_INI") exists in en-GB but missing from en-US" + ERRORS=$((ERRORS + 1)) + fi + done + for US_INI in "$LANG_ROOT/en-US"/*.ini; do + [ ! -f "$US_INI" ] && continue + GB_INI="$LANG_ROOT/en-GB/$(basename "$US_INI")" + if [ ! -f "$GB_INI" ]; then + echo "::error::$(basename "$US_INI") exists in en-US but missing from en-GB" + ERRORS=$((ERRORS + 1)) + fi + done + fi + + # Find all .ini language files + INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null) + if [ -z "$INI_FILES" ]; then + echo "No .ini language files found" + [ "$ERRORS" -gt 0 ] && exit 1 + exit 0 + fi + + echo "Found $(echo "$INI_FILES" | wc -l) language file(s)" + + for FILE in $INI_FILES; do + FNAME=$(basename "$FILE") + LINENUM=0 + SEEN_KEYS="" + + while IFS= read -r line || [ -n "$line" ]; do + LINENUM=$((LINENUM + 1)) + + # Skip empty lines and comments + [ -z "$line" ] && continue + echo "$line" | grep -qE '^\s*;' && continue + echo "$line" | grep -qE '^\s*$' && continue + + # Must match KEY="VALUE" format + if ! echo "$line" | grep -qE '^[A-Z_][A-Z0-9_]*=".*"$'; then + echo "::error file=${FILE},line=${LINENUM}::Malformed line: ${line}" + ERRORS=$((ERRORS + 1)) + continue + fi + + # Extract key and check for duplicates + KEY=$(echo "$line" | sed 's/=.*//') + if echo "$SEEN_KEYS" | grep -qx "$KEY"; then + echo "::error file=${FILE},line=${LINENUM}::Duplicate key: ${KEY}" + ERRORS=$((ERRORS + 1)) + fi + SEEN_KEYS="${SEEN_KEYS} + ${KEY}" + done < "$FILE" + + echo " ${FILE}: checked ${LINENUM} lines" + done + + # Cross-check en-GB vs en-US key consistency + GB_DIR=$(find . -path "*/language/en-GB" -type d -not -path "./.git/*" 2>/dev/null | head -1) + US_DIR=$(find . -path "*/language/en-US" -type d -not -path "./.git/*" 2>/dev/null | head -1) + + if [ -n "$GB_DIR" ] && [ -n "$US_DIR" ]; then + for GB_FILE in "$GB_DIR"/*.ini; do + [ ! -f "$GB_FILE" ] && continue + FNAME=$(basename "$GB_FILE") + US_FILE="$US_DIR/$FNAME" + [ ! -f "$US_FILE" ] && continue + + GB_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$GB_FILE" 2>/dev/null | sort) + US_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$US_FILE" 2>/dev/null | sort) + + # Keys in en-GB but not en-US + MISSING_US=$(comm -23 <(echo "$GB_KEYS") <(echo "$US_KEYS")) + if [ -n "$MISSING_US" ]; then + echo "::warning::Keys in en-GB/$FNAME but missing from en-US/$FNAME:" + echo "$MISSING_US" | while read -r k; do echo " - $k"; done + WARNINGS=$((WARNINGS + 1)) + fi + + # Keys in en-US but not en-GB + MISSING_GB=$(comm -13 <(echo "$GB_KEYS") <(echo "$US_KEYS")) + if [ -n "$MISSING_GB" ]; then + echo "::warning::Keys in en-US/$FNAME but missing from en-GB/$FNAME:" + echo "$MISSING_GB" | while read -r k; do echo " - $k"; done + WARNINGS=$((WARNINGS + 1)) + fi + done + fi + + { + echo "### Language File Validation" + echo "| Metric | Count |" + echo "|---|---|" + echo "| Files checked | $(echo "$INI_FILES" | wc -l) |" + echo "| Errors | ${ERRORS} |" + echo "| Warnings | ${WARNINGS} |" + } >> $GITHUB_STEP_SUMMARY + + if [ "$ERRORS" -gt 0 ]; then + echo "::error::Language validation failed with ${ERRORS} error(s)" + exit 1 + fi + echo "Language files: OK (${WARNINGS} warning(s))" + + - name: Check changelog has unreleased entry + run: | + if [ ! -f "CHANGELOG.md" ]; then + echo "::warning::No CHANGELOG.md found" + exit 0 + fi + # Check for content under [Unreleased] section + if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then + echo "::error::CHANGELOG.md missing [Unreleased] section" + exit 1 + fi + # Check there's at least one entry (Added/Changed/Fixed/Removed) under Unreleased + UNRELEASED_CONTENT=$(sed -n '/## \[Unreleased\]/,/## \[/p' CHANGELOG.md | grep -cE '^\s*-\s' || true) + if [ "$UNRELEASED_CONTENT" -eq 0 ]; then + echo "::error::CHANGELOG.md [Unreleased] section has no entries. Add a changelog entry describing your changes." + echo "## Changelog Check: Failed" >> $GITHUB_STEP_SUMMARY + echo "The \`[Unreleased]\` section in CHANGELOG.md has no entries." >> $GITHUB_STEP_SUMMARY + echo "Add a line like \`- Description of your change\` under a heading (\`### Added\`, \`### Changed\`, \`### Fixed\`, etc.)" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "Changelog: ${UNRELEASED_CONTENT} entry/entries in [Unreleased]" + + - name: Verify package source + run: | + SOURCE_DIR="src" + [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" + if [ ! -d "$SOURCE_DIR" ]; then + echo "::warning::No src/ or htdocs/ directory" + exit 0 + fi + FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l) + echo "Source: ${FILE_COUNT} files" + [ "$FILE_COUNT" -gt 0 ] || { echo "::error::Source directory is empty"; exit 1; } + + # ── Pre-Release RC Build ───────────────────────────────────────────────── + pre-release: + name: Build RC Package + runs-on: ubuntu-latest + needs: [branch-policy, validate] + + steps: + - name: Trigger RC pre-release + env: + GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + REPO: ${{ github.repository }} + BRANCH: ${{ github.head_ref }} + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + run: | + curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}" + echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY + echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY + + # ── Issue Reporter ────────────────────────────────────────────────────── + report-issues: + name: Report Issues + runs-on: ubuntu-latest + needs: [branch-policy, validate] + if: >- + always() && + needs.validate.result == 'failure' + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: automation/ci-issue-reporter.sh + sparse-checkout-cone-mode: false + + - name: "File issue for PR validation failure" + env: + GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + run: | + chmod +x automation/ci-issue-reporter.sh + ./automation/ci-issue-reporter.sh \ + --gate "PR Validation" \ + --workflow "PR Check" \ + --severity error \ + --details "PR validation failed (syntax, manifest, changelog, or source checks). See the CI run for the specific check that failed." diff --git a/README.md b/README.md index ca6b7b9..741420b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index f715793..08a2744 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.04-dev + 01.01.05-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_actionlog_mokobackup/mokobackup.xml b/src/packages/plg_actionlog_mokobackup/mokobackup.xml index b5fe605..43d280b 100644 --- a/src/packages/plg_actionlog_mokobackup/mokobackup.xml +++ b/src/packages/plg_actionlog_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokobackup - 01.01.04-dev + 01.01.05-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_console_mokobackup/mokobackup.xml b/src/packages/plg_console_mokobackup/mokobackup.xml index c641770..9992f42 100644 --- a/src/packages/plg_console_mokobackup/mokobackup.xml +++ b/src/packages/plg_console_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokobackup - 01.01.04-dev + 01.01.05-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_content_mokobackup/mokobackup.xml b/src/packages/plg_content_mokobackup/mokobackup.xml index a337b10..b0610f3 100644 --- a/src/packages/plg_content_mokobackup/mokobackup.xml +++ b/src/packages/plg_content_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokobackup - 01.01.04-dev + 01.01.05-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/src/packages/plg_quickicon_mokobackup/mokobackup.xml index 2856a6b..5a666ac 100644 --- a/src/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/src/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.04-dev + 01.01.05-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/src/packages/plg_system_mokobackup/mokobackup.xml index 1746c42..64ab1cc 100644 --- a/src/packages/plg_system_mokobackup/mokobackup.xml +++ b/src/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.04-dev + 01.01.05-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml index 1b72d8d..bf9d222 100644 --- a/src/packages/plg_task_mokobackup/mokobackup.xml +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.04-dev + 01.01.05-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/src/packages/plg_webservices_mokobackup/mokobackup.xml index f7ebd45..28b70fe 100644 --- a/src/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/src/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.04-dev + 01.01.05-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index b370006..af4eb8b 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.04-dev + 01.01.05-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 5460c7b211e13e03a5a4d67024b189dfab4b513a Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 12:50:46 -0500 Subject: [PATCH 31/81] chore: consolidate changelog entries to minor versions only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collapsed all patch-level entries (01.00.00, 01.01.01–01.01.04) into two minor version sections: 01.00 and 01.01. Deduplicated and grouped entries by Added/Changed/Fixed within each. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 65 +++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f228f01..009ae6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,62 +2,55 @@ ## [Unreleased] -## 01.00.00 — 2026-06-02 +## 01.01 — 2026-06-04 + +### Added +- Admin dashboard view as default landing page with status cards, quick actions, and system health checklist (#28) +- Console plugin (plg_console_mokobackup) — CLI commands: run, list, profiles, restore, cleanup (#29) +- Content plugin (plg_content_mokobackup) — auto-backup before extension install/update (#30) +- Actionlog plugin (plg_actionlog_mokobackup) — logs backup and profile actions to User Action Logs (#31) +- BackupEngine dispatches onMokoBackupAfterRun event for plugin listeners +- Update site notice on dashboard and post-install + +### Changed +- Renamed Kickstart to MokoRestore throughout + +### Fixed +- SQL update migration and error handling +- Removed orphaned scriptfile from component manifest +- Consolidated admin files into single files block + +## 01.00 — 2026-06-02 ### Added - Initial package structure with component, system plugin, task plugin, and webservices plugin - Joomla Scheduled Tasks integration (plg_task_mokobackup) — create multiple tasks, each running a different backup profile on its own schedule - Individual form fields for all profile settings (no raw JSON) - FTP/FTPS uploader with recursive directory creation, passive mode, SSL, and size verification -- Google Drive uploader using OAuth2 refresh tokens and resumable upload API (5 MB chunks) +- Google Drive uploader using OAuth2 refresh tokens and resumable upload API +- S3-compatible remote storage: AWS S3, Wasabi, Backblaze B2, MinIO (#16) - RemoteUploaderInterface for pluggable storage backends -- Remote upload integrated into BackupEngine as Step 3 after archive creation -- Option to delete local copy after successful remote upload (per-profile setting) +- Remote upload integrated into BackupEngine with option to delete local copy after upload - Restore engine with file restoration and database import -- MokoRestore standalone restore script (restore.php) — self-contained site restoration without Joomla, -- "Include Restore Script" toggle per profile — wraps backup with restore.php + site-backup.zip -- FileRestorer class with protected file handling (preserves configuration.php, .htaccess) +- MokoRestore standalone restore script — self-contained site restoration without Joomla +- "Include Restore Script" toggle per profile +- FileRestorer with protected file handling (preserves configuration.php, .htaccess) - DatabaseImporter with streaming line-by-line SQL execution and error tolerance - Admin dashboard quickicon widget — backup status at a glance with warnings (#18) - Differential backups — only back up files changed since last full backup (#19) -- DifferentialScanner: builds file manifests (path/size/mtime) and compares against base -- File manifest stored in backup record for future differential comparisons -- Automatic full-backup fallback when no base manifest exists +- DifferentialScanner with file manifests stored in backup records - JPA archive format import for Akeeba Backup migration (#20) -- JpaUnarchiver: parses Akeeba JPA binary format (headers, gzip, permissions) -- RestoreEngine auto-detects JPA vs ZIP format - AES-256 archive encryption with per-profile password (#17) -- Encrypted archive support in RestoreEngine (password parameter) -- Encrypted archive support in MokoRestore restore.php (password field in UI) -- SHA-256 checksum computed and stored after archive creation (#15) -- "Verify Integrity" toolbar button re-computes hash and compares against stored checksum -- S3-compatible remote storage: AWS S3, Wasabi, Backblaze B2, MinIO (#16) -- S3 uploader with AWS Signature V4, single PUT for files <= 100 MB, multipart for larger -- S3 fields in profile form with showon conditional visibility -- Akeeba importer now maps S3 credentials from Akeeba profiles +- SHA-256 checksum verification for backup integrity (#15) - Email notifications on backup success/failure via Joomla mailer (#14) -- Per-profile notification settings: recipient emails, notify on success/failure -- Failure emails include last 30 lines of backup log for debugging -- mcp_mokobackup MCP server updated with MokoBackupClient for dual-backend support (#21) -- Akeeba Backup Pro importer — imports profiles, filters, remote storage settings, and backup history +- Akeeba Backup Pro importer — profiles, filters, remote storage, and backup history - Auto-disables Akeeba plugins and scheduled tasks after successful import -- "Import from Akeeba" toolbar button in Profiles view (only shown when Akeeba tables detected) -- Supports both INI-format and JSON-format Akeeba configuration parsing -- Maps Akeeba filter format (per-root, nested) to newline-separated exclusion fields -- Profile selector dropdown in Backup Records view for choosing which profile to run - AJAX step-based backup engine for shared hosting (overcomes max_execution_time) -- SteppedBackupEngine: breaks backup into per-table DB dumps and file batches -- SteppedSession: persistent state between AJAX requests via temp JSON files - Progress bar modal in admin UI with real-time phase/percentage updates -- AjaxController for init/step endpoints with CSRF protection - Per-profile archive settings: format, compression level, split size, backup directory -- Backup engine with step-based execution for large sites -- Database dumper with table-level granularity -- File scanner with directory exclusion filters -- ZIP archive builder +- Backup engine with database dumper, file scanner, and ZIP archive builder - Backup profiles with independent configurations - Backup record management (list, download, delete) -- Admin dashboard with backup history - CLI script for cron/scheduled backups - REST API compatible with MokoBackup MCP server - System plugin for automatic backup cleanup with configurable retention -- 2.52.0 From c5d4445bc15f3309d2412a41116e26f1a5065197 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 13:40:29 -0500 Subject: [PATCH 32/81] fix: rewrite Makefile for CI-based releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove local build/package targets — all packaging is handled by the pre-release and auto-release CI workflows. The release target now dispatches the pre-release workflow via Gitea API. Added release-rc target and validate-xml check. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- Makefile | 214 +++++++++++++++++++++++-------------------------------- 1 file changed, 88 insertions(+), 126 deletions(-) diff --git a/Makefile b/Makefile index 50e3eae..df8aa3e 100644 --- a/Makefile +++ b/Makefile @@ -3,43 +3,29 @@ # SPDX-License-Identifier: GPL-3.0-or-later # # MokoJoomBackup — Full-site backup and restore for Joomla +# +# Builds and releases are handled by CI workflows (pre-release.yml, +# auto-release.yml). This Makefile provides local validation helpers +# and workflow dispatch shortcuts. # ============================================================================== -# CONFIGURATION - Customize these for your extension +# CONFIGURATION # ============================================================================== -# Extension Configuration EXTENSION_NAME := mokobackup EXTENSION_TYPE := package -# Options: module, plugin, component, package, template -EXTENSION_VERSION := 1.0.0 -# Module Configuration (for modules only) -MODULE_TYPE := site -# Options: site, admin - -# Plugin Configuration (for plugins only) -PLUGIN_GROUP := system -# Options: system, content, user, authentication, etc. - -# Directories SRC_DIR := src -BUILD_DIR := build -DIST_DIR := dist -DOCS_DIR := docs -# Joomla Installation (for local testing - customize paths) -JOOMLA_ROOT := /var/www/html/joomla -JOOMLA_VERSION := 4 +# Gitea +GITEA_URL := https://git.mokoconsulting.tech +GITEA_ORG := MokoConsulting +GITEA_REPO := MokoJoomBackup # Tools PHP := php COMPOSER := composer -NPM := npm PHPCS := vendor/bin/phpcs -PHPCBF := vendor/bin/phpcbf -PHPUNIT := vendor/bin/phpunit -ZIP := zip # Coding Standards PHPCS_STANDARD := Joomla @@ -58,146 +44,122 @@ COLOR_RED := \033[31m .PHONY: help help: ## Show this help message @echo "$(COLOR_BLUE)╔════════════════════════════════════════════════════════════╗$(COLOR_RESET)" - @echo "$(COLOR_BLUE)║ Joomla Extension Makefile ║$(COLOR_RESET)" + @echo "$(COLOR_BLUE)║ MokoJoomBackup Makefile ║$(COLOR_RESET)" @echo "$(COLOR_BLUE)╚════════════════════════════════════════════════════════════╝$(COLOR_RESET)" @echo "" - @echo "Extension: $(EXTENSION_NAME) ($(EXTENSION_TYPE)) v$(EXTENSION_VERSION)" - @echo "" @echo "$(COLOR_GREEN)Available targets:$(COLOR_RESET)" @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(COLOR_BLUE)%-20s$(COLOR_RESET) %s\n", $$1, $$2}' @echo "" -.PHONY: install-deps -install-deps: ## Install all dependencies (Composer + npm) - @echo "$(COLOR_BLUE)Installing dependencies...$(COLOR_RESET)" - @if [ -f "composer.json" ]; then \ - $(COMPOSER) install; \ - echo "$(COLOR_GREEN)✓ Composer dependencies installed$(COLOR_RESET)"; \ - fi +# -- Validation ---------------------------------------------------------------- .PHONY: lint -lint: ## Run PHP linter (syntax check) +lint: ## Run PHP syntax check on all source files @echo "$(COLOR_BLUE)Running PHP linter...$(COLOR_RESET)" - @find . -name "*.php" ! -path "./vendor/*" ! -path "./node_modules/*" ! -path "./$(BUILD_DIR)/*" \ - -exec $(PHP) -l {} \; | grep -v "No syntax errors" || true + @ERROR=0; \ + find $(SRC_DIR) -name "*.php" -exec $(PHP) -l {} \; 2>&1 | grep -v "No syntax errors" || true; \ + if find $(SRC_DIR) -name "*.php" -exec $(PHP) -l {} \; 2>&1 | grep -q "Parse error"; then \ + echo "$(COLOR_RED)✗ Syntax errors found$(COLOR_RESET)"; exit 1; \ + fi @echo "$(COLOR_GREEN)✓ PHP linting complete$(COLOR_RESET)" .PHONY: phpcs phpcs: ## Run PHP CodeSniffer (Joomla standards) @echo "$(COLOR_BLUE)Running PHP CodeSniffer...$(COLOR_RESET)" @if [ -f "$(PHPCS)" ]; then \ - $(PHPCS) --standard=$(PHPCS_STANDARD) --extensions=php --ignore=vendor,node_modules,$(BUILD_DIR) .; \ + $(PHPCS) --standard=$(PHPCS_STANDARD) --extensions=php $(SRC_DIR); \ else \ - echo "$(COLOR_YELLOW)⚠ PHP CodeSniffer not installed. Run: make install-deps$(COLOR_RESET)"; \ + echo "$(COLOR_YELLOW)⚠ PHP CodeSniffer not installed. Run: composer install$(COLOR_RESET)"; \ fi .PHONY: validate -validate: lint phpcs ## Run all validation checks - @echo "$(COLOR_GREEN)✓ All validation checks passed$(COLOR_RESET)" +validate: lint ## Run all local validation checks + @echo "$(COLOR_GREEN)✓ Validation passed$(COLOR_RESET)" -.PHONY: clean -clean: ## Clean build artifacts - @echo "$(COLOR_BLUE)Cleaning build artifacts...$(COLOR_RESET)" - @rm -rf $(BUILD_DIR) $(DIST_DIR) - @echo "$(COLOR_GREEN)✓ Build artifacts cleaned$(COLOR_RESET)" +.PHONY: validate-xml +validate-xml: ## Validate all XML manifests are well-formed + @echo "$(COLOR_BLUE)Validating XML manifests...$(COLOR_RESET)" + @ERROR=0; \ + for f in $$(find $(SRC_DIR) -name "*.xml"); do \ + $(PHP) -r "new SimpleXMLElement(file_get_contents('$$f'));" 2>/dev/null \ + || { echo "$(COLOR_RED)✗ Invalid XML: $$f$(COLOR_RESET)"; ERROR=1; }; \ + done; \ + [ $$ERROR -eq 0 ] && echo "$(COLOR_GREEN)✓ All XML manifests valid$(COLOR_RESET)" || exit 1 + +# -- Dependencies -------------------------------------------------------------- + +.PHONY: install-deps +install-deps: ## Install PHP dependencies via Composer + @echo "$(COLOR_BLUE)Installing dependencies...$(COLOR_RESET)" + @if [ -f "composer.json" ]; then \ + $(COMPOSER) install; \ + echo "$(COLOR_GREEN)✓ Composer dependencies installed$(COLOR_RESET)"; \ + fi + +.PHONY: security-check +security-check: ## Run security audit on dependencies + @echo "$(COLOR_BLUE)Running security checks...$(COLOR_RESET)" + @if [ -f "composer.json" ]; then \ + $(COMPOSER) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \ + fi + +# -- Minify -------------------------------------------------------------------- MOKO_PLATFORM ?= $(or $(wildcard ../moko-platform),$(wildcard $(HOME)/moko-platform),$(wildcard /opt/moko-platform)) MINIFY_SCRIPT := $(MOKO_PLATFORM)/build/minify.js .PHONY: minify minify: ## Minify CSS/JS assets - @echo "Minifying assets..." + @echo "$(COLOR_BLUE)Minifying assets...$(COLOR_RESET)" @if [ -f "$(MINIFY_SCRIPT)" ]; then \ node "$(MINIFY_SCRIPT)" $(SRC_DIR); \ elif [ -f "scripts/minify.js" ]; then \ node scripts/minify.js; \ else \ - echo "No minify script found"; \ + echo "$(COLOR_YELLOW)⚠ No minify script found$(COLOR_RESET)"; \ fi -.PHONY: build -build: clean validate minify ## Build extension package - @echo "$(COLOR_BLUE)Building Joomla extension package...$(COLOR_RESET)" - @mkdir -p $(DIST_DIR) $(BUILD_DIR) - - # Determine package prefix based on extension type - @case "$(EXTENSION_TYPE)" in \ - module) \ - PACKAGE_PREFIX="mod_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - plugin) \ - PACKAGE_PREFIX="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - component) \ - PACKAGE_PREFIX="com_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - package) \ - PACKAGE_PREFIX="pkg_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - template) \ - PACKAGE_PREFIX="tpl_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - *) \ - echo "$(COLOR_RED)✗ Unknown extension type: $(EXTENSION_TYPE)$(COLOR_RESET)"; \ - exit 1; \ - ;; \ - esac; \ - \ - mkdir -p "$$BUILD_TARGET"; \ - \ - echo "Building $$PACKAGE_PREFIX..."; \ - \ - rsync -av --progress \ - --exclude='$(BUILD_DIR)' \ - --exclude='$(DIST_DIR)' \ - --exclude='.git*' \ - --exclude='vendor/' \ - --exclude='node_modules/' \ - --exclude='tests/' \ - --exclude='Makefile' \ - --exclude='composer.json' \ - --exclude='composer.lock' \ - --exclude='package.json' \ - --exclude='package-lock.json' \ - --exclude='phpunit.xml' \ - --exclude='*.md' \ - --exclude='.editorconfig' \ - . "$$BUILD_TARGET/"; \ - \ - cd $(BUILD_DIR) && $(ZIP) -r "../$(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip" "$${PACKAGE_PREFIX}"; \ - \ - echo "$(COLOR_GREEN)✓ Package created: $(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip$(COLOR_RESET)" - -.PHONY: package -package: build ## Alias for build - @echo "$(COLOR_GREEN)✓ Package ready for distribution$(COLOR_RESET)" +# -- Release (CI workflow dispatch) -------------------------------------------- .PHONY: release -release: validate build ## Create a release (validate + build) - @echo "$(COLOR_GREEN)✓ Release package ready$(COLOR_RESET)" +release: validate validate-xml ## Trigger pre-release build via CI workflow + @echo "$(COLOR_BLUE)Triggering pre-release workflow...$(COLOR_RESET)" + @if ! command -v curl >/dev/null 2>&1; then \ + echo "$(COLOR_RED)✗ curl required$(COLOR_RESET)"; exit 1; \ + fi + @if [ -z "$$MOKOGITEA_TOKEN" ]; then \ + echo "$(COLOR_RED)✗ MOKOGITEA_TOKEN not set$(COLOR_RESET)"; exit 1; \ + fi + @BRANCH=$$(git rev-parse --abbrev-ref HEAD); \ + curl -sf -X POST \ + -H "Authorization: token $$MOKOGITEA_TOKEN" \ + -H "Content-Type: application/json" \ + "$(GITEA_URL)/api/v1/repos/$(GITEA_ORG)/$(GITEA_REPO)/actions/workflows/pre-release.yml/dispatches" \ + -d "{\"ref\":\"$$BRANCH\",\"inputs\":{\"stability\":\"development\"}}" \ + && echo "$(COLOR_GREEN)✓ Pre-release dispatched on $$BRANCH (development channel)$(COLOR_RESET)" \ + || { echo "$(COLOR_RED)✗ Dispatch failed$(COLOR_RESET)"; exit 1; } + +.PHONY: release-rc +release-rc: validate validate-xml ## Trigger release-candidate build via CI workflow + @echo "$(COLOR_BLUE)Triggering RC pre-release workflow...$(COLOR_RESET)" + @if [ -z "$$MOKOGITEA_TOKEN" ]; then \ + echo "$(COLOR_RED)✗ MOKOGITEA_TOKEN not set$(COLOR_RESET)"; exit 1; \ + fi + @BRANCH=$$(git rev-parse --abbrev-ref HEAD); \ + curl -sf -X POST \ + -H "Authorization: token $$MOKOGITEA_TOKEN" \ + -H "Content-Type: application/json" \ + "$(GITEA_URL)/api/v1/repos/$(GITEA_ORG)/$(GITEA_REPO)/actions/workflows/pre-release.yml/dispatches" \ + -d "{\"ref\":\"$$BRANCH\",\"inputs\":{\"stability\":\"release-candidate\"}}" \ + && echo "$(COLOR_GREEN)✓ Pre-release dispatched on $$BRANCH (release-candidate channel)$(COLOR_RESET)" \ + || { echo "$(COLOR_RED)✗ Dispatch failed$(COLOR_RESET)"; exit 1; } + +# -- Info ---------------------------------------------------------------------- .PHONY: version -version: ## Display version information - @echo "$(COLOR_BLUE)Extension Information:$(COLOR_RESET)" - @echo " Name: $(EXTENSION_NAME)" - @echo " Type: $(EXTENSION_TYPE)" - @echo " Version: $(EXTENSION_VERSION)" - -.PHONY: security-check -security-check: ## Run security checks on dependencies - @echo "$(COLOR_BLUE)Running security checks...$(COLOR_RESET)" - @if [ -f "composer.json" ]; then \ - $(COMPOSER) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \ - fi - -.PHONY: all -all: install-deps validate build ## Run complete build pipeline - @echo "$(COLOR_GREEN)✓ Complete build pipeline finished$(COLOR_RESET)" +version: ## Display version from package manifest + @VERSION=$$(grep '' $(SRC_DIR)/pkg_mokobackup.xml | sed 's/.*\(.*\)<\/version>.*/\1/'); \ + echo "$(COLOR_BLUE)$(EXTENSION_NAME)$(COLOR_RESET) v$$VERSION ($(EXTENSION_TYPE))" # Default target .DEFAULT_GOAL := help -- 2.52.0 From a805351dd126700ecd4ecc2f4c4370de58b3c67f Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 14:13:32 -0500 Subject: [PATCH 33/81] fix: move postinstall string to package sys.ini COM_MOKOBACKUP_POSTINSTALL_UPDATE_SITE was showing as a raw key after install because the component language file isn't loaded during package installation. Moved to PKG_MOKOBACKUP_POSTINSTALL_UPDATE_SITE in pkg_mokobackup.sys.ini where the installer can resolve it. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/language/en-GB/pkg_mokobackup.sys.ini | 1 + src/language/en-US/pkg_mokobackup.sys.ini | 1 + src/script.php | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/language/en-GB/pkg_mokobackup.sys.ini b/src/language/en-GB/pkg_mokobackup.sys.ini index 071172a..75457fd 100644 --- a/src/language/en-GB/pkg_mokobackup.sys.ini +++ b/src/language/en-GB/pkg_mokobackup.sys.ini @@ -7,3 +7,4 @@ PKG_MOKOBACKUP="Package - MokoJoomBackup" PKG_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration. Includes admin component, system plugin, and REST API." PKG_MOKOBACKUP_PHP_VERSION_ERROR="MokoJoomBackup requires PHP %s or later." +PKG_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." diff --git a/src/language/en-US/pkg_mokobackup.sys.ini b/src/language/en-US/pkg_mokobackup.sys.ini index 9a32545..4936ac7 100644 --- a/src/language/en-US/pkg_mokobackup.sys.ini +++ b/src/language/en-US/pkg_mokobackup.sys.ini @@ -7,3 +7,4 @@ PKG_MOKOBACKUP="Package - MokoJoomBackup" PKG_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration. Includes admin component, system plugin, and REST API." PKG_MOKOBACKUP_PHP_VERSION_ERROR="MokoJoomBackup requires PHP %s or later." +PKG_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." diff --git a/src/script.php b/src/script.php index 409a88c..d5cc000 100644 --- a/src/script.php +++ b/src/script.php @@ -194,7 +194,7 @@ class Pkg_MokoBackupInstallerScript ); Factory::getApplication()->enqueueMessage( - Text::sprintf('COM_MOKOBACKUP_POSTINSTALL_UPDATE_SITE', $editUrl), + Text::sprintf('PKG_MOKOBACKUP_POSTINSTALL_UPDATE_SITE', $editUrl), 'info' ); } -- 2.52.0 From e4d704dd843f6b7f7d9d77cc002c19bc6b32e38a Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 19:13:45 +0000 Subject: [PATCH 34/81] chore(version): auto-bump 01.01.06-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- src/packages/com_mokobackup/mokobackup.xml | 2 +- src/packages/plg_actionlog_mokobackup/mokobackup.xml | 2 +- src/packages/plg_console_mokobackup/mokobackup.xml | 2 +- src/packages/plg_content_mokobackup/mokobackup.xml | 2 +- src/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- src/packages/plg_system_mokobackup/mokobackup.xml | 2 +- src/packages/plg_task_mokobackup/mokobackup.xml | 2 +- src/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- src/pkg_mokobackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 512869a..4d18fbf 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.05-dev + 01.01.06-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 09a1a30..37661e6 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.05 +# VERSION: 01.01.06 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index 741420b..30f88ed 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 08a2744..4de00c9 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.05-dev + 01.01.06-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_actionlog_mokobackup/mokobackup.xml b/src/packages/plg_actionlog_mokobackup/mokobackup.xml index 43d280b..d0f39c5 100644 --- a/src/packages/plg_actionlog_mokobackup/mokobackup.xml +++ b/src/packages/plg_actionlog_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokobackup - 01.01.05-dev + 01.01.06-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_console_mokobackup/mokobackup.xml b/src/packages/plg_console_mokobackup/mokobackup.xml index 9992f42..225ff1d 100644 --- a/src/packages/plg_console_mokobackup/mokobackup.xml +++ b/src/packages/plg_console_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokobackup - 01.01.05-dev + 01.01.06-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_content_mokobackup/mokobackup.xml b/src/packages/plg_content_mokobackup/mokobackup.xml index b0610f3..fbfcdfd 100644 --- a/src/packages/plg_content_mokobackup/mokobackup.xml +++ b/src/packages/plg_content_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokobackup - 01.01.05-dev + 01.01.06-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/src/packages/plg_quickicon_mokobackup/mokobackup.xml index 5a666ac..25b5361 100644 --- a/src/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/src/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.05-dev + 01.01.06-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/src/packages/plg_system_mokobackup/mokobackup.xml index 64ab1cc..0804d35 100644 --- a/src/packages/plg_system_mokobackup/mokobackup.xml +++ b/src/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.05-dev + 01.01.06-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml index bf9d222..1bd377d 100644 --- a/src/packages/plg_task_mokobackup/mokobackup.xml +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.05-dev + 01.01.06-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/src/packages/plg_webservices_mokobackup/mokobackup.xml index 28b70fe..aeb32e9 100644 --- a/src/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/src/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.05-dev + 01.01.06-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index af4eb8b..2dc8c30 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.05-dev + 01.01.06-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 807da034c9faeac77b3565d38f1f325574ebcbb4 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 16:21:33 -0500 Subject: [PATCH 35/81] fix: add config.xml for component Options The Options toolbar button was crashing with "Form::loadForm could not load file" because config.xml did not exist. Added config.xml with global settings: default backup dir, default profile, update notice toggle, cleanup defaults, notification defaults, and permissions. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/packages/com_mokobackup/config.xml | 108 ++++++++++++++++++ .../language/en-GB/com_mokobackup.ini | 21 ++++ .../language/en-US/com_mokobackup.ini | 19 +++ 3 files changed, 148 insertions(+) create mode 100644 src/packages/com_mokobackup/config.xml diff --git a/src/packages/com_mokobackup/config.xml b/src/packages/com_mokobackup/config.xml new file mode 100644 index 0000000..d18904e --- /dev/null +++ b/src/packages/com_mokobackup/config.xml @@ -0,0 +1,108 @@ + + + +
+ + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + + +
+ +
+ +
+
diff --git a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini index 442c06a..0924e01 100644 --- a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini +++ b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini @@ -209,6 +209,27 @@ COM_MOKOBACKUP_UPDATE_SITE_NOTICE="To receive automatic updates, configure your COM_MOKOBACKUP_UPDATE_SITE_MISSING="MokoJoomBackup update site not found. Reinstall the package to register the update server." COM_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." +; Component Options (config.xml) +COM_MOKOBACKUP_CONFIG_GENERAL="General" +COM_MOKOBACKUP_CONFIG_DEFAULT_BACKUP_DIR="Default Backup Directory" +COM_MOKOBACKUP_CONFIG_DEFAULT_BACKUP_DIR_DESC="Default directory for backup archives, relative to Joomla root. Can be overridden per profile." +COM_MOKOBACKUP_CONFIG_DEFAULT_PROFILE="Default Profile" +COM_MOKOBACKUP_CONFIG_DEFAULT_PROFILE_DESC="Default backup profile used by quick actions and CLI when no profile is specified." +COM_MOKOBACKUP_CONFIG_SHOW_UPDATE_NOTICE="Show Update Site Notice" +COM_MOKOBACKUP_CONFIG_SHOW_UPDATE_NOTICE_DESC="Display the update site configuration notice on the Backup Records view." +COM_MOKOBACKUP_CONFIG_CLEANUP="Cleanup Defaults" +COM_MOKOBACKUP_CONFIG_MAX_AGE="Max Backup Age (days)" +COM_MOKOBACKUP_CONFIG_MAX_AGE_DESC="Default maximum age for backup records. Used by the system plugin and CLI cleanup command." +COM_MOKOBACKUP_CONFIG_MAX_BACKUPS="Max Backup Count" +COM_MOKOBACKUP_CONFIG_MAX_BACKUPS_DESC="Default maximum number of completed backups to retain." +COM_MOKOBACKUP_CONFIG_NOTIFICATIONS="Notifications" +COM_MOKOBACKUP_CONFIG_NOTIFY_EMAIL="Global Notification Email(s)" +COM_MOKOBACKUP_CONFIG_NOTIFY_EMAIL_DESC="Comma-separated list of email addresses for global backup notifications. Per-profile settings override this." +COM_MOKOBACKUP_CONFIG_NOTIFY_SUCCESS="Notify on Success" +COM_MOKOBACKUP_CONFIG_NOTIFY_SUCCESS_DESC="Send email when any backup completes successfully (unless overridden by profile)." +COM_MOKOBACKUP_CONFIG_NOTIFY_FAILURE="Notify on Failure" +COM_MOKOBACKUP_CONFIG_NOTIFY_FAILURE_DESC="Send email when any backup fails (unless overridden by profile)." + ; Errors COM_MOKOBACKUP_ERROR_FILE_NOT_FOUND="Backup archive file not found or has been deleted." COM_MOKOBACKUP_ERROR_NO_RECORD_SELECTED="No backup record selected for restore." diff --git a/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini b/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini index 577580b..7a87412 100644 --- a/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini +++ b/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini @@ -29,3 +29,22 @@ COM_MOKOBACKUP_NO_PROFILES="No backup profiles found." COM_MOKOBACKUP_UPDATE_SITE_NOTICE="To receive automatic updates, configure your Update Site with your download key." COM_MOKOBACKUP_UPDATE_SITE_MISSING="MokoJoomBackup update site not found. Reinstall the package to register the update server." COM_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." +COM_MOKOBACKUP_CONFIG_GENERAL="General" +COM_MOKOBACKUP_CONFIG_DEFAULT_BACKUP_DIR="Default Backup Directory" +COM_MOKOBACKUP_CONFIG_DEFAULT_BACKUP_DIR_DESC="Default directory for backup archives, relative to Joomla root. Can be overridden per profile." +COM_MOKOBACKUP_CONFIG_DEFAULT_PROFILE="Default Profile" +COM_MOKOBACKUP_CONFIG_DEFAULT_PROFILE_DESC="Default backup profile used by quick actions and CLI when no profile is specified." +COM_MOKOBACKUP_CONFIG_SHOW_UPDATE_NOTICE="Show Update Site Notice" +COM_MOKOBACKUP_CONFIG_SHOW_UPDATE_NOTICE_DESC="Display the update site configuration notice on the Backup Records view." +COM_MOKOBACKUP_CONFIG_CLEANUP="Cleanup Defaults" +COM_MOKOBACKUP_CONFIG_MAX_AGE="Max Backup Age (days)" +COM_MOKOBACKUP_CONFIG_MAX_AGE_DESC="Default maximum age for backup records. Used by the system plugin and CLI cleanup command." +COM_MOKOBACKUP_CONFIG_MAX_BACKUPS="Max Backup Count" +COM_MOKOBACKUP_CONFIG_MAX_BACKUPS_DESC="Default maximum number of completed backups to retain." +COM_MOKOBACKUP_CONFIG_NOTIFICATIONS="Notifications" +COM_MOKOBACKUP_CONFIG_NOTIFY_EMAIL="Global Notification Email(s)" +COM_MOKOBACKUP_CONFIG_NOTIFY_EMAIL_DESC="Comma-separated list of email addresses for global backup notifications. Per-profile settings override this." +COM_MOKOBACKUP_CONFIG_NOTIFY_SUCCESS="Notify on Success" +COM_MOKOBACKUP_CONFIG_NOTIFY_SUCCESS_DESC="Send email when any backup completes successfully (unless overridden by profile)." +COM_MOKOBACKUP_CONFIG_NOTIFY_FAILURE="Notify on Failure" +COM_MOKOBACKUP_CONFIG_NOTIFY_FAILURE_DESC="Send email when any backup fails (unless overridden by profile)." -- 2.52.0 From 8d80c218da288f164a9874723eade67283e3a757 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 21:23:01 +0000 Subject: [PATCH 36/81] chore(version): auto-bump 01.01.07-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- src/packages/com_mokobackup/mokobackup.xml | 2 +- src/packages/plg_actionlog_mokobackup/mokobackup.xml | 2 +- src/packages/plg_console_mokobackup/mokobackup.xml | 2 +- src/packages/plg_content_mokobackup/mokobackup.xml | 2 +- src/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- src/packages/plg_system_mokobackup/mokobackup.xml | 2 +- src/packages/plg_task_mokobackup/mokobackup.xml | 2 +- src/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- src/pkg_mokobackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 4d18fbf..88620a1 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.06-dev + 01.01.07-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 37661e6..825b392 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.06 +# VERSION: 01.01.07 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index 30f88ed..547489b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/src/packages/com_mokobackup/mokobackup.xml b/src/packages/com_mokobackup/mokobackup.xml index 4de00c9..9bf599f 100644 --- a/src/packages/com_mokobackup/mokobackup.xml +++ b/src/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.06-dev + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_actionlog_mokobackup/mokobackup.xml b/src/packages/plg_actionlog_mokobackup/mokobackup.xml index d0f39c5..343dc1a 100644 --- a/src/packages/plg_actionlog_mokobackup/mokobackup.xml +++ b/src/packages/plg_actionlog_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokobackup - 01.01.06-dev + 01.01.07-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_console_mokobackup/mokobackup.xml b/src/packages/plg_console_mokobackup/mokobackup.xml index 225ff1d..262f1c7 100644 --- a/src/packages/plg_console_mokobackup/mokobackup.xml +++ b/src/packages/plg_console_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokobackup - 01.01.06-dev + 01.01.07-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_content_mokobackup/mokobackup.xml b/src/packages/plg_content_mokobackup/mokobackup.xml index fbfcdfd..8548bda 100644 --- a/src/packages/plg_content_mokobackup/mokobackup.xml +++ b/src/packages/plg_content_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokobackup - 01.01.06-dev + 01.01.07-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/src/packages/plg_quickicon_mokobackup/mokobackup.xml index 25b5361..4341a06 100644 --- a/src/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/src/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.06-dev + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/src/packages/plg_system_mokobackup/mokobackup.xml index 0804d35..899f088 100644 --- a/src/packages/plg_system_mokobackup/mokobackup.xml +++ b/src/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.06-dev + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml index 1bd377d..cfb1ffc 100644 --- a/src/packages/plg_task_mokobackup/mokobackup.xml +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.06-dev + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/src/packages/plg_webservices_mokobackup/mokobackup.xml index aeb32e9..5ae3520 100644 --- a/src/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/src/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.06-dev + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index 2dc8c30..7f11b69 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.06-dev + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 26635f933c41c3df2fe7763a2d89579b5e9e6b87 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Thu, 4 Jun 2026 22:02:33 +0000 Subject: [PATCH 37/81] chore: add dlid and blockChildUninstall to package manifest [skip ci] --- src/pkg_mokobackup.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index 7f11b69..86dd028 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -37,4 +37,6 @@ https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/updates.xml + + true -- 2.52.0 From d19274d88bdbc2cd4cefe3c23887362c15e71f98 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 19:06:06 -0500 Subject: [PATCH 38/81] chore: remove deprecated update-server.yml workflow [skip ci] Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/workflows/update-server.yml | 302 ------------------------- 1 file changed, 302 deletions(-) delete mode 100644 .mokogitea/workflows/update-server.yml diff --git a/.mokogitea/workflows/update-server.yml b/.mokogitea/workflows/update-server.yml deleted file mode 100644 index ac5c9a5..0000000 --- a/.mokogitea/workflows/update-server.yml +++ /dev/null @@ -1,302 +0,0 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Universal -# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform -# PATH: /templates/workflows/update-server.yml -# VERSION: 09.23.00 -# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches -# -# Thin wrapper around moko-platform CLI tools. -# Builds packages, updates updates.xml, and optionally deploys via SFTP. -# -# Joomla filters update entries by the user's "Minimum Stability" setting. - -name: "Update Server" - -on: - push: - branches: - - 'dev' - - 'dev/**' - - 'alpha/**' - - 'beta/**' - - 'rc/**' - paths: - - 'src/**' - - 'htdocs/**' - pull_request: - types: [closed] - branches: - - 'dev' - - 'dev/**' - - 'alpha/**' - - 'beta/**' - - 'rc/**' - paths: - - 'src/**' - - 'htdocs/**' - workflow_dispatch: - inputs: - stability: - description: 'Stability tag' - required: true - default: 'development' - type: choice - options: - - development - - alpha - - beta - - rc - - stable - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -permissions: - contents: write - -jobs: - update-xml: - name: Update Server - runs-on: release - if: >- - github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push' - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 0 - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}' - run: | - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform 2>/dev/null || true - if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then - cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true - fi - echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV" - - - name: Detect platform - id: platform - run: php ${MOKO_CLI}/manifest_read.php --path . --github-output - - - name: Resolve stability and bump version - id: meta - run: | - BRANCH="${{ github.ref_name }}" - - # Configure git for bot pushes - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - # Determine stability from branch or manual input - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - STABILITY="${{ inputs.stability }}" - elif [[ "$BRANCH" == rc/* ]]; then - STABILITY="rc" - elif [[ "$BRANCH" == beta/* ]]; then - STABILITY="beta" - elif [[ "$BRANCH" == alpha/* ]]; then - STABILITY="alpha" - else - STABILITY="development" - fi - - # Gitea release tag per stability - case "$STABILITY" in - development) TAG="development" ;; - alpha) TAG="alpha" ;; - beta) TAG="beta" ;; - rc) TAG="release-candidate" ;; - *) TAG="stable" ;; - esac - - # Bump patch, set platform suffix, fix consistency — version_bump preserves suffix - php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo '00.00.01')" \ - --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true - php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true - php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - - # Read final version (includes suffix, e.g. 01.02.15-dev) - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") - - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - - # Commit version bump if changed - git add -A - git diff --cached --quiet || { - git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \ - --author="gitea-actions[bot] " - git push - } - - - name: Create release and upload package - id: package - run: | - VERSION="${{ steps.meta.outputs.version }}" - TAG="${{ steps.meta.outputs.tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - # Create or update Gitea release - php ${MOKO_CLI}/release_create.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease - - # Build package and upload - php ${MOKO_CLI}/release_package.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp || true - - - name: Update updates.xml - if: steps.platform.outputs.platform == 'joomla' - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - - if [ ! -f "updates.xml" ]; then - echo "No updates.xml — skipping" - exit 0 - fi - - SHA_FLAG="" - [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" - - php ${MOKO_CLI}/updates_xml_build.php \ - --path . --version "${VERSION}" --stability "${STABILITY}" \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - ${SHA_FLAG} - - # Commit and push updates.xml - git add updates.xml - git diff --cached --quiet || { - git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" - git push - } - - - name: Sync updates.xml to main - if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla' - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \ - "${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true) - - if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then - python3 -c " - import base64, json, urllib.request, sys - with open('updates.xml', 'rb') as f: - content = base64.b64encode(f.read()).decode() - payload = json.dumps({ - 'content': content, - 'sha': '${FILE_SHA}', - 'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]', - 'branch': 'main' - }).encode() - req = urllib.request.Request( - '${API_BASE}/contents/updates.xml', - data=payload, method='PUT', - headers={ - 'Authorization': 'token ${GITEA_TOKEN}', - 'Content-Type': 'application/json' - }) - try: - urllib.request.urlopen(req) - print('updates.xml synced to main') - except Exception as e: - print(f'WARNING: sync to main failed: {e}', file=sys.stderr) - " - fi - - - name: SFTP deploy to dev server - if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev' - env: - DEV_HOST: ${{ vars.DEV_FTP_HOST }} - DEV_PATH: ${{ vars.DEV_FTP_PATH }} - DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} - DEV_USER: ${{ vars.DEV_FTP_USERNAME }} - DEV_PORT: ${{ vars.DEV_FTP_PORT }} - DEV_KEY: ${{ secrets.DEV_FTP_KEY }} - DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }} - run: | - # Permission check: admin or maintain role required - ACTOR="${{ github.actor }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \ - python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read") - case "$PERMISSION" in - admin|maintain|write) ;; - *) - echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write" - exit 0 - ;; - esac - - [ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; } - - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - [ ! -d "$SOURCE_DIR" ] && exit 0 - - PORT="${DEV_PORT:-22}" - REMOTE="${DEV_PATH%/}" - [ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}" - - printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \ - "$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json - if [ -n "$DEV_KEY" ]; then - echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key - printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json - else - printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json - fi - - PLATFORM=$(php ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true) - if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then - php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json - elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then - php ${MOKO_CLI}/../deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json - fi - rm -f /tmp/deploy_key /tmp/sftp-config.json - echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY - - - name: Summary - if: always() - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - DISPLAY="${VERSION}" - echo "## Update Server" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY -- 2.52.0 From fea1800e06ea0a536181ef462af84bf20d957033 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 19:57:36 -0500 Subject: [PATCH 39/81] fix: console plugin namespace and quickicon translation - Console: use Joomla\Application\ApplicationEvents (was wrong namespace Joomla\Application\Event\ApplicationEvents causing ClassNotFoundError on CLI) - Quickicon: translate text key with Text::_() so language strings render instead of showing raw keys Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../plg_console_mokobackup/src/Extension/MokoBackupConsole.php | 2 +- .../src/Extension/MokoBackupQuickicon.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php b/src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php index 5c2ee4f..fca96ac 100644 --- a/src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php +++ b/src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php @@ -28,7 +28,7 @@ final class MokoBackupConsole extends CMSPlugin implements SubscriberInterface public static function getSubscribedEvents(): array { return [ - \Joomla\Application\Event\ApplicationEvents::BEFORE_EXECUTE => 'registerCommands', + \Joomla\Application\ApplicationEvents::BEFORE_EXECUTE => 'registerCommands', ]; } diff --git a/src/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php b/src/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php index 5d0bd9a..c72cda5 100644 --- a/src/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php +++ b/src/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php @@ -15,6 +15,7 @@ namespace Joomla\Plugin\Quickicon\MokoBackup\Extension; defined('_JEXEC') or die; use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Event\Event; use Joomla\Event\SubscriberInterface; @@ -96,7 +97,7 @@ final class MokoBackupQuickicon extends CMSPlugin implements SubscriberInterface 'link' => 'index.php?option=com_mokobackup&view=backups', 'image' => $warning ? 'icon-warning' : 'icon-database', 'icon' => $warning ? 'icon-warning' : 'icon-database', - 'text' => $text, + 'text' => Text::_($text), 'linkadd' => $subtitle ? '
' . htmlspecialchars($subtitle) . '' : '', 'id' => 'plg_quickicon_mokobackup', 'group' => 'MOD_QUICKICON_MAINTENANCE', -- 2.52.0 From 368461577e23e20c4e4c8edeff86e9fc16051897 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 20:15:52 -0500 Subject: [PATCH 40/81] fix: CLI exit codes and SQL schema defaults - Console commands: replace self::SUCCESS/FAILURE with 0/1 (Joomla's AbstractCommand doesn't define these Symfony constants) - SQL: make manifest and log columns DEFAULT NULL to prevent "doesn't have a default value" on INSERT Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/packages/com_mokobackup/sql/install.mysql.sql | 4 ++-- .../com_mokobackup/sql/updates/mysql/01.01.08.sql | 4 ++++ .../src/Command/CleanupCommand.php | 2 +- .../src/Command/ListCommand.php | 4 ++-- .../src/Command/ProfilesCommand.php | 4 ++-- .../src/Command/RestoreCommand.php | 14 +++++++------- .../src/Command/RunCommand.php | 6 +++--- 7 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 src/packages/com_mokobackup/sql/updates/mysql/01.01.08.sql diff --git a/src/packages/com_mokobackup/sql/install.mysql.sql b/src/packages/com_mokobackup/sql/install.mysql.sql index b565a01..877e5f2 100644 --- a/src/packages/com_mokobackup/sql/install.mysql.sql +++ b/src/packages/com_mokobackup/sql/install.mysql.sql @@ -63,8 +63,8 @@ CREATE TABLE IF NOT EXISTS `#__mokobackup_records` ( `remote_filename` VARCHAR(512) NOT NULL DEFAULT '', `checksum` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'SHA-256 hash of archive', `base_record_id` INT(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Base full backup ID for differential', - `manifest` LONGTEXT NOT NULL COMMENT 'JSON file manifest for differential comparison', - `log` MEDIUMTEXT NOT NULL COMMENT 'Step-by-step backup log', + `manifest` LONGTEXT DEFAULT NULL COMMENT 'JSON file manifest for differential comparison', + `log` MEDIUMTEXT DEFAULT NULL COMMENT 'Step-by-step backup log', PRIMARY KEY (`id`), KEY `idx_profile` (`profile_id`), KEY `idx_status` (`status`), diff --git a/src/packages/com_mokobackup/sql/updates/mysql/01.01.08.sql b/src/packages/com_mokobackup/sql/updates/mysql/01.01.08.sql new file mode 100644 index 0000000..29716e0 --- /dev/null +++ b/src/packages/com_mokobackup/sql/updates/mysql/01.01.08.sql @@ -0,0 +1,4 @@ +-- MokoJoomBackup 01.01.08 +-- Fix: allow NULL defaults for manifest and log columns +ALTER TABLE `#__mokobackup_records` MODIFY `manifest` LONGTEXT DEFAULT NULL; +ALTER TABLE `#__mokobackup_records` MODIFY `log` MEDIUMTEXT DEFAULT NULL; diff --git a/src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php b/src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php index b26a2a5..1a8509a 100644 --- a/src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php +++ b/src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php @@ -120,6 +120,6 @@ class CleanupCommand extends AbstractCommand $io->success(($dryRun ? 'Would delete ' : 'Deleted ') . $deleted . ' backup record(s).'); } - return self::SUCCESS; + return 0; } } diff --git a/src/packages/plg_console_mokobackup/src/Command/ListCommand.php b/src/packages/plg_console_mokobackup/src/Command/ListCommand.php index 8fcdddd..9586339 100644 --- a/src/packages/plg_console_mokobackup/src/Command/ListCommand.php +++ b/src/packages/plg_console_mokobackup/src/Command/ListCommand.php @@ -56,7 +56,7 @@ class ListCommand extends AbstractCommand if (empty($records)) { $io->info('No backup records found.'); - return self::SUCCESS; + return 0; } $rows = []; @@ -82,6 +82,6 @@ class ListCommand extends AbstractCommand $rows ); - return self::SUCCESS; + return 0; } } diff --git a/src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php b/src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php index 4d81616..8f4b21c 100644 --- a/src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php +++ b/src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php @@ -44,7 +44,7 @@ class ProfilesCommand extends AbstractCommand if (empty($profiles)) { $io->info('No backup profiles found.'); - return self::SUCCESS; + return 0; } $rows = []; @@ -63,6 +63,6 @@ class ProfilesCommand extends AbstractCommand $rows ); - return self::SUCCESS; + return 0; } } diff --git a/src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php b/src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php index fb857d4..e5f9082 100644 --- a/src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php +++ b/src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php @@ -48,19 +48,19 @@ class RestoreCommand extends AbstractCommand if (!$record) { $io->error('Backup record not found: ' . $recordId); - return self::FAILURE; + return 1; } if ($record->status !== 'complete') { $io->error('Cannot restore — backup status is: ' . $record->status); - return self::FAILURE; + return 1; } if (empty($record->absolute_path) || !is_file($record->absolute_path)) { $io->error('Backup archive not found: ' . ($record->absolute_path ?: 'no path')); - return self::FAILURE; + return 1; } $io->warning('This will overwrite the current site files and/or database.'); @@ -70,7 +70,7 @@ class RestoreCommand extends AbstractCommand if (!$io->confirm('Are you sure you want to continue?', false)) { $io->info('Restore cancelled.'); - return self::SUCCESS; + return 0; } $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokobackup/src/Engine/RestoreEngine.php'; @@ -78,7 +78,7 @@ class RestoreCommand extends AbstractCommand if (!file_exists($engineFile)) { $io->error('RestoreEngine not found. Is the component fully installed?'); - return self::FAILURE; + return 1; } if (!class_exists(RestoreEngine::class)) { @@ -91,11 +91,11 @@ class RestoreCommand extends AbstractCommand if ($result['success']) { $io->success($result['message']); - return self::SUCCESS; + return 0; } $io->error($result['message']); - return self::FAILURE; + return 1; } } diff --git a/src/packages/plg_console_mokobackup/src/Command/RunCommand.php b/src/packages/plg_console_mokobackup/src/Command/RunCommand.php index 52185b9..d187737 100644 --- a/src/packages/plg_console_mokobackup/src/Command/RunCommand.php +++ b/src/packages/plg_console_mokobackup/src/Command/RunCommand.php @@ -45,7 +45,7 @@ class RunCommand extends AbstractCommand if (!file_exists($engineFile)) { $io->error('MokoJoomBackup component not installed.'); - return self::FAILURE; + return 1; } if (!class_exists(BackupEngine::class)) { @@ -58,11 +58,11 @@ class RunCommand extends AbstractCommand if ($result['success']) { $io->success($result['message']); - return self::SUCCESS; + return 0; } $io->error($result['message']); - return self::FAILURE; + return 1; } } -- 2.52.0 From cf50551595f3622fb94c3bec1392e76c781526a7 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 20:30:28 -0500 Subject: [PATCH 41/81] feat: folder picker field and default directory dashboard warning - Custom FolderPickerField with AJAX server-side directory browser (Browse button opens collapsible tree, click to navigate, double-click to select). Uses safe DOM methods instead of innerHTML. - AjaxController::browseDir() endpoint lists subdirectories - Dashboard shows warning when any profile uses the default backup directory inside the web root - Profile form and config.xml use FolderPicker for backup_dir field Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/packages/com_mokobackup/config.xml | 4 +- src/packages/com_mokobackup/forms/profile.xml | 4 +- .../language/en-GB/com_mokobackup.ini | 9 + .../language/en-US/com_mokobackup.ini | 5 + .../src/Controller/AjaxController.php | 57 ++++++ .../src/Field/FolderPickerField.php | 170 ++++++++++++++++++ .../src/Model/DashboardModel.php | 22 +++ .../src/View/Dashboard/HtmlView.php | 12 +- .../com_mokobackup/tmpl/dashboard/default.php | 13 ++ 9 files changed, 287 insertions(+), 9 deletions(-) create mode 100644 src/packages/com_mokobackup/src/Field/FolderPickerField.php diff --git a/src/packages/com_mokobackup/config.xml b/src/packages/com_mokobackup/config.xml index d18904e..d1a0011 100644 --- a/src/packages/com_mokobackup/config.xml +++ b/src/packages/com_mokobackup/config.xml @@ -10,11 +10,11 @@
sendJson($result); } + /** + * Browse server directories for the folder picker field. + * POST: task=ajax.browseDir&path=/some/path + */ + public function browseDir(): void + { + if (!Session::checkToken('get') && !Session::checkToken('post')) { + $this->sendJson(['error' => true, 'message' => 'Invalid token']); + + return; + } + + $path = $this->input->getString('path', JPATH_ROOT); + $path = realpath($path) ?: $path; + + if (!is_dir($path)) { + $this->sendJson(['error' => true, 'message' => 'Directory not found: ' . $path]); + + return; + } + + // Security: only allow browsing within JPATH_ROOT or parent directories + // that could contain a backup folder (e.g., /home/user/backups) + $dirs = []; + $handle = @opendir($path); + + if ($handle) { + while (($entry = readdir($handle)) !== false) { + if ($entry === '.' || $entry === '..') { + continue; + } + + $fullPath = $path . '/' . $entry; + + if (is_dir($fullPath) && $entry[0] !== '.') { + $dirs[] = [ + 'name' => $entry, + 'path' => $fullPath, + ]; + } + } + + closedir($handle); + } + + usort($dirs, fn($a, $b) => strcasecmp($a['name'], $b['name'])); + + $parent = dirname($path); + + $this->sendJson([ + 'error' => false, + 'current' => $path, + 'parent' => ($parent !== $path) ? $parent : null, + 'dirs' => $dirs, + ]); + } + /** * Send a JSON response and close the application. */ diff --git a/src/packages/com_mokobackup/src/Field/FolderPickerField.php b/src/packages/com_mokobackup/src/Field/FolderPickerField.php new file mode 100644 index 0000000..447725d --- /dev/null +++ b/src/packages/com_mokobackup/src/Field/FolderPickerField.php @@ -0,0 +1,170 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Component\MokoBackup\Administrator\Field; + +defined('_JEXEC') or die; + +use Joomla\CMS\Form\FormField; +use Joomla\CMS\Language\Text; + +class FolderPickerField extends FormField +{ + protected $type = 'FolderPicker'; + + protected function getInput(): string + { + $value = htmlspecialchars($this->value ?: $this->default, ENT_QUOTES, 'UTF-8'); + $id = htmlspecialchars($this->id, ENT_QUOTES, 'UTF-8'); + $name = htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8'); + $jRoot = JPATH_ROOT; + + // Resolve to absolute for display + $rawValue = $this->value ?: $this->default; + + if ($rawValue && $rawValue[0] !== '/') { + $absPath = $jRoot . '/' . $rawValue; + } else { + $absPath = $rawValue; + } + + $exists = is_dir($absPath); + $statusClass = $exists ? 'text-success' : 'text-danger'; + $statusIcon = $exists ? 'icon-publish' : 'icon-unpublish'; + $statusText = $exists + ? Text::_('COM_MOKOBACKUP_FOLDER_EXISTS') + : Text::_('COM_MOKOBACKUP_FOLDER_NOT_FOUND'); + $absPathSafe = htmlspecialchars($absPath, ENT_QUOTES, 'UTF-8'); + + return << + + + +
+ + + {$statusText}: {$absPathSafe} + +
+ + +HTML; + } +} diff --git a/src/packages/com_mokobackup/src/Model/DashboardModel.php b/src/packages/com_mokobackup/src/Model/DashboardModel.php index d10a2b2..1b56de5 100644 --- a/src/packages/com_mokobackup/src/Model/DashboardModel.php +++ b/src/packages/com_mokobackup/src/Model/DashboardModel.php @@ -143,6 +143,28 @@ class DashboardModel extends BaseDatabaseModel return $checks; } + /** + * Check if any profiles use the default (web-root) backup directory. + * + * @return bool + */ + public function isUsingDefaultBackupDir(): bool + { + $db = $this->getDatabase(); + $default = 'administrator/components/com_mokobackup/backups'; + + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($db->quoteName('#__mokobackup_profiles')) + ->where($db->quoteName('published') . ' = 1') + ->where('(' . $db->quoteName('backup_dir') . ' = ' . $db->quote($default) + . ' OR ' . $db->quoteName('backup_dir') . ' = ' . $db->quote('') + . ' OR ' . $db->quoteName('backup_dir') . ' IS NULL)'); + $db->setQuery($query); + + return (int) $db->loadResult() > 0; + } + /** * Get published backup profiles for the quick-action selector. * diff --git a/src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php b/src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php index bbfa660..ac24790 100644 --- a/src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php +++ b/src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php @@ -23,17 +23,19 @@ class HtmlView extends BaseHtmlView public object $stats; public array $systemHealth = []; public array $profiles = []; + public bool $defaultDirWarning = false; public function display($tpl = null): void { /** @var \Joomla\Component\MokoBackup\Administrator\Model\DashboardModel $model */ $model = $this->getModel(); - $this->lastBackup = $model->getLastBackup(); - $this->nextScheduled = $model->getNextScheduled(); - $this->stats = $model->getStats(); - $this->systemHealth = $model->getSystemHealth(); - $this->profiles = $model->getProfiles(); + $this->lastBackup = $model->getLastBackup(); + $this->nextScheduled = $model->getNextScheduled(); + $this->stats = $model->getStats(); + $this->systemHealth = $model->getSystemHealth(); + $this->profiles = $model->getProfiles(); + $this->defaultDirWarning = $model->isUsingDefaultBackupDir(); $this->addToolbar(); diff --git a/src/packages/com_mokobackup/tmpl/dashboard/default.php b/src/packages/com_mokobackup/tmpl/dashboard/default.php index 8d7d43c..5f33f87 100644 --- a/src/packages/com_mokobackup/tmpl/dashboard/default.php +++ b/src/packages/com_mokobackup/tmpl/dashboard/default.php @@ -18,6 +18,19 @@ use Joomla\CMS\Session\Session; $ajaxToken = Session::getFormToken(); $ajaxUrl = Route::_('index.php?option=com_mokobackup&format=json', false); ?> +defaultDirWarning) : ?> + + +
-- 2.52.0 From 75e72c248a5cd3aeba40d853c871f1934f14e318 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 20:40:07 -0500 Subject: [PATCH 42/81] feat: tar.gz archives, table checkbox excludes, user group notifications (#32, #33, #34) Archive formats (#32): - ArchiverInterface abstraction with ZipArchiver and TarGzArchiver - BackupEngine uses archiver factory based on profile archive_format - tar.gz uses PharData (bundled with PHP, no extra extensions) - RestoreEngine detects and extracts tar.gz via PharData - AES-256 encryption skipped for non-ZIP formats with log warning Exclude fields (#33): - ExcludeListField: dynamic table with add/remove rows for dirs and files - DatabaseTablesField: auto-populated checkbox list of all site tables - Replaces textarea-based exclusion fields in profile form User group notifications (#34): - usergrouplist field added to profile notifications fieldset - NotificationSender resolves group members to emails at send time - Combined with manual email addresses, deduplicated - SQL migration adds notify_user_groups column Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/packages/com_mokobackup/forms/profile.xml | 26 ++-- .../language/en-GB/com_mokobackup.ini | 8 ++ .../language/en-US/com_mokobackup.ini | 7 + .../com_mokobackup/sql/install.mysql.sql | 1 + .../sql/updates/mysql/01.01.08.sql | 3 + .../src/Engine/ArchiverInterface.php | 41 ++++++ .../src/Engine/BackupEngine.php | 41 ++++-- .../src/Engine/NotificationSender.php | 50 +++++++- .../src/Engine/RestoreEngine.php | 15 ++- .../src/Engine/TarGzArchiver.php | 63 +++++++++ .../com_mokobackup/src/Engine/ZipArchiver.php | 47 +++++++ .../src/Field/DatabaseTablesField.php | 105 +++++++++++++++ .../src/Field/ExcludeListField.php | 120 ++++++++++++++++++ 13 files changed, 500 insertions(+), 27 deletions(-) create mode 100644 src/packages/com_mokobackup/src/Engine/ArchiverInterface.php create mode 100644 src/packages/com_mokobackup/src/Engine/TarGzArchiver.php create mode 100644 src/packages/com_mokobackup/src/Engine/ZipArchiver.php create mode 100644 src/packages/com_mokobackup/src/Field/DatabaseTablesField.php create mode 100644 src/packages/com_mokobackup/src/Field/ExcludeListField.php diff --git a/src/packages/com_mokobackup/forms/profile.xml b/src/packages/com_mokobackup/forms/profile.xml index 837464c..827f6cd 100644 --- a/src/packages/com_mokobackup/forms/profile.xml +++ b/src/packages/com_mokobackup/forms/profile.xml @@ -39,6 +39,7 @@ default="zip" > +
@@ -176,6 +176,14 @@ maxlength="512" hint="admin@example.com, backup@example.com" /> + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Component\MokoBackup\Administrator\Engine; + +defined('_JEXEC') or die; + +interface ArchiverInterface +{ + /** + * Open or create the archive at the given path. + */ + public function open(string $path): void; + + /** + * Add a string as a file inside the archive. + */ + public function addFromString(string $localName, string $contents): void; + + /** + * Add a file from disk into the archive. + */ + public function addFile(string $filePath, string $localName): void; + + /** + * Finalize and close the archive. + */ + public function close(): void; + + /** + * Return the file extension for this archive type (e.g. 'zip', 'tar.gz'). + */ + public function getExtension(): string; +} diff --git a/src/packages/com_mokobackup/src/Engine/BackupEngine.php b/src/packages/com_mokobackup/src/Engine/BackupEngine.php index cf456cd..1b239fd 100644 --- a/src/packages/com_mokobackup/src/Engine/BackupEngine.php +++ b/src/packages/com_mokobackup/src/Engine/BackupEngine.php @@ -71,7 +71,10 @@ class BackupEngine $now = date('Y-m-d H:i:s'); $tag = date('Ymd_His'); $hostname = preg_replace('/[^a-zA-Z0-9._-]/', '', $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? php_uname('n')); - $archiveName = $hostname . '_' . $tag . '_profile' . $profileId . '.zip'; + $archiveFormat = $profile->archive_format ?? 'zip'; + $archiver = $this->createArchiver($archiveFormat); + $archiveExt = $archiver->getExtension(); + $archiveName = $hostname . '_' . $tag . '_profile' . $profileId . '.' . $archiveExt; if (empty($description)) { $description = $profile->title . ' — ' . $now; @@ -105,12 +108,8 @@ class BackupEngine $this->log('Backup started: ' . $description); $archivePath = $this->backupDir . '/' . $archiveName; - // Create ZIP archive - $zip = new \ZipArchive(); - - if ($zip->open($archivePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { - throw new \RuntimeException('Cannot create archive: ' . $archivePath); - } + // Create archive + $archiver->open($archivePath); $dbSize = 0; $filesCount = 0; @@ -121,7 +120,7 @@ class BackupEngine $this->log('Starting database dump...'); $dumper = new DatabaseDumper($excludeTables); $sqlDump = $dumper->dump(); - $zip->addFromString('database.sql', $sqlDump); + $archiver->addFromString('database.sql', $sqlDump); $dbSize = strlen($sqlDump); $tablesCount = $dumper->getTablesCount(); $this->log('Database dump complete: ' . $tablesCount . ' tables, ' . number_format($dbSize) . ' bytes'); @@ -157,7 +156,7 @@ class BackupEngine $fullPath = JPATH_ROOT . '/' . $relativePath; if (is_file($fullPath) && is_readable($fullPath)) { - $zip->addFile($fullPath, $relativePath); + $archiver->addFile($fullPath, $relativePath); } } @@ -170,15 +169,19 @@ class BackupEngine } } - $zip->close(); + $archiver->close(); // Step 1.5: Apply AES-256 encryption (if configured) $encryptionPassword = $profile->encryption_password ?? ''; if (!empty($encryptionPassword)) { - $this->log('Encrypting archive with AES-256...'); - $this->encryptArchive($archivePath, $encryptionPassword); - $this->log('Archive encrypted'); + if ($archiveFormat !== 'zip') { + $this->log('WARNING: AES-256 encryption only supported for ZIP archives — skipping encryption'); + } else { + $this->log('Encrypting archive with AES-256...'); + $this->encryptArchive($archivePath, $encryptionPassword); + $this->log('Archive encrypted'); + } } // Record archive size and compute checksum (after encryption) @@ -361,6 +364,18 @@ class BackupEngine return true; } + /** + * Create the appropriate archiver based on the archive format. + */ + private function createArchiver(string $format): ArchiverInterface + { + return match ($format) { + 'zip' => new ZipArchiver(), + 'tar.gz' => new TarGzArchiver(), + default => new ZipArchiver(), + }; + } + /** * Create the appropriate remote uploader based on the storage type. */ diff --git a/src/packages/com_mokobackup/src/Engine/NotificationSender.php b/src/packages/com_mokobackup/src/Engine/NotificationSender.php index 82808a0..9008aa2 100644 --- a/src/packages/com_mokobackup/src/Engine/NotificationSender.php +++ b/src/packages/com_mokobackup/src/Engine/NotificationSender.php @@ -33,9 +33,13 @@ class NotificationSender */ public static function send(object $profile, object $record, bool $success, string $logText = ''): bool { - $notifyEmail = trim($profile->notify_email ?? ''); + $notifyEmail = trim($profile->notify_email ?? ''); + $notifyUserGroups = $profile->notify_user_groups ?? ''; - if (empty($notifyEmail)) { + // Resolve user group members to email addresses + $groupEmails = self::resolveUserGroupEmails($notifyUserGroups); + + if (empty($notifyEmail) && empty($groupEmails)) { return false; } @@ -54,9 +58,10 @@ class NotificationSender $siteName = $config->get('sitename', 'Joomla Site'); $siteUrl = Uri::root(); - // Parse recipient list (comma-separated) + // Parse recipient list (comma-separated) + user group emails $recipients = array_map('trim', explode(',', $notifyEmail)); - $recipients = array_filter($recipients, fn($e) => filter_var($e, FILTER_VALIDATE_EMAIL)); + $recipients = array_merge($recipients, $groupEmails); + $recipients = array_unique(array_filter($recipients, fn($e) => filter_var($e, FILTER_VALIDATE_EMAIL))); if (empty($recipients)) { return false; @@ -133,4 +138,41 @@ class NotificationSender return false; } } + + /** + * Resolve user group IDs to email addresses of group members. + * + * @param string|array $groups Comma-separated group IDs or array + * + * @return array Email addresses + */ + private static function resolveUserGroupEmails(string|array $groups): array + { + if (empty($groups)) { + return []; + } + + if (\is_string($groups)) { + $groups = array_filter(array_map('intval', explode(',', $groups))); + } + + if (empty($groups)) { + return []; + } + + try { + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select('DISTINCT ' . $db->quoteName('u.email')) + ->from($db->quoteName('#__users', 'u')) + ->join('INNER', $db->quoteName('#__user_usergroup_map', 'ugm') . ' ON ugm.user_id = u.id') + ->where($db->quoteName('u.block') . ' = 0') + ->whereIn($db->quoteName('ugm.group_id'), $groups); + $db->setQuery($query); + + return $db->loadColumn() ?: []; + } catch (\Throwable $e) { + return []; + } + } } diff --git a/src/packages/com_mokobackup/src/Engine/RestoreEngine.php b/src/packages/com_mokobackup/src/Engine/RestoreEngine.php index eb33467..7099957 100644 --- a/src/packages/com_mokobackup/src/Engine/RestoreEngine.php +++ b/src/packages/com_mokobackup/src/Engine/RestoreEngine.php @@ -89,12 +89,15 @@ class RestoreEngine // Step 1: Extract archive to staging $this->log('Extracting archive: ' . basename($archivePath)); - // Detect format: JPA or ZIP + // Detect format: JPA, tar.gz, or ZIP if (JpaUnarchiver::isJpaFile($archivePath)) { $this->log('Detected JPA format (Akeeba Backup archive)'); $jpa = new JpaUnarchiver($archivePath, $this->stagingDir); $count = $jpa->extract(); $this->log('Extracted ' . $count . ' files from JPA'); + } elseif (str_ends_with($archivePath, '.tar.gz') || str_ends_with($archivePath, '.tgz')) { + $this->log('Detected tar.gz format'); + $this->extractTarGz($archivePath); } else { $this->extractArchive($archivePath, $password); } @@ -200,6 +203,16 @@ class RestoreEngine $zip->close(); } + /** + * Extract a tar.gz archive to the staging directory. + */ + private function extractTarGz(string $archivePath): void + { + $phar = new \PharData($archivePath); + $phar->extractTo($this->stagingDir, null, true); + $this->log('Extracted tar.gz archive'); + } + /** * Recursively delete a directory and all its contents. */ diff --git a/src/packages/com_mokobackup/src/Engine/TarGzArchiver.php b/src/packages/com_mokobackup/src/Engine/TarGzArchiver.php new file mode 100644 index 0000000..fdce0ce --- /dev/null +++ b/src/packages/com_mokobackup/src/Engine/TarGzArchiver.php @@ -0,0 +1,63 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Component\MokoBackup\Administrator\Engine; + +defined('_JEXEC') or die; + +class TarGzArchiver implements ArchiverInterface +{ + private \PharData $tar; + private string $tarPath; + + public function open(string $path): void + { + // PharData creates .tar first, then we compress to .tar.gz + // Strip .gz to get the .tar path for initial creation + $this->tarPath = preg_replace('/\.gz$/', '', $path); + + // Remove existing files to avoid "already exists" errors + if (is_file($this->tarPath)) { + @unlink($this->tarPath); + } + + if (is_file($path)) { + @unlink($path); + } + + $this->tar = new \PharData($this->tarPath); + } + + public function addFromString(string $localName, string $contents): void + { + $this->tar->addFromString($localName, $contents); + } + + public function addFile(string $filePath, string $localName): void + { + $this->tar->addFile($filePath, $localName); + } + + public function close(): void + { + // Compress the .tar to .tar.gz + $this->tar->compress(\Phar::GZ); + + // Remove the uncompressed .tar + if (is_file($this->tarPath)) { + @unlink($this->tarPath); + } + } + + public function getExtension(): string + { + return 'tar.gz'; + } +} diff --git a/src/packages/com_mokobackup/src/Engine/ZipArchiver.php b/src/packages/com_mokobackup/src/Engine/ZipArchiver.php new file mode 100644 index 0000000..e161035 --- /dev/null +++ b/src/packages/com_mokobackup/src/Engine/ZipArchiver.php @@ -0,0 +1,47 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Component\MokoBackup\Administrator\Engine; + +defined('_JEXEC') or die; + +class ZipArchiver implements ArchiverInterface +{ + private \ZipArchive $zip; + + public function open(string $path): void + { + $this->zip = new \ZipArchive(); + + if ($this->zip->open($path, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { + throw new \RuntimeException('Cannot create ZIP archive: ' . $path); + } + } + + public function addFromString(string $localName, string $contents): void + { + $this->zip->addFromString($localName, $contents); + } + + public function addFile(string $filePath, string $localName): void + { + $this->zip->addFile($filePath, $localName); + } + + public function close(): void + { + $this->zip->close(); + } + + public function getExtension(): string + { + return 'zip'; + } +} diff --git a/src/packages/com_mokobackup/src/Field/DatabaseTablesField.php b/src/packages/com_mokobackup/src/Field/DatabaseTablesField.php new file mode 100644 index 0000000..e563017 --- /dev/null +++ b/src/packages/com_mokobackup/src/Field/DatabaseTablesField.php @@ -0,0 +1,105 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Component\MokoBackup\Administrator\Field; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Form\FormField; +use Joomla\CMS\Language\Text; + +class DatabaseTablesField extends FormField +{ + protected $type = 'DatabaseTables'; + + protected function getInput(): string + { + $db = Factory::getDbo(); + $tables = $db->getTableList(); + $prefix = $db->getPrefix(); + + // Parse current exclusions (newline-separated) + $excluded = []; + + if (!empty($this->value)) { + $excluded = array_filter(array_map('trim', explode("\n", str_replace("\r", '', $this->value)))); + } + + // Normalize: replace literal #__ with actual prefix for comparison + $excludedNormalized = array_map(function ($t) use ($prefix) { + return str_replace('#__', $prefix, $t); + }, $excluded); + + $id = htmlspecialchars($this->id, ENT_QUOTES, 'UTF-8'); + $name = htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8'); + + $html = '
'; + $html .= ''; + $html .= '
' . Text::_('COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES_HELP') . '
'; + $html .= '
'; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + + foreach ($tables as $table) { + $isExcluded = \in_array($table, $excludedNormalized, true); + + // Convert to #__ notation for storage + $storeValue = $table; + + if (str_starts_with($table, $prefix)) { + $storeValue = '#__' . substr($table, \strlen($prefix)); + } + + $safeValue = htmlspecialchars($storeValue, ENT_QUOTES, 'UTF-8'); + $safeTable = htmlspecialchars($table, ENT_QUOTES, 'UTF-8'); + $checked = $isExcluded ? ' checked' : ''; + + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + + $html .= '
' . Text::_('COM_MOKOBACKUP_FIELD_TABLE_NAME') . '
' . $safeTable . '
'; + + // Script to sync checkboxes to hidden field + $html .= << +SCRIPT; + + return $html; + } +} diff --git a/src/packages/com_mokobackup/src/Field/ExcludeListField.php b/src/packages/com_mokobackup/src/Field/ExcludeListField.php new file mode 100644 index 0000000..483e68c --- /dev/null +++ b/src/packages/com_mokobackup/src/Field/ExcludeListField.php @@ -0,0 +1,120 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Component\MokoBackup\Administrator\Field; + +defined('_JEXEC') or die; + +use Joomla\CMS\Form\FormField; +use Joomla\CMS\Language\Text; + +class ExcludeListField extends FormField +{ + protected $type = 'ExcludeList'; + + protected function getInput(): string + { + $id = htmlspecialchars($this->id, ENT_QUOTES, 'UTF-8'); + $name = htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8'); + $placeholder = htmlspecialchars((string) ($this->element['hint'] ?? ''), ENT_QUOTES, 'UTF-8'); + + // Parse current values (newline-separated) + $items = []; + + if (!empty($this->value)) { + $items = array_values(array_filter(array_map('trim', explode("\n", str_replace("\r", '', $this->value))))); + } + + $html = '
'; + $html .= ''; + $html .= ''; + $html .= ''; + + foreach ($items as $item) { + $safeItem = htmlspecialchars($item, ENT_QUOTES, 'UTF-8'); + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + + $html .= '
'; + $html .= ''; + $html .= '
'; + + $html .= << +SCRIPT; + + return $html; + } +} -- 2.52.0 From 3c469f0dae0ee9ef13c8f49b38f5ff8ead7b9bc3 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Fri, 5 Jun 2026 00:11:46 -0500 Subject: [PATCH 43/81] feat: placeholder support, download fix, table exclusion modes, log viewer, detail view - Add PlaceholderResolver for [host], [date], [profile_name], etc. in backup_dir and new archive_name_format profile field - Fix download ERR_INVALID_RESPONSE by flushing output buffers before sending file headers - Table exclusions now have separate Data and Structure checkboxes (backward compatible with existing newline format) - Backup log files written alongside archive for posterity - Log viewer modal in backup records list and inline in detail view - Clickable record description links to detail view with checksum, file path, DB size, and full log - Dashboard health check shows actual resolved backup directory path - Fix update site link to use list view (avoids Joomla core bug) - Schema migration 01.01.09 for archive_name_format column Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/packages/com_mokobackup/forms/profile.xml | 9 ++ .../language/en-GB/com_mokobackup.ini | 13 +- .../language/en-US/com_mokobackup.ini | 9 +- .../com_mokobackup/sql/install.mysql.sql | 1 + .../sql/updates/mysql/01.01.09.sql | 3 + .../src/Controller/AjaxController.php | 52 ++++++++ .../src/Controller/BackupsController.php | 27 ++-- .../src/Engine/BackupEngine.php | 35 ++++- .../src/Engine/DatabaseDumper.php | 102 ++++++++++++--- .../src/Engine/PlaceholderResolver.php | 122 ++++++++++++++++++ .../src/Engine/SteppedBackupEngine.php | 36 ++++-- .../src/Field/DatabaseTablesField.php | 82 +++++++++--- .../src/Model/DashboardModel.php | 28 +++- .../com_mokobackup/tmpl/backup/default.php | 69 +++++++++- .../com_mokobackup/tmpl/backups/default.php | 67 +++++++++- src/script.php | 2 +- 16 files changed, 584 insertions(+), 73 deletions(-) create mode 100644 src/packages/com_mokobackup/sql/updates/mysql/01.01.09.sql create mode 100644 src/packages/com_mokobackup/src/Engine/PlaceholderResolver.php diff --git a/src/packages/com_mokobackup/forms/profile.xml b/src/packages/com_mokobackup/forms/profile.xml index 827f6cd..123e01c 100644 --- a/src/packages/com_mokobackup/forms/profile.xml +++ b/src/packages/com_mokobackup/forms/profile.xml @@ -70,6 +70,15 @@ default="administrator/components/com_mokobackup/backups" addfieldprefix="Joomla\Component\MokoBackup\Administrator\Field" /> + sendJson(['error' => true, 'message' => 'Invalid token']); + + return; + } + + $id = $this->input->getInt('id', 0); + + if (!$id) { + $this->sendJson(['error' => true, 'message' => 'Missing record ID']); + + return; + } + + $db = \Joomla\CMS\Factory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName(['absolute_path', 'log'])) + ->from($db->quoteName('#__mokobackup_records')) + ->where($db->quoteName('id') . ' = ' . $id); + $db->setQuery($query); + $record = $db->loadObject(); + + if (!$record) { + $this->sendJson(['error' => true, 'message' => 'Record not found']); + + return; + } + + // Try to load log from file alongside the archive + $logPath = preg_replace('/\.(zip|tar\.gz)$/i', '.log', $record->absolute_path); + $logContent = ''; + + if (is_file($logPath)) { + $logContent = file_get_contents($logPath); + } elseif (!empty($record->log)) { + // Fall back to database-stored log + $logContent = $record->log; + } + + $this->sendJson([ + 'error' => false, + 'log' => $logContent ?: '(no log available)', + 'source' => is_file($logPath) ? 'file' : 'database', + ]); + } + /** * Send a JSON response and close the application. */ diff --git a/src/packages/com_mokobackup/src/Controller/BackupsController.php b/src/packages/com_mokobackup/src/Controller/BackupsController.php index 0ad0490..c8a3f15 100644 --- a/src/packages/com_mokobackup/src/Controller/BackupsController.php +++ b/src/packages/com_mokobackup/src/Controller/BackupsController.php @@ -68,17 +68,28 @@ class BackupsController extends AdminController return; } - $app = $this->app; - $app->clearHeaders(); - $app->setHeader('Content-Type', 'application/zip'); - $app->setHeader('Content-Disposition', 'attachment; filename="' . basename($item->archivename) . '"'); - $app->setHeader('Content-Length', (string) filesize($item->absolute_path)); - $app->setHeader('Cache-Control', 'no-cache, must-revalidate'); - $app->sendHeaders(); + // Flush any output buffers to prevent HTML mixing with binary data + while (@ob_end_clean()) { + // clear all buffers + } + + $filename = basename($item->archivename); + $filesize = filesize($item->absolute_path); + + // Detect content type from file extension + $contentType = str_ends_with($filename, '.tar.gz') + ? 'application/gzip' + : 'application/zip'; + + header('Content-Type: ' . $contentType); + header('Content-Disposition: attachment; filename="' . $filename . '"'); + header('Content-Length: ' . $filesize); + header('Cache-Control: no-cache, must-revalidate'); + header('Pragma: no-cache'); readfile($item->absolute_path); - $app->close(); + $this->app->close(); } /** diff --git a/src/packages/com_mokobackup/src/Engine/BackupEngine.php b/src/packages/com_mokobackup/src/Engine/BackupEngine.php index 1b239fd..59254f4 100644 --- a/src/packages/com_mokobackup/src/Engine/BackupEngine.php +++ b/src/packages/com_mokobackup/src/Engine/BackupEngine.php @@ -60,21 +60,24 @@ class BackupEngine $excludeFiles = $this->parseNewlineList($profile->exclude_files ?? ''); $excludeTables = $this->parseNewlineList($profile->exclude_tables ?? ''); - // Determine backup directory - $this->backupDir = JPATH_ROOT . '/' . ($profile->backup_dir ?: 'administrator/components/com_mokobackup/backups'); + // Resolve placeholders in directory and filename + $resolver = new PlaceholderResolver($profile); + + $configuredDir = $profile->backup_dir ?: 'administrator/components/com_mokobackup/backups'; + $this->backupDir = $this->resolveBackupDir($resolver->resolve($configuredDir)); if (!is_dir($this->backupDir)) { mkdir($this->backupDir, 0755, true); } // Create backup record - $now = date('Y-m-d H:i:s'); - $tag = date('Ymd_His'); - $hostname = preg_replace('/[^a-zA-Z0-9._-]/', '', $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? php_uname('n')); + $now = date('Y-m-d H:i:s'); + $tag = $resolver->getTag(); $archiveFormat = $profile->archive_format ?? 'zip'; $archiver = $this->createArchiver($archiveFormat); $archiveExt = $archiver->getExtension(); - $archiveName = $hostname . '_' . $tag . '_profile' . $profileId . '.' . $archiveExt; + $nameFormat = $profile->archive_name_format ?? '[host]_[datetime]_profile[profile_id]'; + $archiveName = $resolver->resolve($nameFormat) . '.' . $archiveExt; if (empty($description)) { $description = $profile->title . ' — ' . $now; @@ -233,6 +236,11 @@ class BackupEngine } } + // Write log file alongside the archive + $logContent = implode("\n", $this->log); + $logPath = preg_replace('/\.(zip|tar\.gz)$/i', '.log', $archivePath); + @file_put_contents($logPath, $logContent); + // Final record update $update = (object) [ 'id' => $recordId, @@ -246,7 +254,7 @@ class BackupEngine 'remote_filename' => $remoteFilename, 'checksum' => $checksum, 'manifest' => !empty($manifest) ? json_encode($manifest) : '', - 'log' => implode("\n", $this->log), + 'log' => $logContent, ]; $db->updateObject('#__mokobackup_records', $update, 'id'); @@ -489,6 +497,19 @@ class BackupEngine } } + /** + * Resolve a backup directory path. Absolute paths are used as-is, + * relative paths are resolved from JPATH_ROOT. + */ + private function resolveBackupDir(string $dir): string + { + if ($dir !== '' && ($dir[0] === '/' || preg_match('#^[A-Za-z]:[/\\\\]#', $dir))) { + return rtrim($dir, '/\\'); + } + + return JPATH_ROOT . '/' . $dir; + } + private function log(string $message): void { $this->log[] = '[' . date('H:i:s') . '] ' . $message; diff --git a/src/packages/com_mokobackup/src/Engine/DatabaseDumper.php b/src/packages/com_mokobackup/src/Engine/DatabaseDumper.php index 3c81269..05661c5 100644 --- a/src/packages/com_mokobackup/src/Engine/DatabaseDumper.php +++ b/src/packages/com_mokobackup/src/Engine/DatabaseDumper.php @@ -16,15 +16,33 @@ use Joomla\CMS\Factory; class DatabaseDumper { - private array $excludeTables; + /** @var array Tables to exclude entirely (both structure and data) */ + private array $excludeBoth = []; + + /** @var array Tables to exclude data only (structure is kept) */ + private array $excludeDataOnly = []; + + /** @var array Tables to exclude structure only (data is kept — unusual) */ + private array $excludeStructureOnly = []; + private int $tablesCount = 0; /** - * @param array $excludeTables Table names to exclude (with #__ prefix) + * @param array $excludeTables Table names to exclude (with #__ prefix). + * Supports suffixes: :data-only, :structure-only. + * No suffix = exclude both (backward compatible). */ public function __construct(array $excludeTables = []) { - $this->excludeTables = $excludeTables; + foreach ($excludeTables as $entry) { + if (str_ends_with($entry, ':data-only')) { + $this->excludeDataOnly[] = substr($entry, 0, -10); + } elseif (str_ends_with($entry, ':structure-only')) { + $this->excludeStructureOnly[] = substr($entry, 0, -15); + } else { + $this->excludeBoth[] = $entry; + } + } } /** @@ -62,29 +80,49 @@ class DatabaseDumper // Check if excluded $abstractName = '#__' . substr($table, strlen($prefix)); - if ($this->isExcluded($abstractName, $table)) { + if ($this->isExcludedBoth($abstractName, $table)) { continue; } + $skipData = $this->isExcludedDataOnly($abstractName, $table); + $skipStructure = $this->isExcludedStructureOnly($abstractName, $table); + $this->tablesCount++; - // Get CREATE TABLE statement - $db->setQuery('SHOW CREATE TABLE ' . $db->quoteName($table)); - $createRow = $db->loadRow(); + $output[] = '-- --------------------------------------------------------'; + $output[] = '-- Table: ' . $table; - if (!$createRow || empty($createRow[1])) { - continue; + if ($skipData) { + $output[] = '-- (data excluded)'; + } + + if ($skipStructure) { + $output[] = '-- (structure excluded)'; } $output[] = '-- --------------------------------------------------------'; - $output[] = '-- Table: ' . $table; - $output[] = '-- --------------------------------------------------------'; - $output[] = ''; - $output[] = 'DROP TABLE IF EXISTS ' . $db->quoteName($table) . ';'; - $output[] = $createRow[1] . ';'; $output[] = ''; - // Dump data in chunks + // Get CREATE TABLE statement (unless structure is excluded) + if (!$skipStructure) { + $db->setQuery('SHOW CREATE TABLE ' . $db->quoteName($table)); + $createRow = $db->loadRow(); + + if (!$createRow || empty($createRow[1])) { + continue; + } + + $output[] = 'DROP TABLE IF EXISTS ' . $db->quoteName($table) . ';'; + $output[] = $createRow[1] . ';'; + $output[] = ''; + } + + // Dump data (unless data is excluded) + if ($skipData) { + $output[] = ''; + continue; + } + $db->setQuery('SELECT COUNT(*) FROM ' . $db->quoteName($table)); $rowCount = (int) $db->loadResult(); @@ -135,11 +173,39 @@ class DatabaseDumper } /** - * Check if a table is excluded. + * Check if a table is fully excluded (both data and structure). */ - private function isExcluded(string $abstractName, string $realName): bool + private function isExcludedBoth(string $abstractName, string $realName): bool { - foreach ($this->excludeTables as $pattern) { + foreach ($this->excludeBoth as $pattern) { + if ($pattern === $abstractName || $pattern === $realName) { + return true; + } + } + + return false; + } + + /** + * Check if a table's data is excluded (structure only). + */ + private function isExcludedDataOnly(string $abstractName, string $realName): bool + { + foreach ($this->excludeDataOnly as $pattern) { + if ($pattern === $abstractName || $pattern === $realName) { + return true; + } + } + + return false; + } + + /** + * Check if a table's structure is excluded (data only). + */ + private function isExcludedStructureOnly(string $abstractName, string $realName): bool + { + foreach ($this->excludeStructureOnly as $pattern) { if ($pattern === $abstractName || $pattern === $realName) { return true; } diff --git a/src/packages/com_mokobackup/src/Engine/PlaceholderResolver.php b/src/packages/com_mokobackup/src/Engine/PlaceholderResolver.php new file mode 100644 index 0000000..712b219 --- /dev/null +++ b/src/packages/com_mokobackup/src/Engine/PlaceholderResolver.php @@ -0,0 +1,122 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + * + * Resolves placeholders like [host], [date], [profile_name] in backup + * directory paths and archive filename formats. + */ + +namespace Joomla\Component\MokoBackup\Administrator\Engine; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; + +class PlaceholderResolver +{ + /** + * Supported placeholders and their descriptions (for documentation). + */ + public const PLACEHOLDERS = [ + '[host]' => 'Server hostname', + '[date]' => 'Date as Ymd (e.g. 20260604)', + '[time]' => 'Time as His (e.g. 143025)', + '[datetime]' => 'Date and time as Ymd_His', + '[year]' => 'Four-digit year', + '[month]' => 'Two-digit month', + '[day]' => 'Two-digit day', + '[hour]' => 'Two-digit hour (24h)', + '[minute]' => 'Two-digit minute', + '[second]' => 'Two-digit second', + '[profile_id]' => 'Backup profile ID', + '[profile_name]' => 'Profile title (sanitized)', + '[site_name]' => 'Joomla site name (sanitized)', + '[type]' => 'Backup type (full, database, files, differential)', + '[random]' => 'Random 6-character hex string', + ]; + + private array $replacements; + + /** + * @param object $profile The backup profile object + */ + public function __construct(object $profile) + { + $now = new \DateTimeImmutable('now'); + $hostname = preg_replace('/[^a-zA-Z0-9._-]/', '', $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? php_uname('n')); + + $siteName = ''; + + try { + $siteName = Factory::getApplication()->get('sitename', ''); + } catch (\Throwable $e) { + // Fallback: not critical + } + + $this->replacements = [ + '[host]' => $hostname, + '[date]' => $now->format('Ymd'), + '[time]' => $now->format('His'), + '[datetime]' => $now->format('Ymd_His'), + '[year]' => $now->format('Y'), + '[month]' => $now->format('m'), + '[day]' => $now->format('d'), + '[hour]' => $now->format('H'), + '[minute]' => $now->format('i'), + '[second]' => $now->format('s'), + '[profile_id]' => (string) ($profile->id ?? '0'), + '[profile_name]' => $this->sanitize($profile->title ?? 'default'), + '[site_name]' => $this->sanitize($siteName ?: 'joomla'), + '[type]' => $profile->backup_type ?? 'full', + '[random]' => bin2hex(random_bytes(3)), + ]; + } + + /** + * Replace all placeholders in a string. + * + * @param string $template String containing [placeholder] tokens + * + * @return string Resolved string + */ + public function resolve(string $template): string + { + return str_replace( + array_keys($this->replacements), + array_values($this->replacements), + $template + ); + } + + /** + * Get the raw hostname value (for backward compatibility). + */ + public function getHostname(): string + { + return $this->replacements['[host]']; + } + + /** + * Get the datetime tag value (for backward compatibility). + */ + public function getTag(): string + { + return $this->replacements['[datetime]']; + } + + /** + * Sanitize a string for use in filenames/paths. + * Keeps alphanumerics, dots, hyphens, underscores. Replaces spaces with hyphens. + */ + private function sanitize(string $value): string + { + $value = str_replace(' ', '-', trim($value)); + + return preg_replace('/[^a-zA-Z0-9._-]/', '', $value); + } +} diff --git a/src/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php b/src/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php index 8a550d4..7a7559a 100644 --- a/src/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php +++ b/src/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php @@ -60,17 +60,18 @@ class SteppedBackupEngine $session->includeMokoRestore = (bool) ($profile->include_mokorestore ?? false); $session->remoteKeepLocal = (bool) ($profile->remote_keep_local ?? true); - // Build archive path - $backupDir = JPATH_ROOT . '/' . $session->backupDir; + // Resolve placeholders in directory and filename + $resolver = new PlaceholderResolver($profile); + $backupDir = $this->resolveBackupDir($resolver->resolve($session->backupDir)); if (!is_dir($backupDir)) { mkdir($backupDir, 0755, true); } - $now = date('Y-m-d H:i:s'); - $tag = date('Ymd_His'); - $hostname = preg_replace('/[^a-zA-Z0-9._-]/', '', $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? php_uname('n')); - $archiveName = $hostname . '_' . $tag . '_profile' . $profileId . '.zip'; + $now = date('Y-m-d H:i:s'); + $tag = $resolver->getTag(); + $nameFormat = $profile->archive_name_format ?? '[host]_[datetime]_profile[profile_id]'; + $archiveName = $resolver->resolve($nameFormat) . '.zip'; $session->archivePath = $backupDir . '/' . $archiveName; $session->archiveName = $archiveName; @@ -408,12 +409,18 @@ class SteppedBackupEngine */ private function completeRecord(SteppedSession $session): void { - $db = Factory::getDbo(); + $db = Factory::getDbo(); + $logContent = implode("\n", $session->log); + + // Write log file alongside the archive + $logPath = preg_replace('/\.(zip|tar\.gz)$/i', '.log', $session->archivePath); + @file_put_contents($logPath, $logContent); + $update = (object) [ 'id' => $session->recordId, 'status' => 'complete', 'backupend' => date('Y-m-d H:i:s'), - 'log' => implode("\n", $session->log), + 'log' => $logContent, ]; $db->updateObject('#__mokobackup_records', $update, 'id'); @@ -536,6 +543,19 @@ class SteppedBackupEngine return $tables; } + /** + * Resolve a backup directory path. Absolute paths are used as-is, + * relative paths are resolved from JPATH_ROOT. + */ + private function resolveBackupDir(string $dir): string + { + if ($dir !== '' && ($dir[0] === '/' || preg_match('#^[A-Za-z]:[/\\\\]#', $dir))) { + return rtrim($dir, '/\\'); + } + + return JPATH_ROOT . '/' . $dir; + } + private function parseNewlineList(string $text): array { if (empty($text)) { diff --git a/src/packages/com_mokobackup/src/Field/DatabaseTablesField.php b/src/packages/com_mokobackup/src/Field/DatabaseTablesField.php index e563017..937202d 100644 --- a/src/packages/com_mokobackup/src/Field/DatabaseTablesField.php +++ b/src/packages/com_mokobackup/src/Field/DatabaseTablesField.php @@ -26,17 +26,29 @@ class DatabaseTablesField extends FormField $tables = $db->getTableList(); $prefix = $db->getPrefix(); - // Parse current exclusions (newline-separated) - $excluded = []; + // Parse current exclusions (newline-separated, with optional :data-only suffix) + $excludeData = []; + $excludeStructure = []; if (!empty($this->value)) { - $excluded = array_filter(array_map('trim', explode("\n", str_replace("\r", '', $this->value)))); - } + $lines = array_filter(array_map('trim', explode("\n", str_replace("\r", '', $this->value)))); - // Normalize: replace literal #__ with actual prefix for comparison - $excludedNormalized = array_map(function ($t) use ($prefix) { - return str_replace('#__', $prefix, $t); - }, $excluded); + foreach ($lines as $line) { + // Normalize table name to real prefix for comparison + if (str_ends_with($line, ':data-only')) { + $tableName = str_replace('#__', $prefix, substr($line, 0, -10)); + $excludeData[$tableName] = true; + } elseif (str_ends_with($line, ':structure-only')) { + $tableName = str_replace('#__', $prefix, substr($line, 0, -15)); + $excludeStructure[$tableName] = true; + } else { + // No suffix = exclude both (backward compatible) + $tableName = str_replace('#__', $prefix, $line); + $excludeData[$tableName] = true; + $excludeStructure[$tableName] = true; + } + } + } $id = htmlspecialchars($this->id, ENT_QUOTES, 'UTF-8'); $name = htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8'); @@ -47,12 +59,16 @@ class DatabaseTablesField extends FormField $html .= '
'; $html .= ''; $html .= ''; - $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; $html .= ''; $html .= ''; foreach ($tables as $table) { - $isExcluded = \in_array($table, $excludedNormalized, true); + $dataChecked = isset($excludeData[$table]) ? ' checked' : ''; + $structureChecked = isset($excludeStructure[$table]) ? ' checked' : ''; // Convert to #__ notation for storage $storeValue = $table; @@ -63,10 +79,12 @@ class DatabaseTablesField extends FormField $safeValue = htmlspecialchars($storeValue, ENT_QUOTES, 'UTF-8'); $safeTable = htmlspecialchars($table, ENT_QUOTES, 'UTF-8'); - $checked = $isExcluded ? ' checked' : ''; $html .= ''; - $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; $html .= ''; $html .= ''; } @@ -78,20 +96,44 @@ class DatabaseTablesField extends FormField diff --git a/src/packages/com_mokobackup/tmpl/backups/default.php b/src/packages/com_mokobackup/tmpl/backups/default.php index 590f519..cba340d 100644 --- a/src/packages/com_mokobackup/tmpl/backups/default.php +++ b/src/packages/com_mokobackup/tmpl/backups/default.php @@ -99,7 +99,12 @@ $listDirn = $this->escape($this->state->get('list.direction')); id); ?> -
' . Text::_('COM_MOKOBACKUP_FIELD_EXCLUDE_DATA') . '' . Text::_('COM_MOKOBACKUP_FIELD_EXCLUDE_STRUCTURE') . '' . Text::_('COM_MOKOBACKUP_FIELD_TABLE_NAME') . '
' . $safeTable . '
- escape($item->description); ?> + + escape($item->description); ?> + + checksum)) : ?> +
: checksum, 0, 16); ?>... +
escape($item->profile_title ?? 'Profile #' . $item->profile_id); ?> @@ -130,13 +135,18 @@ $listDirn = $this->escape($this->state->get('list.direction')); backupstart, Text::_('DATE_FORMAT_LC4')); ?> + status === 'complete' && $item->filesexist) : ?> + id; ?> @@ -274,5 +284,58 @@ $listDirn = $this->escape($this->state->get('list.direction')); // Expose for toolbar button window.mokobackupStart = startSteppedBackup; + + // View Log modal handler + document.addEventListener('click', function(e) { + var btn = e.target.closest('.mb-view-log'); + if (!btn) return; + e.preventDefault(); + var recordId = btn.getAttribute('data-id'); + var modal = document.getElementById('mb-log-modal'); + var body = document.getElementById('mb-log-body'); + body.textContent = 'Loading...'; + modal.style.display = 'block'; + + var form = new URLSearchParams(); + form.append('task', 'ajax.viewLog'); + form.append('id', recordId); + form.append(TOKEN_NAME, '1'); + + fetch(AJAX_URL, { + method: 'POST', + body: form, + headers: { 'X-Requested-With': 'XMLHttpRequest' } + }) + .then(function(r) { return r.json(); }) + .then(function(data) { + if (data.error) { + body.textContent = data.message || 'Error loading log'; + } else { + body.textContent = data.log; + } + }) + .catch(function(err) { + body.textContent = 'Error: ' + err.message; + }); + }); + + document.addEventListener('click', function(e) { + if (e.target.id === 'mb-log-modal' || e.target.classList.contains('mb-log-close')) { + document.getElementById('mb-log-modal').style.display = 'none'; + } + }); })(); + + + diff --git a/src/script.php b/src/script.php index d5cc000..d970bcd 100644 --- a/src/script.php +++ b/src/script.php @@ -190,7 +190,7 @@ class Pkg_MokoBackupInstallerScript if ($updateSiteId > 0) { $editUrl = Route::_( - 'index.php?option=com_installer&view=updatesites&task=updatesite.edit&id=' . $updateSiteId + 'index.php?option=com_installer&view=updatesites&filter[search]=mokobackup' ); Factory::getApplication()->enqueueMessage( -- 2.52.0 From b1632e790e4f4bb1943c1522e81d9365a758a0ce Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 06:07:53 -0500 Subject: [PATCH 44/81] feat: make dashboard status tiles clickable links to their sections Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../com_mokobackup/tmpl/dashboard/default.php | 130 ++++++++++-------- 1 file changed, 69 insertions(+), 61 deletions(-) diff --git a/src/packages/com_mokobackup/tmpl/dashboard/default.php b/src/packages/com_mokobackup/tmpl/dashboard/default.php index 5f33f87..92ae6d6 100644 --- a/src/packages/com_mokobackup/tmpl/dashboard/default.php +++ b/src/packages/com_mokobackup/tmpl/dashboard/default.php @@ -32,70 +32,78 @@ $ajaxUrl = Route::_('index.php?option=com_mokobackup&format=json', false);
- + -- 2.52.0 From df59fd730331c5a15d279df1d9aaf02fd8584a51 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 07:13:26 -0500 Subject: [PATCH 45/81] fix: dashboard tiles use onclick navigation, last backup links to record detail Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../com_mokobackup/tmpl/dashboard/default.php | 138 +++++++++--------- 1 file changed, 70 insertions(+), 68 deletions(-) diff --git a/src/packages/com_mokobackup/tmpl/dashboard/default.php b/src/packages/com_mokobackup/tmpl/dashboard/default.php index 92ae6d6..86ded25 100644 --- a/src/packages/com_mokobackup/tmpl/dashboard/default.php +++ b/src/packages/com_mokobackup/tmpl/dashboard/default.php @@ -34,79 +34,81 @@ $ajaxUrl = Route::_('index.php?option=com_mokobackup&format=json', false);
- - - - - -
- - + +
+
+
+ +
+ nextScheduled) : ?> +

+ nextScheduled->next_execution, Text::_('DATE_FORMAT_LC4')); ?> +

+ escape($this->nextScheduled->title); ?> + +

+ +
+
+
+ +
+
+
+ +
+

stats->total_count; ?>

+
+
+
+ +
+
+
+ +
+

+ stats->total_size); ?> +

+ stats->fail_count_7d > 0) : ?> + + stats->fail_count_7d); ?> + + +
+
+ + +
-- 2.52.0 From 2755ef709f02df141df9d3de83d4f2519cd06c04 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 07:24:33 -0500 Subject: [PATCH 46/81] feat: interactive directory tree browser for exclude filters Replace plain text ExcludeList with DirectoryFilter field that provides a browsable server directory tree with checkboxes, removable pills, and manual path entry. Backward compatible storage format. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/packages/com_mokobackup/forms/profile.xml | 2 +- .../language/en-GB/com_mokobackup.ini | 5 +- .../src/Field/DirectoryFilterField.php | 259 ++++++++++++++++++ 3 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 src/packages/com_mokobackup/src/Field/DirectoryFilterField.php diff --git a/src/packages/com_mokobackup/forms/profile.xml b/src/packages/com_mokobackup/forms/profile.xml index 123e01c..5c3fd2d 100644 --- a/src/packages/com_mokobackup/forms/profile.xml +++ b/src/packages/com_mokobackup/forms/profile.xml @@ -124,7 +124,7 @@
+ * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + * + * Interactive directory tree field with checkboxes for exclude/include filtering. + * Loads the directory tree from the server via AJAX (browseDir endpoint). + */ + +namespace Joomla\Component\MokoBackup\Administrator\Field; + +defined('_JEXEC') or die; + +use Joomla\CMS\Form\FormField; +use Joomla\CMS\Language\Text; + +class DirectoryFilterField extends FormField +{ + protected $type = 'DirectoryFilter'; + + protected function getInput(): string + { + $id = htmlspecialchars($this->id, ENT_QUOTES, 'UTF-8'); + $name = htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8'); + $mode = htmlspecialchars((string) ($this->element['mode'] ?? 'exclude'), ENT_QUOTES, 'UTF-8'); + + // Parse current values (newline-separated) + $items = []; + + if (!empty($this->value)) { + $items = array_values(array_filter(array_map('trim', explode("\n", str_replace("\r", '', $this->value))))); + } + + $itemsJson = json_encode($items); + $jRoot = json_encode(JPATH_ROOT); + + $labelExclude = Text::_('COM_MOKOBACKUP_FILTER_EXCLUDED'); + $labelInclude = Text::_('COM_MOKOBACKUP_FILTER_INCLUDED'); + $labelManual = Text::_('COM_MOKOBACKUP_FILTER_ADD_MANUAL'); + $addLabel = Text::_('JGLOBAL_FIELD_ADD'); + $placeholder = htmlspecialchars((string) ($this->element['hint'] ?? 'path/to/directory'), ENT_QUOTES, 'UTF-8'); + + return << + + + +
+ + +
+ + +
+ + +
+
+ + +
+
+
+
+ + + + +HTML; + } +} -- 2.52.0 From fdd004b345a54db0e097ed863327b86cbebfcc3c Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 6 Jun 2026 12:32:27 +0000 Subject: [PATCH 47/81] chore: sync .mokogitea/workflows/pre-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/pre-release.yml | 161 +++++++++++++++------------ 1 file changed, 89 insertions(+), 72 deletions(-) diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index ff818ba..1a9eeef 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -7,7 +7,7 @@ # INGROUP: moko-platform.Release # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /templates/workflows/universal/pre-release.yml.template -# VERSION: 09.23.00 +# VERSION: 05.01.00 # BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch name: "Universal: Pre-Release" @@ -17,6 +17,10 @@ on: types: [closed] branches: - dev + pull_request_target: + types: [synchronize, opened, reopened] + branches: + - main workflow_dispatch: inputs: stability: @@ -43,7 +47,8 @@ jobs: runs-on: release if: >- github.event_name == 'workflow_dispatch' || - (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') + (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') || + (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main') steps: - name: Checkout @@ -51,22 +56,29 @@ jobs: with: fetch-depth: 0 token: ${{ secrets.MOKOGITEA_TOKEN }} + ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }} - name: Setup moko-platform tools env: MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting run: | - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) + if [ -f “/opt/moko-platform/cli/version_bump.php” ] && [ -f “/opt/moko-platform/vendor/autoload.php” ]; then + echo “Using pre-installed /opt/moko-platform” + echo “MOKO_CLI=/opt/moko-platform/cli” >> “$GITHUB_ENV” + else + echo “Falling back to fresh clone” + if ! command -v composer &> /dev/null; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + git clone --depth 1 --branch main --quiet \ + “https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git” \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet + echo “MOKO_CLI=/tmp/moko-platform-api/cli” >> “$GITHUB_ENV” fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet - echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV" - name: Detect platform id: platform @@ -76,24 +88,40 @@ jobs: - name: Resolve metadata and bump version id: meta run: | - STABILITY="${{ inputs.stability || 'development' }}" + # Auto-detect stability: RC for PRs targeting main, else use input or default to development + if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then + STABILITY="release-candidate" + else + STABILITY="${{ inputs.stability || 'development' }}" + fi case "$STABILITY" in - development) TAG="development" ;; - alpha) TAG="alpha" ;; - beta) TAG="beta" ;; - release-candidate) TAG="release-candidate" ;; + development) SUFFIX="-dev"; TAG="development" ;; + alpha) SUFFIX="-alpha"; TAG="alpha" ;; + beta) SUFFIX="-beta"; TAG="beta" ;; + release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; esac - # Set stability suffix, bump preserves it, fix consistency + # Bump version via CLI: patch for dev/alpha/beta, minor for RC + case "$STABILITY" in + release-candidate) BUMP="minor" ;; + *) BUMP="patch" ;; + esac + + php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true + + # Set stability suffix and verify consistency + VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") + VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') + php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo '00.00.01')" \ - --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true + --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - # Read final version (includes suffix, e.g. 01.02.15-dev) - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) - [ -z "$VERSION" ] && VERSION="00.00.01" + # Append suffix for output + if [ -n "$SUFFIX" ]; then + VERSION="${VERSION}${SUFFIX}" + fi # Commit version bump git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" @@ -118,11 +146,12 @@ jobs: echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" + echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT" echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" - echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION} ===" + echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" - name: Create release id: release @@ -135,6 +164,41 @@ jobs: --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --repo "${GITEA_REPO}" --branch dev --prerelease + - name: Update release notes from CHANGELOG.md + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) + if [ -f "CHANGELOG.md" ]; then + NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) + [ -z "$NOTES" ] && NOTES="Release ${VERSION}" + else + NOTES="Release ${VERSION}" + fi + + # Update release body via API + RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + + if [ -n "$RELEASE_ID" ]; then + python3 -c " + import json, urllib.request + body = open('/dev/stdin').read() + payload = json.dumps({'body': body}).encode() + req = urllib.request.Request( + '${API_BASE}/releases/${RELEASE_ID}', + data=payload, method='PATCH', + headers={ + 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', + 'Content-Type': 'application/json' + }) + urllib.request.urlopen(req) + " <<< "$NOTES" + echo "Release notes updated from CHANGELOG.md" + fi + - name: Build package and upload id: package run: | @@ -146,55 +210,8 @@ jobs: --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --repo "${GITEA_REPO}" --output /tmp || true - - name: Update updates.xml - if: steps.platform.outputs.platform == 'joomla' - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - - if [ ! -f "updates.xml" ]; then - echo "No updates.xml -- skipping" - exit 0 - fi - - SHA_FLAG="" - [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" - - php ${MOKO_CLI}/updates_xml_build.php \ - --path . --version "${VERSION}" --stability "${STABILITY}" \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - ${SHA_FLAG} - - # Commit and push - if ! git diff --quiet updates.xml 2>/dev/null; then - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git add updates.xml - git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" - git push origin HEAD 2>&1 || echo "WARNING: push failed" - fi - - - name: "Sync updates.xml to all branches" - if: steps.platform.outputs.platform == 'joomla' - run: | - CURRENT_BRANCH="${{ github.ref_name }}" - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - - for BRANCH in main dev; do - [ "$BRANCH" = "$CURRENT_BRANCH" ] && continue - echo "Syncing updates.xml -> ${BRANCH}" - git fetch origin "${BRANCH}" 2>/dev/null || continue - git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue - git checkout "${CURRENT_BRANCH}" -- updates.xml - if ! git diff --quiet updates.xml 2>/dev/null; then - git add updates.xml - git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]" - git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed" - fi - git checkout "${CURRENT_BRANCH}" 2>/dev/null - done + # updates.xml is generated dynamically by MokoGitea license server + # No need to build, commit, or sync updates.xml from workflows - name: "Delete lesser pre-release channels (cascade)" continue-on-error: true -- 2.52.0 From 28fcc72ced2fdba3fc693f6599a63ea3fac13a7e Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 07:34:40 -0500 Subject: [PATCH 48/81] fix: consolidate schema migrations to version within extension range Migrations 01.01.08 and 01.01.09 were never applied on upgrade because their version numbers exceeded the extension version (01.01.07-dev). Joomla skips migrations with version > extension version. Consolidated into 01.01.02.sql which falls between 01.01.01 and 01.01.07-dev, ensuring existing installs receive the ALTER TABLE statements for notify_user_groups and archive_name_format. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../sql/updates/mysql/{01.01.08.sql => 01.01.02.sql} | 7 ++++++- src/packages/com_mokobackup/sql/updates/mysql/01.01.09.sql | 3 --- 2 files changed, 6 insertions(+), 4 deletions(-) rename src/packages/com_mokobackup/sql/updates/mysql/{01.01.08.sql => 01.01.02.sql} (53%) delete mode 100644 src/packages/com_mokobackup/sql/updates/mysql/01.01.09.sql diff --git a/src/packages/com_mokobackup/sql/updates/mysql/01.01.08.sql b/src/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql similarity index 53% rename from src/packages/com_mokobackup/sql/updates/mysql/01.01.08.sql rename to src/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql index 724a3f3..ddc43d7 100644 --- a/src/packages/com_mokobackup/sql/updates/mysql/01.01.08.sql +++ b/src/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql @@ -1,7 +1,12 @@ --- MokoJoomBackup 01.01.08 +-- MokoJoomBackup 01.01.02 +-- Consolidated schema updates: NULL defaults, notifications, archive name format + -- Fix: allow NULL defaults for manifest and log columns ALTER TABLE `#__mokobackup_records` MODIFY `manifest` LONGTEXT DEFAULT NULL; ALTER TABLE `#__mokobackup_records` MODIFY `log` MEDIUMTEXT DEFAULT NULL; -- Add user group notifications column to profiles ALTER TABLE `#__mokobackup_profiles` ADD COLUMN `notify_user_groups` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Comma-separated Joomla user group IDs' AFTER `notify_email`; + +-- Add archive_name_format column with placeholder support +ALTER TABLE `#__mokobackup_profiles` ADD COLUMN `archive_name_format` VARCHAR(512) NOT NULL DEFAULT '[host]_[datetime]_profile[profile_id]' COMMENT 'Filename format with placeholders' AFTER `backup_dir`; diff --git a/src/packages/com_mokobackup/sql/updates/mysql/01.01.09.sql b/src/packages/com_mokobackup/sql/updates/mysql/01.01.09.sql deleted file mode 100644 index 32222f4..0000000 --- a/src/packages/com_mokobackup/sql/updates/mysql/01.01.09.sql +++ /dev/null @@ -1,3 +0,0 @@ --- MokoJoomBackup 01.01.09 --- Add archive_name_format column with placeholder support -ALTER TABLE `#__mokobackup_profiles` ADD COLUMN `archive_name_format` VARCHAR(512) NOT NULL DEFAULT '[host]_[datetime]_profile[profile_id]' COMMENT 'Filename format with placeholders' AFTER `backup_dir`; -- 2.52.0 From a13f7ca6a6d8a8a7bd3e46a321bdf0f0124d47a4 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 08:08:33 -0500 Subject: [PATCH 49/81] chore: rename src/ to source/ per MokoStandards convention Update all references in Makefile, manifest.xml, .gitignore, and CI workflows (ci-joomla, pr-check, repo-health) to use source/ as the primary directory with src/ as a fallback for compatibility. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 2 +- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/ci-joomla.yml | 12 ++++++------ .mokogitea/workflows/pr-check.yml | 9 +++++---- .mokogitea/workflows/repo-health.yml | 10 ++++++---- Makefile | 2 +- {src => source}/index.html | 0 {src => source}/language/en-GB/index.html | 0 .../language/en-GB/pkg_mokobackup.sys.ini | 0 {src => source}/language/en-US/index.html | 0 .../language/en-US/pkg_mokobackup.sys.ini | 0 {src => source}/language/index.html | 0 .../packages/com_mokobackup/api/index.html | 0 .../api/src/Controller/BackupsController.php | 0 .../com_mokobackup/api/src/Controller/index.html | 0 .../api/src/View/Backups/JsonapiView.php | 0 .../com_mokobackup/api/src/View/Backups/index.html | 0 .../packages/com_mokobackup/api/src/View/index.html | 0 .../packages/com_mokobackup/api/src/index.html | 0 .../packages/com_mokobackup/cli/index.html | 0 .../packages/com_mokobackup/cli/mokobackup.php | 0 {src => source}/packages/com_mokobackup/config.xml | 0 .../packages/com_mokobackup/forms/backup.xml | 0 .../packages/com_mokobackup/forms/filter_backups.xml | 0 .../com_mokobackup/forms/filter_profiles.xml | 0 .../packages/com_mokobackup/forms/index.html | 0 .../packages/com_mokobackup/forms/profile.xml | 0 {src => source}/packages/com_mokobackup/index.html | 0 .../com_mokobackup/language/en-GB/com_mokobackup.ini | 0 .../language/en-GB/com_mokobackup.sys.ini | 0 .../com_mokobackup/language/en-GB/index.html | 0 .../com_mokobackup/language/en-US/com_mokobackup.ini | 0 .../language/en-US/com_mokobackup.sys.ini | 0 .../com_mokobackup/language/en-US/index.html | 0 .../packages/com_mokobackup/language/index.html | 0 .../packages/com_mokobackup/mokobackup.xml | 0 .../packages/com_mokobackup/services/index.html | 0 .../packages/com_mokobackup/services/provider.php | 0 .../packages/com_mokobackup/sql/index.html | 0 .../packages/com_mokobackup/sql/install.mysql.sql | 0 .../packages/com_mokobackup/sql/mysql/index.html | 0 .../packages/com_mokobackup/sql/uninstall.mysql.sql | 0 .../packages/com_mokobackup/sql/updates/index.html | 0 .../com_mokobackup/sql/updates/mysql/01.00.00.sql | 0 .../com_mokobackup/sql/updates/mysql/01.01.01.sql | 0 .../com_mokobackup/sql/updates/mysql/01.01.02.sql | 0 .../com_mokobackup/sql/updates/mysql/index.html | 0 .../com_mokobackup/src/Controller/AjaxController.php | 0 .../src/Controller/BackupController.php | 0 .../src/Controller/BackupsController.php | 0 .../src/Controller/DisplayController.php | 0 .../src/Controller/ProfileController.php | 0 .../src/Controller/ProfilesController.php | 0 .../com_mokobackup/src/Controller/index.html | 0 .../com_mokobackup/src/Engine/AkeebaImporter.php | 0 .../com_mokobackup/src/Engine/ArchiverInterface.php | 0 .../com_mokobackup/src/Engine/BackupEngine.php | 0 .../com_mokobackup/src/Engine/DatabaseDumper.php | 0 .../com_mokobackup/src/Engine/DatabaseImporter.php | 0 .../src/Engine/DifferentialScanner.php | 0 .../com_mokobackup/src/Engine/FileRestorer.php | 0 .../com_mokobackup/src/Engine/FileScanner.php | 0 .../com_mokobackup/src/Engine/FtpUploader.php | 0 .../src/Engine/GoogleDriveUploader.php | 0 .../com_mokobackup/src/Engine/JpaUnarchiver.php | 0 .../com_mokobackup/src/Engine/MokoRestore.php | 0 .../com_mokobackup/src/Engine/NotificationSender.php | 0 .../src/Engine/PlaceholderResolver.php | 0 .../src/Engine/RemoteUploaderInterface.php | 0 .../com_mokobackup/src/Engine/RestoreEngine.php | 0 .../com_mokobackup/src/Engine/S3Uploader.php | 0 .../src/Engine/SteppedBackupEngine.php | 0 .../com_mokobackup/src/Engine/SteppedSession.php | 0 .../com_mokobackup/src/Engine/TarGzArchiver.php | 0 .../com_mokobackup/src/Engine/ZipArchiver.php | 0 .../packages/com_mokobackup/src/Engine/index.html | 0 .../src/Extension/MokoBackupComponent.php | 0 .../packages/com_mokobackup/src/Extension/index.html | 0 .../com_mokobackup/src/Field/DatabaseTablesField.php | 0 .../src/Field/DirectoryFilterField.php | 0 .../com_mokobackup/src/Field/ExcludeListField.php | 0 .../com_mokobackup/src/Field/FolderPickerField.php | 0 .../com_mokobackup/src/Model/BackupModel.php | 0 .../com_mokobackup/src/Model/BackupsModel.php | 0 .../com_mokobackup/src/Model/DashboardModel.php | 0 .../com_mokobackup/src/Model/ProfileModel.php | 0 .../com_mokobackup/src/Model/ProfilesModel.php | 0 .../packages/com_mokobackup/src/Model/index.html | 0 .../com_mokobackup/src/Table/BackupTable.php | 0 .../com_mokobackup/src/Table/ProfileTable.php | 0 .../packages/com_mokobackup/src/Table/index.html | 0 .../com_mokobackup/src/View/Backup/HtmlView.php | 0 .../com_mokobackup/src/View/Backup/index.html | 0 .../com_mokobackup/src/View/Backups/HtmlView.php | 0 .../com_mokobackup/src/View/Backups/index.html | 0 .../com_mokobackup/src/View/Dashboard/HtmlView.php | 0 .../com_mokobackup/src/View/Profile/HtmlView.php | 0 .../com_mokobackup/src/View/Profile/index.html | 0 .../com_mokobackup/src/View/Profiles/HtmlView.php | 0 .../com_mokobackup/src/View/Profiles/index.html | 0 .../packages/com_mokobackup/src/View/index.html | 0 .../packages/com_mokobackup/src/index.html | 0 .../packages/com_mokobackup/tmpl/backup/default.php | 0 .../packages/com_mokobackup/tmpl/backup/index.html | 0 .../packages/com_mokobackup/tmpl/backups/default.php | 0 .../packages/com_mokobackup/tmpl/backups/index.html | 0 .../com_mokobackup/tmpl/dashboard/default.php | 0 .../packages/com_mokobackup/tmpl/index.html | 0 .../packages/com_mokobackup/tmpl/profile/edit.php | 0 .../packages/com_mokobackup/tmpl/profile/index.html | 0 .../com_mokobackup/tmpl/profiles/default.php | 0 .../packages/com_mokobackup/tmpl/profiles/index.html | 0 {src => source}/packages/index.html | 0 .../language/en-GB/plg_actionlog_mokobackup.ini | 0 .../language/en-GB/plg_actionlog_mokobackup.sys.ini | 0 .../language/en-US/plg_actionlog_mokobackup.ini | 0 .../language/en-US/plg_actionlog_mokobackup.sys.ini | 0 .../packages/plg_actionlog_mokobackup/mokobackup.php | 0 .../packages/plg_actionlog_mokobackup/mokobackup.xml | 0 .../plg_actionlog_mokobackup/services/provider.php | 0 .../src/Extension/MokoBackupActionlog.php | 0 .../language/en-GB/plg_console_mokobackup.ini | 0 .../language/en-GB/plg_console_mokobackup.sys.ini | 0 .../language/en-US/plg_console_mokobackup.ini | 0 .../language/en-US/plg_console_mokobackup.sys.ini | 0 .../packages/plg_console_mokobackup/mokobackup.php | 0 .../packages/plg_console_mokobackup/mokobackup.xml | 0 .../plg_console_mokobackup/services/provider.php | 0 .../src/Command/CleanupCommand.php | 0 .../src/Command/ListCommand.php | 0 .../src/Command/ProfilesCommand.php | 0 .../src/Command/RestoreCommand.php | 0 .../src/Command/RunCommand.php | 0 .../src/Extension/MokoBackupConsole.php | 0 .../language/en-GB/plg_content_mokobackup.ini | 0 .../language/en-GB/plg_content_mokobackup.sys.ini | 0 .../language/en-US/plg_content_mokobackup.ini | 0 .../language/en-US/plg_content_mokobackup.sys.ini | 0 .../packages/plg_content_mokobackup/mokobackup.php | 0 .../packages/plg_content_mokobackup/mokobackup.xml | 0 .../plg_content_mokobackup/services/provider.php | 0 .../src/Extension/MokoBackupContent.php | 0 .../packages/plg_quickicon_mokobackup/index.html | 0 .../language/en-GB/index.html | 0 .../language/en-GB/plg_quickicon_mokobackup.ini | 0 .../language/en-GB/plg_quickicon_mokobackup.sys.ini | 0 .../language/en-US/index.html | 0 .../language/en-US/plg_quickicon_mokobackup.ini | 0 .../language/en-US/plg_quickicon_mokobackup.sys.ini | 0 .../plg_quickicon_mokobackup/language/index.html | 0 .../packages/plg_quickicon_mokobackup/mokobackup.php | 0 .../packages/plg_quickicon_mokobackup/mokobackup.xml | 0 .../plg_quickicon_mokobackup/services/index.html | 0 .../plg_quickicon_mokobackup/services/provider.php | 0 .../src/Extension/MokoBackupQuickicon.php | 0 .../src/Extension/index.html | 0 .../packages/plg_quickicon_mokobackup/src/index.html | 0 .../packages/plg_system_mokobackup/index.html | 0 .../plg_system_mokobackup/language/en-GB/index.html | 0 .../language/en-GB/plg_system_mokobackup.ini | 0 .../language/en-GB/plg_system_mokobackup.sys.ini | 0 .../plg_system_mokobackup/language/en-US/index.html | 0 .../language/en-US/plg_system_mokobackup.ini | 0 .../language/en-US/plg_system_mokobackup.sys.ini | 0 .../plg_system_mokobackup/language/index.html | 0 .../packages/plg_system_mokobackup/mokobackup.php | 0 .../packages/plg_system_mokobackup/mokobackup.xml | 0 .../plg_system_mokobackup/services/index.html | 0 .../plg_system_mokobackup/services/provider.php | 0 .../src/Extension/MokoBackup.php | 0 .../plg_system_mokobackup/src/Extension/index.html | 0 .../packages/plg_system_mokobackup/src/index.html | 0 .../packages/plg_task_mokobackup/forms/index.html | 0 .../plg_task_mokobackup/forms/run_profile.xml | 0 .../packages/plg_task_mokobackup/index.html | 0 .../plg_task_mokobackup/language/en-GB/index.html | 0 .../language/en-GB/plg_task_mokobackup.ini | 0 .../language/en-GB/plg_task_mokobackup.sys.ini | 0 .../plg_task_mokobackup/language/en-US/index.html | 0 .../language/en-US/plg_task_mokobackup.ini | 0 .../language/en-US/plg_task_mokobackup.sys.ini | 0 .../packages/plg_task_mokobackup/language/index.html | 0 .../packages/plg_task_mokobackup/mokobackup.php | 0 .../packages/plg_task_mokobackup/mokobackup.xml | 0 .../packages/plg_task_mokobackup/services/index.html | 0 .../plg_task_mokobackup/services/provider.php | 0 .../src/Extension/MokoBackupTask.php | 0 .../plg_task_mokobackup/src/Extension/index.html | 0 .../packages/plg_task_mokobackup/src/index.html | 0 .../packages/plg_webservices_mokobackup/index.html | 0 .../language/en-GB/index.html | 0 .../language/en-GB/plg_webservices_mokobackup.ini | 0 .../en-GB/plg_webservices_mokobackup.sys.ini | 0 .../language/en-US/index.html | 0 .../language/en-US/plg_webservices_mokobackup.ini | 0 .../en-US/plg_webservices_mokobackup.sys.ini | 0 .../plg_webservices_mokobackup/language/index.html | 0 .../plg_webservices_mokobackup/mokobackup.php | 0 .../plg_webservices_mokobackup/mokobackup.xml | 0 .../plg_webservices_mokobackup/services/index.html | 0 .../plg_webservices_mokobackup/services/provider.php | 0 .../src/Extension/MokoBackupWebServices.php | 0 .../src/Extension/index.html | 0 .../plg_webservices_mokobackup/src/index.html | 0 {src => source}/pkg_mokobackup.xml | 0 {src => source}/script.php | 0 206 files changed, 20 insertions(+), 17 deletions(-) rename {src => source}/index.html (100%) rename {src => source}/language/en-GB/index.html (100%) rename {src => source}/language/en-GB/pkg_mokobackup.sys.ini (100%) rename {src => source}/language/en-US/index.html (100%) rename {src => source}/language/en-US/pkg_mokobackup.sys.ini (100%) rename {src => source}/language/index.html (100%) rename {src => source}/packages/com_mokobackup/api/index.html (100%) rename {src => source}/packages/com_mokobackup/api/src/Controller/BackupsController.php (100%) rename {src => source}/packages/com_mokobackup/api/src/Controller/index.html (100%) rename {src => source}/packages/com_mokobackup/api/src/View/Backups/JsonapiView.php (100%) rename {src => source}/packages/com_mokobackup/api/src/View/Backups/index.html (100%) rename {src => source}/packages/com_mokobackup/api/src/View/index.html (100%) rename {src => source}/packages/com_mokobackup/api/src/index.html (100%) rename {src => source}/packages/com_mokobackup/cli/index.html (100%) rename {src => source}/packages/com_mokobackup/cli/mokobackup.php (100%) rename {src => source}/packages/com_mokobackup/config.xml (100%) rename {src => source}/packages/com_mokobackup/forms/backup.xml (100%) rename {src => source}/packages/com_mokobackup/forms/filter_backups.xml (100%) rename {src => source}/packages/com_mokobackup/forms/filter_profiles.xml (100%) rename {src => source}/packages/com_mokobackup/forms/index.html (100%) rename {src => source}/packages/com_mokobackup/forms/profile.xml (100%) rename {src => source}/packages/com_mokobackup/index.html (100%) rename {src => source}/packages/com_mokobackup/language/en-GB/com_mokobackup.ini (100%) rename {src => source}/packages/com_mokobackup/language/en-GB/com_mokobackup.sys.ini (100%) rename {src => source}/packages/com_mokobackup/language/en-GB/index.html (100%) rename {src => source}/packages/com_mokobackup/language/en-US/com_mokobackup.ini (100%) rename {src => source}/packages/com_mokobackup/language/en-US/com_mokobackup.sys.ini (100%) rename {src => source}/packages/com_mokobackup/language/en-US/index.html (100%) rename {src => source}/packages/com_mokobackup/language/index.html (100%) rename {src => source}/packages/com_mokobackup/mokobackup.xml (100%) rename {src => source}/packages/com_mokobackup/services/index.html (100%) rename {src => source}/packages/com_mokobackup/services/provider.php (100%) rename {src => source}/packages/com_mokobackup/sql/index.html (100%) rename {src => source}/packages/com_mokobackup/sql/install.mysql.sql (100%) rename {src => source}/packages/com_mokobackup/sql/mysql/index.html (100%) rename {src => source}/packages/com_mokobackup/sql/uninstall.mysql.sql (100%) rename {src => source}/packages/com_mokobackup/sql/updates/index.html (100%) rename {src => source}/packages/com_mokobackup/sql/updates/mysql/01.00.00.sql (100%) rename {src => source}/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql (100%) rename {src => source}/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql (100%) rename {src => source}/packages/com_mokobackup/sql/updates/mysql/index.html (100%) rename {src => source}/packages/com_mokobackup/src/Controller/AjaxController.php (100%) rename {src => source}/packages/com_mokobackup/src/Controller/BackupController.php (100%) rename {src => source}/packages/com_mokobackup/src/Controller/BackupsController.php (100%) rename {src => source}/packages/com_mokobackup/src/Controller/DisplayController.php (100%) rename {src => source}/packages/com_mokobackup/src/Controller/ProfileController.php (100%) rename {src => source}/packages/com_mokobackup/src/Controller/ProfilesController.php (100%) rename {src => source}/packages/com_mokobackup/src/Controller/index.html (100%) rename {src => source}/packages/com_mokobackup/src/Engine/AkeebaImporter.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/ArchiverInterface.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/BackupEngine.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/DatabaseDumper.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/DatabaseImporter.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/DifferentialScanner.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/FileRestorer.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/FileScanner.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/FtpUploader.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/GoogleDriveUploader.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/JpaUnarchiver.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/MokoRestore.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/NotificationSender.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/PlaceholderResolver.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/RemoteUploaderInterface.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/RestoreEngine.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/S3Uploader.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/SteppedSession.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/TarGzArchiver.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/ZipArchiver.php (100%) rename {src => source}/packages/com_mokobackup/src/Engine/index.html (100%) rename {src => source}/packages/com_mokobackup/src/Extension/MokoBackupComponent.php (100%) rename {src => source}/packages/com_mokobackup/src/Extension/index.html (100%) rename {src => source}/packages/com_mokobackup/src/Field/DatabaseTablesField.php (100%) rename {src => source}/packages/com_mokobackup/src/Field/DirectoryFilterField.php (100%) rename {src => source}/packages/com_mokobackup/src/Field/ExcludeListField.php (100%) rename {src => source}/packages/com_mokobackup/src/Field/FolderPickerField.php (100%) rename {src => source}/packages/com_mokobackup/src/Model/BackupModel.php (100%) rename {src => source}/packages/com_mokobackup/src/Model/BackupsModel.php (100%) rename {src => source}/packages/com_mokobackup/src/Model/DashboardModel.php (100%) rename {src => source}/packages/com_mokobackup/src/Model/ProfileModel.php (100%) rename {src => source}/packages/com_mokobackup/src/Model/ProfilesModel.php (100%) rename {src => source}/packages/com_mokobackup/src/Model/index.html (100%) rename {src => source}/packages/com_mokobackup/src/Table/BackupTable.php (100%) rename {src => source}/packages/com_mokobackup/src/Table/ProfileTable.php (100%) rename {src => source}/packages/com_mokobackup/src/Table/index.html (100%) rename {src => source}/packages/com_mokobackup/src/View/Backup/HtmlView.php (100%) rename {src => source}/packages/com_mokobackup/src/View/Backup/index.html (100%) rename {src => source}/packages/com_mokobackup/src/View/Backups/HtmlView.php (100%) rename {src => source}/packages/com_mokobackup/src/View/Backups/index.html (100%) rename {src => source}/packages/com_mokobackup/src/View/Dashboard/HtmlView.php (100%) rename {src => source}/packages/com_mokobackup/src/View/Profile/HtmlView.php (100%) rename {src => source}/packages/com_mokobackup/src/View/Profile/index.html (100%) rename {src => source}/packages/com_mokobackup/src/View/Profiles/HtmlView.php (100%) rename {src => source}/packages/com_mokobackup/src/View/Profiles/index.html (100%) rename {src => source}/packages/com_mokobackup/src/View/index.html (100%) rename {src => source}/packages/com_mokobackup/src/index.html (100%) rename {src => source}/packages/com_mokobackup/tmpl/backup/default.php (100%) rename {src => source}/packages/com_mokobackup/tmpl/backup/index.html (100%) rename {src => source}/packages/com_mokobackup/tmpl/backups/default.php (100%) rename {src => source}/packages/com_mokobackup/tmpl/backups/index.html (100%) rename {src => source}/packages/com_mokobackup/tmpl/dashboard/default.php (100%) rename {src => source}/packages/com_mokobackup/tmpl/index.html (100%) rename {src => source}/packages/com_mokobackup/tmpl/profile/edit.php (100%) rename {src => source}/packages/com_mokobackup/tmpl/profile/index.html (100%) rename {src => source}/packages/com_mokobackup/tmpl/profiles/default.php (100%) rename {src => source}/packages/com_mokobackup/tmpl/profiles/index.html (100%) rename {src => source}/packages/index.html (100%) rename {src => source}/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini (100%) rename {src => source}/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini (100%) rename {src => source}/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_actionlog_mokobackup/mokobackup.php (100%) rename {src => source}/packages/plg_actionlog_mokobackup/mokobackup.xml (100%) rename {src => source}/packages/plg_actionlog_mokobackup/services/provider.php (100%) rename {src => source}/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php (100%) rename {src => source}/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini (100%) rename {src => source}/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini (100%) rename {src => source}/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_console_mokobackup/mokobackup.php (100%) rename {src => source}/packages/plg_console_mokobackup/mokobackup.xml (100%) rename {src => source}/packages/plg_console_mokobackup/services/provider.php (100%) rename {src => source}/packages/plg_console_mokobackup/src/Command/CleanupCommand.php (100%) rename {src => source}/packages/plg_console_mokobackup/src/Command/ListCommand.php (100%) rename {src => source}/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php (100%) rename {src => source}/packages/plg_console_mokobackup/src/Command/RestoreCommand.php (100%) rename {src => source}/packages/plg_console_mokobackup/src/Command/RunCommand.php (100%) rename {src => source}/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php (100%) rename {src => source}/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini (100%) rename {src => source}/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini (100%) rename {src => source}/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_content_mokobackup/mokobackup.php (100%) rename {src => source}/packages/plg_content_mokobackup/mokobackup.xml (100%) rename {src => source}/packages/plg_content_mokobackup/services/provider.php (100%) rename {src => source}/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php (100%) rename {src => source}/packages/plg_quickicon_mokobackup/index.html (100%) rename {src => source}/packages/plg_quickicon_mokobackup/language/en-GB/index.html (100%) rename {src => source}/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.ini (100%) rename {src => source}/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_quickicon_mokobackup/language/en-US/index.html (100%) rename {src => source}/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.ini (100%) rename {src => source}/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_quickicon_mokobackup/language/index.html (100%) rename {src => source}/packages/plg_quickicon_mokobackup/mokobackup.php (100%) rename {src => source}/packages/plg_quickicon_mokobackup/mokobackup.xml (100%) rename {src => source}/packages/plg_quickicon_mokobackup/services/index.html (100%) rename {src => source}/packages/plg_quickicon_mokobackup/services/provider.php (100%) rename {src => source}/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php (100%) rename {src => source}/packages/plg_quickicon_mokobackup/src/Extension/index.html (100%) rename {src => source}/packages/plg_quickicon_mokobackup/src/index.html (100%) rename {src => source}/packages/plg_system_mokobackup/index.html (100%) rename {src => source}/packages/plg_system_mokobackup/language/en-GB/index.html (100%) rename {src => source}/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.ini (100%) rename {src => source}/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_system_mokobackup/language/en-US/index.html (100%) rename {src => source}/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.ini (100%) rename {src => source}/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_system_mokobackup/language/index.html (100%) rename {src => source}/packages/plg_system_mokobackup/mokobackup.php (100%) rename {src => source}/packages/plg_system_mokobackup/mokobackup.xml (100%) rename {src => source}/packages/plg_system_mokobackup/services/index.html (100%) rename {src => source}/packages/plg_system_mokobackup/services/provider.php (100%) rename {src => source}/packages/plg_system_mokobackup/src/Extension/MokoBackup.php (100%) rename {src => source}/packages/plg_system_mokobackup/src/Extension/index.html (100%) rename {src => source}/packages/plg_system_mokobackup/src/index.html (100%) rename {src => source}/packages/plg_task_mokobackup/forms/index.html (100%) rename {src => source}/packages/plg_task_mokobackup/forms/run_profile.xml (100%) rename {src => source}/packages/plg_task_mokobackup/index.html (100%) rename {src => source}/packages/plg_task_mokobackup/language/en-GB/index.html (100%) rename {src => source}/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini (100%) rename {src => source}/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_task_mokobackup/language/en-US/index.html (100%) rename {src => source}/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini (100%) rename {src => source}/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_task_mokobackup/language/index.html (100%) rename {src => source}/packages/plg_task_mokobackup/mokobackup.php (100%) rename {src => source}/packages/plg_task_mokobackup/mokobackup.xml (100%) rename {src => source}/packages/plg_task_mokobackup/services/index.html (100%) rename {src => source}/packages/plg_task_mokobackup/services/provider.php (100%) rename {src => source}/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php (100%) rename {src => source}/packages/plg_task_mokobackup/src/Extension/index.html (100%) rename {src => source}/packages/plg_task_mokobackup/src/index.html (100%) rename {src => source}/packages/plg_webservices_mokobackup/index.html (100%) rename {src => source}/packages/plg_webservices_mokobackup/language/en-GB/index.html (100%) rename {src => source}/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.ini (100%) rename {src => source}/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_webservices_mokobackup/language/en-US/index.html (100%) rename {src => source}/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.ini (100%) rename {src => source}/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.sys.ini (100%) rename {src => source}/packages/plg_webservices_mokobackup/language/index.html (100%) rename {src => source}/packages/plg_webservices_mokobackup/mokobackup.php (100%) rename {src => source}/packages/plg_webservices_mokobackup/mokobackup.xml (100%) rename {src => source}/packages/plg_webservices_mokobackup/services/index.html (100%) rename {src => source}/packages/plg_webservices_mokobackup/services/provider.php (100%) rename {src => source}/packages/plg_webservices_mokobackup/src/Extension/MokoBackupWebServices.php (100%) rename {src => source}/packages/plg_webservices_mokobackup/src/Extension/index.html (100%) rename {src => source}/packages/plg_webservices_mokobackup/src/index.html (100%) rename {src => source}/pkg_mokobackup.xml (100%) rename {src => source}/script.php (100%) diff --git a/.gitignore b/.gitignore index 4abd225..4881dc1 100644 --- a/.gitignore +++ b/.gitignore @@ -151,7 +151,7 @@ package-lock.json # PHP / Composer tooling # ============================================================ vendor/ -!src/media/vendor/ +!source/media/vendor/ composer.lock *.phar codeception.phar diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 88620a1..498e7fc 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -16,6 +16,6 @@ PHP joomla-extension - src/ + source/ diff --git a/.mokogitea/workflows/ci-joomla.yml b/.mokogitea/workflows/ci-joomla.yml index f679e86..e67987b 100644 --- a/.mokogitea/workflows/ci-joomla.yml +++ b/.mokogitea/workflows/ci-joomla.yml @@ -67,7 +67,7 @@ jobs: - name: PHP syntax check run: | ERRORS=0 - for DIR in src/ htdocs/; do + for DIR in source/ src/ htdocs/; do if [ -d "$DIR" ]; then FOUND=1 while IFS= read -r -d '' FILE; do @@ -207,7 +207,7 @@ jobs: echo "### Language Directory Check" >> $GITHUB_STEP_SUMMARY ERRORS=0 - for DIR in src/ htdocs/; do + for DIR in source/ src/ htdocs/; do [ -d "$DIR" ] || continue # Find all language directories while IFS= read -r -d '' LANG_DIR; do @@ -239,7 +239,7 @@ jobs: MISSING=0 CHECKED=0 - for DIR in src/ htdocs/; do + for DIR in source/ src/ htdocs/; do if [ -d "$DIR" ]; then while IFS= read -r -d '' SUBDIR; do CHECKED=$((CHECKED + 1)) @@ -252,7 +252,7 @@ jobs: done if [ "${CHECKED}" -eq 0 ]; then - echo "No src/ or htdocs/ directories found — skipping." >> $GITHUB_STEP_SUMMARY + echo "No source/, src/, or htdocs/ directories found — skipping." >> $GITHUB_STEP_SUMMARY elif [ "${MISSING}" -gt 0 ]; then echo "" >> $GITHUB_STEP_SUMMARY echo "**${MISSING} director(ies) missing index.html out of ${CHECKED} checked.**" >> $GITHUB_STEP_SUMMARY @@ -450,7 +450,7 @@ jobs: # Determine source directory SRC_DIR="" - for DIR in src/ htdocs/ lib/; do + for DIR in source/ src/ htdocs/ lib/; do if [ -d "$DIR" ]; then SRC_DIR="$DIR" break @@ -458,7 +458,7 @@ jobs: done if [ -z "$SRC_DIR" ]; then - echo "No source directory found (src/, htdocs/, lib/) — skipping." >> $GITHUB_STEP_SUMMARY + echo "No source directory found (source/, src/, htdocs/, lib/) — skipping." >> $GITHUB_STEP_SUMMARY exit 0 fi diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 6625857..7d086e2 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -159,11 +159,11 @@ jobs: echo "::error file=${file}::Missing JEXEC guard: ${file}" ERRORS=$((ERRORS + 1)) fi - done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0) + done < <(find . -name "*.php" \( -path "*/source/*" -o -path "*/src/*" \) -not -path "./.git/*" -not -path "./vendor/*" -print0) if [ "$ERRORS" -gt 0 ]; then echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard" echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY - echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY + echo "${ERRORS} file(s) in source/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY exit 1 fi echo "JEXEC guard: OK" @@ -451,10 +451,11 @@ jobs: - name: Verify package source run: | - SOURCE_DIR="src" + SOURCE_DIR="source" + [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="src" [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" if [ ! -d "$SOURCE_DIR" ]; then - echo "::warning::No src/ or htdocs/ directory" + echo "::warning::No source/, src/, or htdocs/ directory" exit 0 fi FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l) diff --git a/.mokogitea/workflows/repo-health.yml b/.mokogitea/workflows/repo-health.yml index e3653eb..6dcdcf3 100644 --- a/.mokogitea/workflows/repo-health.yml +++ b/.mokogitea/workflows/repo-health.yml @@ -396,17 +396,19 @@ jobs: missing_required=() missing_optional=() - # Source directory: src/ or htdocs/ (either is valid for extension repos) + # Source directory: source/, src/, or htdocs/ (any is valid for extension repos) SOURCE_DIR="" - if [ -d "src" ]; then + if [ -d "source" ]; then + SOURCE_DIR="source" + elif [ -d "src" ]; then SOURCE_DIR="src" elif [ -d "htdocs" ]; then SOURCE_DIR="htdocs" elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then - # Platform/tooling repos don't need src/ + # Platform/tooling repos don't need source/ SOURCE_DIR="" else - missing_required+=("src/ or htdocs/ (source directory required)") + missing_required+=("source/ or htdocs/ (source directory required)") fi for item in "${required_artifacts[@]}"; do diff --git a/Makefile b/Makefile index df8aa3e..f38765e 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ EXTENSION_NAME := mokobackup EXTENSION_TYPE := package -SRC_DIR := src +SRC_DIR := source # Gitea GITEA_URL := https://git.mokoconsulting.tech diff --git a/src/index.html b/source/index.html similarity index 100% rename from src/index.html rename to source/index.html diff --git a/src/language/en-GB/index.html b/source/language/en-GB/index.html similarity index 100% rename from src/language/en-GB/index.html rename to source/language/en-GB/index.html diff --git a/src/language/en-GB/pkg_mokobackup.sys.ini b/source/language/en-GB/pkg_mokobackup.sys.ini similarity index 100% rename from src/language/en-GB/pkg_mokobackup.sys.ini rename to source/language/en-GB/pkg_mokobackup.sys.ini diff --git a/src/language/en-US/index.html b/source/language/en-US/index.html similarity index 100% rename from src/language/en-US/index.html rename to source/language/en-US/index.html diff --git a/src/language/en-US/pkg_mokobackup.sys.ini b/source/language/en-US/pkg_mokobackup.sys.ini similarity index 100% rename from src/language/en-US/pkg_mokobackup.sys.ini rename to source/language/en-US/pkg_mokobackup.sys.ini diff --git a/src/language/index.html b/source/language/index.html similarity index 100% rename from src/language/index.html rename to source/language/index.html diff --git a/src/packages/com_mokobackup/api/index.html b/source/packages/com_mokobackup/api/index.html similarity index 100% rename from src/packages/com_mokobackup/api/index.html rename to source/packages/com_mokobackup/api/index.html diff --git a/src/packages/com_mokobackup/api/src/Controller/BackupsController.php b/source/packages/com_mokobackup/api/src/Controller/BackupsController.php similarity index 100% rename from src/packages/com_mokobackup/api/src/Controller/BackupsController.php rename to source/packages/com_mokobackup/api/src/Controller/BackupsController.php diff --git a/src/packages/com_mokobackup/api/src/Controller/index.html b/source/packages/com_mokobackup/api/src/Controller/index.html similarity index 100% rename from src/packages/com_mokobackup/api/src/Controller/index.html rename to source/packages/com_mokobackup/api/src/Controller/index.html diff --git a/src/packages/com_mokobackup/api/src/View/Backups/JsonapiView.php b/source/packages/com_mokobackup/api/src/View/Backups/JsonapiView.php similarity index 100% rename from src/packages/com_mokobackup/api/src/View/Backups/JsonapiView.php rename to source/packages/com_mokobackup/api/src/View/Backups/JsonapiView.php diff --git a/src/packages/com_mokobackup/api/src/View/Backups/index.html b/source/packages/com_mokobackup/api/src/View/Backups/index.html similarity index 100% rename from src/packages/com_mokobackup/api/src/View/Backups/index.html rename to source/packages/com_mokobackup/api/src/View/Backups/index.html diff --git a/src/packages/com_mokobackup/api/src/View/index.html b/source/packages/com_mokobackup/api/src/View/index.html similarity index 100% rename from src/packages/com_mokobackup/api/src/View/index.html rename to source/packages/com_mokobackup/api/src/View/index.html diff --git a/src/packages/com_mokobackup/api/src/index.html b/source/packages/com_mokobackup/api/src/index.html similarity index 100% rename from src/packages/com_mokobackup/api/src/index.html rename to source/packages/com_mokobackup/api/src/index.html diff --git a/src/packages/com_mokobackup/cli/index.html b/source/packages/com_mokobackup/cli/index.html similarity index 100% rename from src/packages/com_mokobackup/cli/index.html rename to source/packages/com_mokobackup/cli/index.html diff --git a/src/packages/com_mokobackup/cli/mokobackup.php b/source/packages/com_mokobackup/cli/mokobackup.php similarity index 100% rename from src/packages/com_mokobackup/cli/mokobackup.php rename to source/packages/com_mokobackup/cli/mokobackup.php diff --git a/src/packages/com_mokobackup/config.xml b/source/packages/com_mokobackup/config.xml similarity index 100% rename from src/packages/com_mokobackup/config.xml rename to source/packages/com_mokobackup/config.xml diff --git a/src/packages/com_mokobackup/forms/backup.xml b/source/packages/com_mokobackup/forms/backup.xml similarity index 100% rename from src/packages/com_mokobackup/forms/backup.xml rename to source/packages/com_mokobackup/forms/backup.xml diff --git a/src/packages/com_mokobackup/forms/filter_backups.xml b/source/packages/com_mokobackup/forms/filter_backups.xml similarity index 100% rename from src/packages/com_mokobackup/forms/filter_backups.xml rename to source/packages/com_mokobackup/forms/filter_backups.xml diff --git a/src/packages/com_mokobackup/forms/filter_profiles.xml b/source/packages/com_mokobackup/forms/filter_profiles.xml similarity index 100% rename from src/packages/com_mokobackup/forms/filter_profiles.xml rename to source/packages/com_mokobackup/forms/filter_profiles.xml diff --git a/src/packages/com_mokobackup/forms/index.html b/source/packages/com_mokobackup/forms/index.html similarity index 100% rename from src/packages/com_mokobackup/forms/index.html rename to source/packages/com_mokobackup/forms/index.html diff --git a/src/packages/com_mokobackup/forms/profile.xml b/source/packages/com_mokobackup/forms/profile.xml similarity index 100% rename from src/packages/com_mokobackup/forms/profile.xml rename to source/packages/com_mokobackup/forms/profile.xml diff --git a/src/packages/com_mokobackup/index.html b/source/packages/com_mokobackup/index.html similarity index 100% rename from src/packages/com_mokobackup/index.html rename to source/packages/com_mokobackup/index.html diff --git a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini b/source/packages/com_mokobackup/language/en-GB/com_mokobackup.ini similarity index 100% rename from src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini rename to source/packages/com_mokobackup/language/en-GB/com_mokobackup.ini diff --git a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.sys.ini b/source/packages/com_mokobackup/language/en-GB/com_mokobackup.sys.ini similarity index 100% rename from src/packages/com_mokobackup/language/en-GB/com_mokobackup.sys.ini rename to source/packages/com_mokobackup/language/en-GB/com_mokobackup.sys.ini diff --git a/src/packages/com_mokobackup/language/en-GB/index.html b/source/packages/com_mokobackup/language/en-GB/index.html similarity index 100% rename from src/packages/com_mokobackup/language/en-GB/index.html rename to source/packages/com_mokobackup/language/en-GB/index.html diff --git a/src/packages/com_mokobackup/language/en-US/com_mokobackup.ini b/source/packages/com_mokobackup/language/en-US/com_mokobackup.ini similarity index 100% rename from src/packages/com_mokobackup/language/en-US/com_mokobackup.ini rename to source/packages/com_mokobackup/language/en-US/com_mokobackup.ini diff --git a/src/packages/com_mokobackup/language/en-US/com_mokobackup.sys.ini b/source/packages/com_mokobackup/language/en-US/com_mokobackup.sys.ini similarity index 100% rename from src/packages/com_mokobackup/language/en-US/com_mokobackup.sys.ini rename to source/packages/com_mokobackup/language/en-US/com_mokobackup.sys.ini diff --git a/src/packages/com_mokobackup/language/en-US/index.html b/source/packages/com_mokobackup/language/en-US/index.html similarity index 100% rename from src/packages/com_mokobackup/language/en-US/index.html rename to source/packages/com_mokobackup/language/en-US/index.html diff --git a/src/packages/com_mokobackup/language/index.html b/source/packages/com_mokobackup/language/index.html similarity index 100% rename from src/packages/com_mokobackup/language/index.html rename to source/packages/com_mokobackup/language/index.html diff --git a/src/packages/com_mokobackup/mokobackup.xml b/source/packages/com_mokobackup/mokobackup.xml similarity index 100% rename from src/packages/com_mokobackup/mokobackup.xml rename to source/packages/com_mokobackup/mokobackup.xml diff --git a/src/packages/com_mokobackup/services/index.html b/source/packages/com_mokobackup/services/index.html similarity index 100% rename from src/packages/com_mokobackup/services/index.html rename to source/packages/com_mokobackup/services/index.html diff --git a/src/packages/com_mokobackup/services/provider.php b/source/packages/com_mokobackup/services/provider.php similarity index 100% rename from src/packages/com_mokobackup/services/provider.php rename to source/packages/com_mokobackup/services/provider.php diff --git a/src/packages/com_mokobackup/sql/index.html b/source/packages/com_mokobackup/sql/index.html similarity index 100% rename from src/packages/com_mokobackup/sql/index.html rename to source/packages/com_mokobackup/sql/index.html diff --git a/src/packages/com_mokobackup/sql/install.mysql.sql b/source/packages/com_mokobackup/sql/install.mysql.sql similarity index 100% rename from src/packages/com_mokobackup/sql/install.mysql.sql rename to source/packages/com_mokobackup/sql/install.mysql.sql diff --git a/src/packages/com_mokobackup/sql/mysql/index.html b/source/packages/com_mokobackup/sql/mysql/index.html similarity index 100% rename from src/packages/com_mokobackup/sql/mysql/index.html rename to source/packages/com_mokobackup/sql/mysql/index.html diff --git a/src/packages/com_mokobackup/sql/uninstall.mysql.sql b/source/packages/com_mokobackup/sql/uninstall.mysql.sql similarity index 100% rename from src/packages/com_mokobackup/sql/uninstall.mysql.sql rename to source/packages/com_mokobackup/sql/uninstall.mysql.sql diff --git a/src/packages/com_mokobackup/sql/updates/index.html b/source/packages/com_mokobackup/sql/updates/index.html similarity index 100% rename from src/packages/com_mokobackup/sql/updates/index.html rename to source/packages/com_mokobackup/sql/updates/index.html diff --git a/src/packages/com_mokobackup/sql/updates/mysql/01.00.00.sql b/source/packages/com_mokobackup/sql/updates/mysql/01.00.00.sql similarity index 100% rename from src/packages/com_mokobackup/sql/updates/mysql/01.00.00.sql rename to source/packages/com_mokobackup/sql/updates/mysql/01.00.00.sql diff --git a/src/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql b/source/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql similarity index 100% rename from src/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql rename to source/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql diff --git a/src/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql b/source/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql similarity index 100% rename from src/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql rename to source/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql diff --git a/src/packages/com_mokobackup/sql/updates/mysql/index.html b/source/packages/com_mokobackup/sql/updates/mysql/index.html similarity index 100% rename from src/packages/com_mokobackup/sql/updates/mysql/index.html rename to source/packages/com_mokobackup/sql/updates/mysql/index.html diff --git a/src/packages/com_mokobackup/src/Controller/AjaxController.php b/source/packages/com_mokobackup/src/Controller/AjaxController.php similarity index 100% rename from src/packages/com_mokobackup/src/Controller/AjaxController.php rename to source/packages/com_mokobackup/src/Controller/AjaxController.php diff --git a/src/packages/com_mokobackup/src/Controller/BackupController.php b/source/packages/com_mokobackup/src/Controller/BackupController.php similarity index 100% rename from src/packages/com_mokobackup/src/Controller/BackupController.php rename to source/packages/com_mokobackup/src/Controller/BackupController.php diff --git a/src/packages/com_mokobackup/src/Controller/BackupsController.php b/source/packages/com_mokobackup/src/Controller/BackupsController.php similarity index 100% rename from src/packages/com_mokobackup/src/Controller/BackupsController.php rename to source/packages/com_mokobackup/src/Controller/BackupsController.php diff --git a/src/packages/com_mokobackup/src/Controller/DisplayController.php b/source/packages/com_mokobackup/src/Controller/DisplayController.php similarity index 100% rename from src/packages/com_mokobackup/src/Controller/DisplayController.php rename to source/packages/com_mokobackup/src/Controller/DisplayController.php diff --git a/src/packages/com_mokobackup/src/Controller/ProfileController.php b/source/packages/com_mokobackup/src/Controller/ProfileController.php similarity index 100% rename from src/packages/com_mokobackup/src/Controller/ProfileController.php rename to source/packages/com_mokobackup/src/Controller/ProfileController.php diff --git a/src/packages/com_mokobackup/src/Controller/ProfilesController.php b/source/packages/com_mokobackup/src/Controller/ProfilesController.php similarity index 100% rename from src/packages/com_mokobackup/src/Controller/ProfilesController.php rename to source/packages/com_mokobackup/src/Controller/ProfilesController.php diff --git a/src/packages/com_mokobackup/src/Controller/index.html b/source/packages/com_mokobackup/src/Controller/index.html similarity index 100% rename from src/packages/com_mokobackup/src/Controller/index.html rename to source/packages/com_mokobackup/src/Controller/index.html diff --git a/src/packages/com_mokobackup/src/Engine/AkeebaImporter.php b/source/packages/com_mokobackup/src/Engine/AkeebaImporter.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/AkeebaImporter.php rename to source/packages/com_mokobackup/src/Engine/AkeebaImporter.php diff --git a/src/packages/com_mokobackup/src/Engine/ArchiverInterface.php b/source/packages/com_mokobackup/src/Engine/ArchiverInterface.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/ArchiverInterface.php rename to source/packages/com_mokobackup/src/Engine/ArchiverInterface.php diff --git a/src/packages/com_mokobackup/src/Engine/BackupEngine.php b/source/packages/com_mokobackup/src/Engine/BackupEngine.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/BackupEngine.php rename to source/packages/com_mokobackup/src/Engine/BackupEngine.php diff --git a/src/packages/com_mokobackup/src/Engine/DatabaseDumper.php b/source/packages/com_mokobackup/src/Engine/DatabaseDumper.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/DatabaseDumper.php rename to source/packages/com_mokobackup/src/Engine/DatabaseDumper.php diff --git a/src/packages/com_mokobackup/src/Engine/DatabaseImporter.php b/source/packages/com_mokobackup/src/Engine/DatabaseImporter.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/DatabaseImporter.php rename to source/packages/com_mokobackup/src/Engine/DatabaseImporter.php diff --git a/src/packages/com_mokobackup/src/Engine/DifferentialScanner.php b/source/packages/com_mokobackup/src/Engine/DifferentialScanner.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/DifferentialScanner.php rename to source/packages/com_mokobackup/src/Engine/DifferentialScanner.php diff --git a/src/packages/com_mokobackup/src/Engine/FileRestorer.php b/source/packages/com_mokobackup/src/Engine/FileRestorer.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/FileRestorer.php rename to source/packages/com_mokobackup/src/Engine/FileRestorer.php diff --git a/src/packages/com_mokobackup/src/Engine/FileScanner.php b/source/packages/com_mokobackup/src/Engine/FileScanner.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/FileScanner.php rename to source/packages/com_mokobackup/src/Engine/FileScanner.php diff --git a/src/packages/com_mokobackup/src/Engine/FtpUploader.php b/source/packages/com_mokobackup/src/Engine/FtpUploader.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/FtpUploader.php rename to source/packages/com_mokobackup/src/Engine/FtpUploader.php diff --git a/src/packages/com_mokobackup/src/Engine/GoogleDriveUploader.php b/source/packages/com_mokobackup/src/Engine/GoogleDriveUploader.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/GoogleDriveUploader.php rename to source/packages/com_mokobackup/src/Engine/GoogleDriveUploader.php diff --git a/src/packages/com_mokobackup/src/Engine/JpaUnarchiver.php b/source/packages/com_mokobackup/src/Engine/JpaUnarchiver.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/JpaUnarchiver.php rename to source/packages/com_mokobackup/src/Engine/JpaUnarchiver.php diff --git a/src/packages/com_mokobackup/src/Engine/MokoRestore.php b/source/packages/com_mokobackup/src/Engine/MokoRestore.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/MokoRestore.php rename to source/packages/com_mokobackup/src/Engine/MokoRestore.php diff --git a/src/packages/com_mokobackup/src/Engine/NotificationSender.php b/source/packages/com_mokobackup/src/Engine/NotificationSender.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/NotificationSender.php rename to source/packages/com_mokobackup/src/Engine/NotificationSender.php diff --git a/src/packages/com_mokobackup/src/Engine/PlaceholderResolver.php b/source/packages/com_mokobackup/src/Engine/PlaceholderResolver.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/PlaceholderResolver.php rename to source/packages/com_mokobackup/src/Engine/PlaceholderResolver.php diff --git a/src/packages/com_mokobackup/src/Engine/RemoteUploaderInterface.php b/source/packages/com_mokobackup/src/Engine/RemoteUploaderInterface.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/RemoteUploaderInterface.php rename to source/packages/com_mokobackup/src/Engine/RemoteUploaderInterface.php diff --git a/src/packages/com_mokobackup/src/Engine/RestoreEngine.php b/source/packages/com_mokobackup/src/Engine/RestoreEngine.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/RestoreEngine.php rename to source/packages/com_mokobackup/src/Engine/RestoreEngine.php diff --git a/src/packages/com_mokobackup/src/Engine/S3Uploader.php b/source/packages/com_mokobackup/src/Engine/S3Uploader.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/S3Uploader.php rename to source/packages/com_mokobackup/src/Engine/S3Uploader.php diff --git a/src/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php b/source/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php rename to source/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php diff --git a/src/packages/com_mokobackup/src/Engine/SteppedSession.php b/source/packages/com_mokobackup/src/Engine/SteppedSession.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/SteppedSession.php rename to source/packages/com_mokobackup/src/Engine/SteppedSession.php diff --git a/src/packages/com_mokobackup/src/Engine/TarGzArchiver.php b/source/packages/com_mokobackup/src/Engine/TarGzArchiver.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/TarGzArchiver.php rename to source/packages/com_mokobackup/src/Engine/TarGzArchiver.php diff --git a/src/packages/com_mokobackup/src/Engine/ZipArchiver.php b/source/packages/com_mokobackup/src/Engine/ZipArchiver.php similarity index 100% rename from src/packages/com_mokobackup/src/Engine/ZipArchiver.php rename to source/packages/com_mokobackup/src/Engine/ZipArchiver.php diff --git a/src/packages/com_mokobackup/src/Engine/index.html b/source/packages/com_mokobackup/src/Engine/index.html similarity index 100% rename from src/packages/com_mokobackup/src/Engine/index.html rename to source/packages/com_mokobackup/src/Engine/index.html diff --git a/src/packages/com_mokobackup/src/Extension/MokoBackupComponent.php b/source/packages/com_mokobackup/src/Extension/MokoBackupComponent.php similarity index 100% rename from src/packages/com_mokobackup/src/Extension/MokoBackupComponent.php rename to source/packages/com_mokobackup/src/Extension/MokoBackupComponent.php diff --git a/src/packages/com_mokobackup/src/Extension/index.html b/source/packages/com_mokobackup/src/Extension/index.html similarity index 100% rename from src/packages/com_mokobackup/src/Extension/index.html rename to source/packages/com_mokobackup/src/Extension/index.html diff --git a/src/packages/com_mokobackup/src/Field/DatabaseTablesField.php b/source/packages/com_mokobackup/src/Field/DatabaseTablesField.php similarity index 100% rename from src/packages/com_mokobackup/src/Field/DatabaseTablesField.php rename to source/packages/com_mokobackup/src/Field/DatabaseTablesField.php diff --git a/src/packages/com_mokobackup/src/Field/DirectoryFilterField.php b/source/packages/com_mokobackup/src/Field/DirectoryFilterField.php similarity index 100% rename from src/packages/com_mokobackup/src/Field/DirectoryFilterField.php rename to source/packages/com_mokobackup/src/Field/DirectoryFilterField.php diff --git a/src/packages/com_mokobackup/src/Field/ExcludeListField.php b/source/packages/com_mokobackup/src/Field/ExcludeListField.php similarity index 100% rename from src/packages/com_mokobackup/src/Field/ExcludeListField.php rename to source/packages/com_mokobackup/src/Field/ExcludeListField.php diff --git a/src/packages/com_mokobackup/src/Field/FolderPickerField.php b/source/packages/com_mokobackup/src/Field/FolderPickerField.php similarity index 100% rename from src/packages/com_mokobackup/src/Field/FolderPickerField.php rename to source/packages/com_mokobackup/src/Field/FolderPickerField.php diff --git a/src/packages/com_mokobackup/src/Model/BackupModel.php b/source/packages/com_mokobackup/src/Model/BackupModel.php similarity index 100% rename from src/packages/com_mokobackup/src/Model/BackupModel.php rename to source/packages/com_mokobackup/src/Model/BackupModel.php diff --git a/src/packages/com_mokobackup/src/Model/BackupsModel.php b/source/packages/com_mokobackup/src/Model/BackupsModel.php similarity index 100% rename from src/packages/com_mokobackup/src/Model/BackupsModel.php rename to source/packages/com_mokobackup/src/Model/BackupsModel.php diff --git a/src/packages/com_mokobackup/src/Model/DashboardModel.php b/source/packages/com_mokobackup/src/Model/DashboardModel.php similarity index 100% rename from src/packages/com_mokobackup/src/Model/DashboardModel.php rename to source/packages/com_mokobackup/src/Model/DashboardModel.php diff --git a/src/packages/com_mokobackup/src/Model/ProfileModel.php b/source/packages/com_mokobackup/src/Model/ProfileModel.php similarity index 100% rename from src/packages/com_mokobackup/src/Model/ProfileModel.php rename to source/packages/com_mokobackup/src/Model/ProfileModel.php diff --git a/src/packages/com_mokobackup/src/Model/ProfilesModel.php b/source/packages/com_mokobackup/src/Model/ProfilesModel.php similarity index 100% rename from src/packages/com_mokobackup/src/Model/ProfilesModel.php rename to source/packages/com_mokobackup/src/Model/ProfilesModel.php diff --git a/src/packages/com_mokobackup/src/Model/index.html b/source/packages/com_mokobackup/src/Model/index.html similarity index 100% rename from src/packages/com_mokobackup/src/Model/index.html rename to source/packages/com_mokobackup/src/Model/index.html diff --git a/src/packages/com_mokobackup/src/Table/BackupTable.php b/source/packages/com_mokobackup/src/Table/BackupTable.php similarity index 100% rename from src/packages/com_mokobackup/src/Table/BackupTable.php rename to source/packages/com_mokobackup/src/Table/BackupTable.php diff --git a/src/packages/com_mokobackup/src/Table/ProfileTable.php b/source/packages/com_mokobackup/src/Table/ProfileTable.php similarity index 100% rename from src/packages/com_mokobackup/src/Table/ProfileTable.php rename to source/packages/com_mokobackup/src/Table/ProfileTable.php diff --git a/src/packages/com_mokobackup/src/Table/index.html b/source/packages/com_mokobackup/src/Table/index.html similarity index 100% rename from src/packages/com_mokobackup/src/Table/index.html rename to source/packages/com_mokobackup/src/Table/index.html diff --git a/src/packages/com_mokobackup/src/View/Backup/HtmlView.php b/source/packages/com_mokobackup/src/View/Backup/HtmlView.php similarity index 100% rename from src/packages/com_mokobackup/src/View/Backup/HtmlView.php rename to source/packages/com_mokobackup/src/View/Backup/HtmlView.php diff --git a/src/packages/com_mokobackup/src/View/Backup/index.html b/source/packages/com_mokobackup/src/View/Backup/index.html similarity index 100% rename from src/packages/com_mokobackup/src/View/Backup/index.html rename to source/packages/com_mokobackup/src/View/Backup/index.html diff --git a/src/packages/com_mokobackup/src/View/Backups/HtmlView.php b/source/packages/com_mokobackup/src/View/Backups/HtmlView.php similarity index 100% rename from src/packages/com_mokobackup/src/View/Backups/HtmlView.php rename to source/packages/com_mokobackup/src/View/Backups/HtmlView.php diff --git a/src/packages/com_mokobackup/src/View/Backups/index.html b/source/packages/com_mokobackup/src/View/Backups/index.html similarity index 100% rename from src/packages/com_mokobackup/src/View/Backups/index.html rename to source/packages/com_mokobackup/src/View/Backups/index.html diff --git a/src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php b/source/packages/com_mokobackup/src/View/Dashboard/HtmlView.php similarity index 100% rename from src/packages/com_mokobackup/src/View/Dashboard/HtmlView.php rename to source/packages/com_mokobackup/src/View/Dashboard/HtmlView.php diff --git a/src/packages/com_mokobackup/src/View/Profile/HtmlView.php b/source/packages/com_mokobackup/src/View/Profile/HtmlView.php similarity index 100% rename from src/packages/com_mokobackup/src/View/Profile/HtmlView.php rename to source/packages/com_mokobackup/src/View/Profile/HtmlView.php diff --git a/src/packages/com_mokobackup/src/View/Profile/index.html b/source/packages/com_mokobackup/src/View/Profile/index.html similarity index 100% rename from src/packages/com_mokobackup/src/View/Profile/index.html rename to source/packages/com_mokobackup/src/View/Profile/index.html diff --git a/src/packages/com_mokobackup/src/View/Profiles/HtmlView.php b/source/packages/com_mokobackup/src/View/Profiles/HtmlView.php similarity index 100% rename from src/packages/com_mokobackup/src/View/Profiles/HtmlView.php rename to source/packages/com_mokobackup/src/View/Profiles/HtmlView.php diff --git a/src/packages/com_mokobackup/src/View/Profiles/index.html b/source/packages/com_mokobackup/src/View/Profiles/index.html similarity index 100% rename from src/packages/com_mokobackup/src/View/Profiles/index.html rename to source/packages/com_mokobackup/src/View/Profiles/index.html diff --git a/src/packages/com_mokobackup/src/View/index.html b/source/packages/com_mokobackup/src/View/index.html similarity index 100% rename from src/packages/com_mokobackup/src/View/index.html rename to source/packages/com_mokobackup/src/View/index.html diff --git a/src/packages/com_mokobackup/src/index.html b/source/packages/com_mokobackup/src/index.html similarity index 100% rename from src/packages/com_mokobackup/src/index.html rename to source/packages/com_mokobackup/src/index.html diff --git a/src/packages/com_mokobackup/tmpl/backup/default.php b/source/packages/com_mokobackup/tmpl/backup/default.php similarity index 100% rename from src/packages/com_mokobackup/tmpl/backup/default.php rename to source/packages/com_mokobackup/tmpl/backup/default.php diff --git a/src/packages/com_mokobackup/tmpl/backup/index.html b/source/packages/com_mokobackup/tmpl/backup/index.html similarity index 100% rename from src/packages/com_mokobackup/tmpl/backup/index.html rename to source/packages/com_mokobackup/tmpl/backup/index.html diff --git a/src/packages/com_mokobackup/tmpl/backups/default.php b/source/packages/com_mokobackup/tmpl/backups/default.php similarity index 100% rename from src/packages/com_mokobackup/tmpl/backups/default.php rename to source/packages/com_mokobackup/tmpl/backups/default.php diff --git a/src/packages/com_mokobackup/tmpl/backups/index.html b/source/packages/com_mokobackup/tmpl/backups/index.html similarity index 100% rename from src/packages/com_mokobackup/tmpl/backups/index.html rename to source/packages/com_mokobackup/tmpl/backups/index.html diff --git a/src/packages/com_mokobackup/tmpl/dashboard/default.php b/source/packages/com_mokobackup/tmpl/dashboard/default.php similarity index 100% rename from src/packages/com_mokobackup/tmpl/dashboard/default.php rename to source/packages/com_mokobackup/tmpl/dashboard/default.php diff --git a/src/packages/com_mokobackup/tmpl/index.html b/source/packages/com_mokobackup/tmpl/index.html similarity index 100% rename from src/packages/com_mokobackup/tmpl/index.html rename to source/packages/com_mokobackup/tmpl/index.html diff --git a/src/packages/com_mokobackup/tmpl/profile/edit.php b/source/packages/com_mokobackup/tmpl/profile/edit.php similarity index 100% rename from src/packages/com_mokobackup/tmpl/profile/edit.php rename to source/packages/com_mokobackup/tmpl/profile/edit.php diff --git a/src/packages/com_mokobackup/tmpl/profile/index.html b/source/packages/com_mokobackup/tmpl/profile/index.html similarity index 100% rename from src/packages/com_mokobackup/tmpl/profile/index.html rename to source/packages/com_mokobackup/tmpl/profile/index.html diff --git a/src/packages/com_mokobackup/tmpl/profiles/default.php b/source/packages/com_mokobackup/tmpl/profiles/default.php similarity index 100% rename from src/packages/com_mokobackup/tmpl/profiles/default.php rename to source/packages/com_mokobackup/tmpl/profiles/default.php diff --git a/src/packages/com_mokobackup/tmpl/profiles/index.html b/source/packages/com_mokobackup/tmpl/profiles/index.html similarity index 100% rename from src/packages/com_mokobackup/tmpl/profiles/index.html rename to source/packages/com_mokobackup/tmpl/profiles/index.html diff --git a/src/packages/index.html b/source/packages/index.html similarity index 100% rename from src/packages/index.html rename to source/packages/index.html diff --git a/src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini b/source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini similarity index 100% rename from src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini rename to source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini diff --git a/src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini b/source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini rename to source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini diff --git a/src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini b/source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini similarity index 100% rename from src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini rename to source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini diff --git a/src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini b/source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini rename to source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini diff --git a/src/packages/plg_actionlog_mokobackup/mokobackup.php b/source/packages/plg_actionlog_mokobackup/mokobackup.php similarity index 100% rename from src/packages/plg_actionlog_mokobackup/mokobackup.php rename to source/packages/plg_actionlog_mokobackup/mokobackup.php diff --git a/src/packages/plg_actionlog_mokobackup/mokobackup.xml b/source/packages/plg_actionlog_mokobackup/mokobackup.xml similarity index 100% rename from src/packages/plg_actionlog_mokobackup/mokobackup.xml rename to source/packages/plg_actionlog_mokobackup/mokobackup.xml diff --git a/src/packages/plg_actionlog_mokobackup/services/provider.php b/source/packages/plg_actionlog_mokobackup/services/provider.php similarity index 100% rename from src/packages/plg_actionlog_mokobackup/services/provider.php rename to source/packages/plg_actionlog_mokobackup/services/provider.php diff --git a/src/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php b/source/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php similarity index 100% rename from src/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php rename to source/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php diff --git a/src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini b/source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini similarity index 100% rename from src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini rename to source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini diff --git a/src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini b/source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini rename to source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini diff --git a/src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini b/source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini similarity index 100% rename from src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini rename to source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini diff --git a/src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini b/source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini rename to source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini diff --git a/src/packages/plg_console_mokobackup/mokobackup.php b/source/packages/plg_console_mokobackup/mokobackup.php similarity index 100% rename from src/packages/plg_console_mokobackup/mokobackup.php rename to source/packages/plg_console_mokobackup/mokobackup.php diff --git a/src/packages/plg_console_mokobackup/mokobackup.xml b/source/packages/plg_console_mokobackup/mokobackup.xml similarity index 100% rename from src/packages/plg_console_mokobackup/mokobackup.xml rename to source/packages/plg_console_mokobackup/mokobackup.xml diff --git a/src/packages/plg_console_mokobackup/services/provider.php b/source/packages/plg_console_mokobackup/services/provider.php similarity index 100% rename from src/packages/plg_console_mokobackup/services/provider.php rename to source/packages/plg_console_mokobackup/services/provider.php diff --git a/src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php b/source/packages/plg_console_mokobackup/src/Command/CleanupCommand.php similarity index 100% rename from src/packages/plg_console_mokobackup/src/Command/CleanupCommand.php rename to source/packages/plg_console_mokobackup/src/Command/CleanupCommand.php diff --git a/src/packages/plg_console_mokobackup/src/Command/ListCommand.php b/source/packages/plg_console_mokobackup/src/Command/ListCommand.php similarity index 100% rename from src/packages/plg_console_mokobackup/src/Command/ListCommand.php rename to source/packages/plg_console_mokobackup/src/Command/ListCommand.php diff --git a/src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php b/source/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php similarity index 100% rename from src/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php rename to source/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php diff --git a/src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php b/source/packages/plg_console_mokobackup/src/Command/RestoreCommand.php similarity index 100% rename from src/packages/plg_console_mokobackup/src/Command/RestoreCommand.php rename to source/packages/plg_console_mokobackup/src/Command/RestoreCommand.php diff --git a/src/packages/plg_console_mokobackup/src/Command/RunCommand.php b/source/packages/plg_console_mokobackup/src/Command/RunCommand.php similarity index 100% rename from src/packages/plg_console_mokobackup/src/Command/RunCommand.php rename to source/packages/plg_console_mokobackup/src/Command/RunCommand.php diff --git a/src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php b/source/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php similarity index 100% rename from src/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php rename to source/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php diff --git a/src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini b/source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini similarity index 100% rename from src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini rename to source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini diff --git a/src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini b/source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini rename to source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini diff --git a/src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini b/source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini similarity index 100% rename from src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini rename to source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini diff --git a/src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini b/source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini rename to source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini diff --git a/src/packages/plg_content_mokobackup/mokobackup.php b/source/packages/plg_content_mokobackup/mokobackup.php similarity index 100% rename from src/packages/plg_content_mokobackup/mokobackup.php rename to source/packages/plg_content_mokobackup/mokobackup.php diff --git a/src/packages/plg_content_mokobackup/mokobackup.xml b/source/packages/plg_content_mokobackup/mokobackup.xml similarity index 100% rename from src/packages/plg_content_mokobackup/mokobackup.xml rename to source/packages/plg_content_mokobackup/mokobackup.xml diff --git a/src/packages/plg_content_mokobackup/services/provider.php b/source/packages/plg_content_mokobackup/services/provider.php similarity index 100% rename from src/packages/plg_content_mokobackup/services/provider.php rename to source/packages/plg_content_mokobackup/services/provider.php diff --git a/src/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php b/source/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php similarity index 100% rename from src/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php rename to source/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php diff --git a/src/packages/plg_quickicon_mokobackup/index.html b/source/packages/plg_quickicon_mokobackup/index.html similarity index 100% rename from src/packages/plg_quickicon_mokobackup/index.html rename to source/packages/plg_quickicon_mokobackup/index.html diff --git a/src/packages/plg_quickicon_mokobackup/language/en-GB/index.html b/source/packages/plg_quickicon_mokobackup/language/en-GB/index.html similarity index 100% rename from src/packages/plg_quickicon_mokobackup/language/en-GB/index.html rename to source/packages/plg_quickicon_mokobackup/language/en-GB/index.html diff --git a/src/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.ini b/source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.ini similarity index 100% rename from src/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.ini rename to source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.ini diff --git a/src/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.sys.ini b/source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.sys.ini rename to source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.sys.ini diff --git a/src/packages/plg_quickicon_mokobackup/language/en-US/index.html b/source/packages/plg_quickicon_mokobackup/language/en-US/index.html similarity index 100% rename from src/packages/plg_quickicon_mokobackup/language/en-US/index.html rename to source/packages/plg_quickicon_mokobackup/language/en-US/index.html diff --git a/src/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.ini b/source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.ini similarity index 100% rename from src/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.ini rename to source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.ini diff --git a/src/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.sys.ini b/source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.sys.ini rename to source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.sys.ini diff --git a/src/packages/plg_quickicon_mokobackup/language/index.html b/source/packages/plg_quickicon_mokobackup/language/index.html similarity index 100% rename from src/packages/plg_quickicon_mokobackup/language/index.html rename to source/packages/plg_quickicon_mokobackup/language/index.html diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.php b/source/packages/plg_quickicon_mokobackup/mokobackup.php similarity index 100% rename from src/packages/plg_quickicon_mokobackup/mokobackup.php rename to source/packages/plg_quickicon_mokobackup/mokobackup.php diff --git a/src/packages/plg_quickicon_mokobackup/mokobackup.xml b/source/packages/plg_quickicon_mokobackup/mokobackup.xml similarity index 100% rename from src/packages/plg_quickicon_mokobackup/mokobackup.xml rename to source/packages/plg_quickicon_mokobackup/mokobackup.xml diff --git a/src/packages/plg_quickicon_mokobackup/services/index.html b/source/packages/plg_quickicon_mokobackup/services/index.html similarity index 100% rename from src/packages/plg_quickicon_mokobackup/services/index.html rename to source/packages/plg_quickicon_mokobackup/services/index.html diff --git a/src/packages/plg_quickicon_mokobackup/services/provider.php b/source/packages/plg_quickicon_mokobackup/services/provider.php similarity index 100% rename from src/packages/plg_quickicon_mokobackup/services/provider.php rename to source/packages/plg_quickicon_mokobackup/services/provider.php diff --git a/src/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php b/source/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php similarity index 100% rename from src/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php rename to source/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php diff --git a/src/packages/plg_quickicon_mokobackup/src/Extension/index.html b/source/packages/plg_quickicon_mokobackup/src/Extension/index.html similarity index 100% rename from src/packages/plg_quickicon_mokobackup/src/Extension/index.html rename to source/packages/plg_quickicon_mokobackup/src/Extension/index.html diff --git a/src/packages/plg_quickicon_mokobackup/src/index.html b/source/packages/plg_quickicon_mokobackup/src/index.html similarity index 100% rename from src/packages/plg_quickicon_mokobackup/src/index.html rename to source/packages/plg_quickicon_mokobackup/src/index.html diff --git a/src/packages/plg_system_mokobackup/index.html b/source/packages/plg_system_mokobackup/index.html similarity index 100% rename from src/packages/plg_system_mokobackup/index.html rename to source/packages/plg_system_mokobackup/index.html diff --git a/src/packages/plg_system_mokobackup/language/en-GB/index.html b/source/packages/plg_system_mokobackup/language/en-GB/index.html similarity index 100% rename from src/packages/plg_system_mokobackup/language/en-GB/index.html rename to source/packages/plg_system_mokobackup/language/en-GB/index.html diff --git a/src/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.ini b/source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.ini similarity index 100% rename from src/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.ini rename to source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.ini diff --git a/src/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.sys.ini b/source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.sys.ini rename to source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.sys.ini diff --git a/src/packages/plg_system_mokobackup/language/en-US/index.html b/source/packages/plg_system_mokobackup/language/en-US/index.html similarity index 100% rename from src/packages/plg_system_mokobackup/language/en-US/index.html rename to source/packages/plg_system_mokobackup/language/en-US/index.html diff --git a/src/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.ini b/source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.ini similarity index 100% rename from src/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.ini rename to source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.ini diff --git a/src/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.sys.ini b/source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.sys.ini rename to source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.sys.ini diff --git a/src/packages/plg_system_mokobackup/language/index.html b/source/packages/plg_system_mokobackup/language/index.html similarity index 100% rename from src/packages/plg_system_mokobackup/language/index.html rename to source/packages/plg_system_mokobackup/language/index.html diff --git a/src/packages/plg_system_mokobackup/mokobackup.php b/source/packages/plg_system_mokobackup/mokobackup.php similarity index 100% rename from src/packages/plg_system_mokobackup/mokobackup.php rename to source/packages/plg_system_mokobackup/mokobackup.php diff --git a/src/packages/plg_system_mokobackup/mokobackup.xml b/source/packages/plg_system_mokobackup/mokobackup.xml similarity index 100% rename from src/packages/plg_system_mokobackup/mokobackup.xml rename to source/packages/plg_system_mokobackup/mokobackup.xml diff --git a/src/packages/plg_system_mokobackup/services/index.html b/source/packages/plg_system_mokobackup/services/index.html similarity index 100% rename from src/packages/plg_system_mokobackup/services/index.html rename to source/packages/plg_system_mokobackup/services/index.html diff --git a/src/packages/plg_system_mokobackup/services/provider.php b/source/packages/plg_system_mokobackup/services/provider.php similarity index 100% rename from src/packages/plg_system_mokobackup/services/provider.php rename to source/packages/plg_system_mokobackup/services/provider.php diff --git a/src/packages/plg_system_mokobackup/src/Extension/MokoBackup.php b/source/packages/plg_system_mokobackup/src/Extension/MokoBackup.php similarity index 100% rename from src/packages/plg_system_mokobackup/src/Extension/MokoBackup.php rename to source/packages/plg_system_mokobackup/src/Extension/MokoBackup.php diff --git a/src/packages/plg_system_mokobackup/src/Extension/index.html b/source/packages/plg_system_mokobackup/src/Extension/index.html similarity index 100% rename from src/packages/plg_system_mokobackup/src/Extension/index.html rename to source/packages/plg_system_mokobackup/src/Extension/index.html diff --git a/src/packages/plg_system_mokobackup/src/index.html b/source/packages/plg_system_mokobackup/src/index.html similarity index 100% rename from src/packages/plg_system_mokobackup/src/index.html rename to source/packages/plg_system_mokobackup/src/index.html diff --git a/src/packages/plg_task_mokobackup/forms/index.html b/source/packages/plg_task_mokobackup/forms/index.html similarity index 100% rename from src/packages/plg_task_mokobackup/forms/index.html rename to source/packages/plg_task_mokobackup/forms/index.html diff --git a/src/packages/plg_task_mokobackup/forms/run_profile.xml b/source/packages/plg_task_mokobackup/forms/run_profile.xml similarity index 100% rename from src/packages/plg_task_mokobackup/forms/run_profile.xml rename to source/packages/plg_task_mokobackup/forms/run_profile.xml diff --git a/src/packages/plg_task_mokobackup/index.html b/source/packages/plg_task_mokobackup/index.html similarity index 100% rename from src/packages/plg_task_mokobackup/index.html rename to source/packages/plg_task_mokobackup/index.html diff --git a/src/packages/plg_task_mokobackup/language/en-GB/index.html b/source/packages/plg_task_mokobackup/language/en-GB/index.html similarity index 100% rename from src/packages/plg_task_mokobackup/language/en-GB/index.html rename to source/packages/plg_task_mokobackup/language/en-GB/index.html diff --git a/src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini b/source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini similarity index 100% rename from src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini rename to source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini diff --git a/src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini b/source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini rename to source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini diff --git a/src/packages/plg_task_mokobackup/language/en-US/index.html b/source/packages/plg_task_mokobackup/language/en-US/index.html similarity index 100% rename from src/packages/plg_task_mokobackup/language/en-US/index.html rename to source/packages/plg_task_mokobackup/language/en-US/index.html diff --git a/src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini b/source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini similarity index 100% rename from src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini rename to source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini diff --git a/src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini b/source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini rename to source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini diff --git a/src/packages/plg_task_mokobackup/language/index.html b/source/packages/plg_task_mokobackup/language/index.html similarity index 100% rename from src/packages/plg_task_mokobackup/language/index.html rename to source/packages/plg_task_mokobackup/language/index.html diff --git a/src/packages/plg_task_mokobackup/mokobackup.php b/source/packages/plg_task_mokobackup/mokobackup.php similarity index 100% rename from src/packages/plg_task_mokobackup/mokobackup.php rename to source/packages/plg_task_mokobackup/mokobackup.php diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/source/packages/plg_task_mokobackup/mokobackup.xml similarity index 100% rename from src/packages/plg_task_mokobackup/mokobackup.xml rename to source/packages/plg_task_mokobackup/mokobackup.xml diff --git a/src/packages/plg_task_mokobackup/services/index.html b/source/packages/plg_task_mokobackup/services/index.html similarity index 100% rename from src/packages/plg_task_mokobackup/services/index.html rename to source/packages/plg_task_mokobackup/services/index.html diff --git a/src/packages/plg_task_mokobackup/services/provider.php b/source/packages/plg_task_mokobackup/services/provider.php similarity index 100% rename from src/packages/plg_task_mokobackup/services/provider.php rename to source/packages/plg_task_mokobackup/services/provider.php diff --git a/src/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php b/source/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php similarity index 100% rename from src/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php rename to source/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php diff --git a/src/packages/plg_task_mokobackup/src/Extension/index.html b/source/packages/plg_task_mokobackup/src/Extension/index.html similarity index 100% rename from src/packages/plg_task_mokobackup/src/Extension/index.html rename to source/packages/plg_task_mokobackup/src/Extension/index.html diff --git a/src/packages/plg_task_mokobackup/src/index.html b/source/packages/plg_task_mokobackup/src/index.html similarity index 100% rename from src/packages/plg_task_mokobackup/src/index.html rename to source/packages/plg_task_mokobackup/src/index.html diff --git a/src/packages/plg_webservices_mokobackup/index.html b/source/packages/plg_webservices_mokobackup/index.html similarity index 100% rename from src/packages/plg_webservices_mokobackup/index.html rename to source/packages/plg_webservices_mokobackup/index.html diff --git a/src/packages/plg_webservices_mokobackup/language/en-GB/index.html b/source/packages/plg_webservices_mokobackup/language/en-GB/index.html similarity index 100% rename from src/packages/plg_webservices_mokobackup/language/en-GB/index.html rename to source/packages/plg_webservices_mokobackup/language/en-GB/index.html diff --git a/src/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.ini b/source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.ini similarity index 100% rename from src/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.ini rename to source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.ini diff --git a/src/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.sys.ini b/source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.sys.ini rename to source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.sys.ini diff --git a/src/packages/plg_webservices_mokobackup/language/en-US/index.html b/source/packages/plg_webservices_mokobackup/language/en-US/index.html similarity index 100% rename from src/packages/plg_webservices_mokobackup/language/en-US/index.html rename to source/packages/plg_webservices_mokobackup/language/en-US/index.html diff --git a/src/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.ini b/source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.ini similarity index 100% rename from src/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.ini rename to source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.ini diff --git a/src/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.sys.ini b/source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.sys.ini similarity index 100% rename from src/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.sys.ini rename to source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.sys.ini diff --git a/src/packages/plg_webservices_mokobackup/language/index.html b/source/packages/plg_webservices_mokobackup/language/index.html similarity index 100% rename from src/packages/plg_webservices_mokobackup/language/index.html rename to source/packages/plg_webservices_mokobackup/language/index.html diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.php b/source/packages/plg_webservices_mokobackup/mokobackup.php similarity index 100% rename from src/packages/plg_webservices_mokobackup/mokobackup.php rename to source/packages/plg_webservices_mokobackup/mokobackup.php diff --git a/src/packages/plg_webservices_mokobackup/mokobackup.xml b/source/packages/plg_webservices_mokobackup/mokobackup.xml similarity index 100% rename from src/packages/plg_webservices_mokobackup/mokobackup.xml rename to source/packages/plg_webservices_mokobackup/mokobackup.xml diff --git a/src/packages/plg_webservices_mokobackup/services/index.html b/source/packages/plg_webservices_mokobackup/services/index.html similarity index 100% rename from src/packages/plg_webservices_mokobackup/services/index.html rename to source/packages/plg_webservices_mokobackup/services/index.html diff --git a/src/packages/plg_webservices_mokobackup/services/provider.php b/source/packages/plg_webservices_mokobackup/services/provider.php similarity index 100% rename from src/packages/plg_webservices_mokobackup/services/provider.php rename to source/packages/plg_webservices_mokobackup/services/provider.php diff --git a/src/packages/plg_webservices_mokobackup/src/Extension/MokoBackupWebServices.php b/source/packages/plg_webservices_mokobackup/src/Extension/MokoBackupWebServices.php similarity index 100% rename from src/packages/plg_webservices_mokobackup/src/Extension/MokoBackupWebServices.php rename to source/packages/plg_webservices_mokobackup/src/Extension/MokoBackupWebServices.php diff --git a/src/packages/plg_webservices_mokobackup/src/Extension/index.html b/source/packages/plg_webservices_mokobackup/src/Extension/index.html similarity index 100% rename from src/packages/plg_webservices_mokobackup/src/Extension/index.html rename to source/packages/plg_webservices_mokobackup/src/Extension/index.html diff --git a/src/packages/plg_webservices_mokobackup/src/index.html b/source/packages/plg_webservices_mokobackup/src/index.html similarity index 100% rename from src/packages/plg_webservices_mokobackup/src/index.html rename to source/packages/plg_webservices_mokobackup/src/index.html diff --git a/src/pkg_mokobackup.xml b/source/pkg_mokobackup.xml similarity index 100% rename from src/pkg_mokobackup.xml rename to source/pkg_mokobackup.xml diff --git a/src/script.php b/source/script.php similarity index 100% rename from src/script.php rename to source/script.php -- 2.52.0 From da9da3ef2cb7714480a24cdd1030e9d7f257e1c6 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 09:30:48 -0500 Subject: [PATCH 50/81] chore: move CLAUDE.md to .mokogitea/ directory Relocate CLAUDE.md from repo root to .mokogitea/ per project convention. Content updated with focused, repo-specific architecture and rules. Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md => .mokogitea/CLAUDE.md | 49 ++++++++++++------------------- 1 file changed, 19 insertions(+), 30 deletions(-) rename CLAUDE.md => .mokogitea/CLAUDE.md (53%) diff --git a/CLAUDE.md b/.mokogitea/CLAUDE.md similarity index 53% rename from CLAUDE.md rename to .mokogitea/CLAUDE.md index 7049fa1..7fcb82c 100644 --- a/CLAUDE.md +++ b/.mokogitea/CLAUDE.md @@ -1,45 +1,37 @@ -# CLAUDE.md +# MokoJoomBackup -This file provides guidance to Claude Code when working with this repository. +Full-site backup and restore for Joomla — database, files, and configuration. Replaces Akeeba Backup Pro. -## Project Overview - -**MokoJoomBackup** -- Full-site backup and restore for Joomla — database, files, and configuration +## Quick Reference | Field | Value | |---|---| -| **Platform** | joomla | -| **Language** | PHP | -| **Default branch** | main | -| **License** | GPL-3.0-or-later | +| **Package** | `pkg_mokobackup` | +| **Language** | PHP 8.1+ | +| **Branch** | develop on `dev`, merge to `main` (protected) | | **Wiki** | [MokoJoomBackup Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/wiki) | -| **Standards** | [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home) | -## Common Commands +## Commands ```bash -make build # Build the project +make build # Build package ZIP make lint # Run linters make validate # Validate structure make release # Full release pipeline -make minify # Minify CSS/JS assets make clean # Clean build artifacts -``` - -```bash composer install # Install PHP dependencies ``` ## Architecture -This is a Joomla **package** extension (`pkg_mokobackup`) containing three sub-extensions: +Joomla **package** with four sub-extensions: ### com_mokobackup (Component) -- Admin backend for managing backup profiles and backup records +- Admin backend for managing backup profiles and records - Backup engine: `Engine/BackupEngine`, `Engine/DatabaseDumper`, `Engine/FileScanner`, `Engine/Archiver` - Joomla 4/5 MVC: Controllers, Models, Views, Tables - Namespace: `Joomla\Component\MokoBackup\Administrator` -- Database tables: `#__mokobackup_profiles`, `#__mokobackup_records` +- DB tables: `#__mokobackup_profiles`, `#__mokobackup_records` - CLI: `cli/mokobackup.php` for cron-based backups ### plg_system_mokobackup (System Plugin) @@ -49,36 +41,33 @@ This is a Joomla **package** extension (`pkg_mokobackup`) containing three sub-e ### plg_task_mokobackup (Task Plugin) - Integrates with Joomla's Scheduled Tasks (com_scheduler) - Registers "Run Backup Profile" task type -- Each scheduled task selects a backup profile — create multiple tasks for different schedules - Namespace: `Joomla\Plugin\Task\MokoBackup` ### plg_webservices_mokobackup (WebServices Plugin) -- REST API for remote backup management -- Wire-compatible with existing mcp_mokobackup MCP server +- REST API for remote backup management (wire-compatible with mcp_mokobackup) - Endpoints: backup, backups, profiles, download, delete - Namespace: `Joomla\Plugin\WebServices\MokoBackup` ### Database Schema -Two tables: - `#__mokobackup_profiles` — backup profiles (name, description, config JSON, filters JSON) - `#__mokobackup_records` — backup records (profile_id, status, origin, archive path, sizes, timestamps) ## Rules -- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, or `*.min.css`/`*.min.js` -- **Attribution**: use `Authored-by: Moko Consulting` in commits -- **Branch strategy**: develop on `dev`, merge to `main` for release +- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js` +- **Attribution**: `Authored-by: Moko Consulting` +- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`) - **Minification**: handled at build time (CI) -- **Wiki**: documentation lives in the Gitea wiki, not in `docs/` files -- **Standards**: this repo follows [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home) +- **Wiki**: documentation lives in the Gitea wiki, not `docs/` files +- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home) ## Coding Standards - PHP 8.1+ minimum -- Joomla 4/5 DI container pattern: `services/provider.php` > Extension class +- Joomla 4/5 DI container pattern: `services/provider.php` → Extension class - Legacy stub `.php` file required for plugin loader but empty - `SubscriberInterface` for event subscription (not `on*` method naming) -- `bind() > check() > store()` for Table operations (not `save()`) +- `bind() → check() → store()` for Table operations (not `save()`) - Language file placement: site (no `folder`) vs admin (`folder="administrator"`) - SPDX license headers on all PHP files -- 2.52.0 From be52bc048faf042c11dd6556d99b099f158c29f9 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 12:48:42 -0500 Subject: [PATCH 51/81] feat: Joomla-styled standalone installer with provisioning wizard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete rewrite of MokoRestore restore.php generator: - 7-step wizard UI matching Joomla installer look and feel - Pre-flight checks (PHP, extensions, disk space, backup detection) - Archive extraction with password support and config pre-fill - Database connection test and SQL import with error reporting - Create or update configuration.php with fresh Joomla secret - Super admin dropdown with bcrypt password reset - Client provisioning: reset hits, clear sessions/cache/keys/tokens/logs - Zero innerHTML — all DOM built with safe createElement/textContent - Fully self-contained single PHP file, no external dependencies Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../com_mokobackup/src/Engine/MokoRestore.php | 1366 +++++++++++++---- 1 file changed, 1078 insertions(+), 288 deletions(-) diff --git a/source/packages/com_mokobackup/src/Engine/MokoRestore.php b/source/packages/com_mokobackup/src/Engine/MokoRestore.php index 38c2c0a..5321485 100644 --- a/source/packages/com_mokobackup/src/Engine/MokoRestore.php +++ b/source/packages/com_mokobackup/src/Engine/MokoRestore.php @@ -7,17 +7,18 @@ * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE * - * Standalone restore script generator. + * Standalone restore/installer script generator. * * When "Include MokoRestore" is enabled on a profile, the backup archive * is wrapped: * * outer.zip - * ├── restore.php ← Standalone restore script (no Joomla needed) + * ├── restore.php ← Standalone installer (no Joomla needed) * └── site-backup.zip ← The actual site backup * * Upload outer.zip to a blank server, extract, open restore.php in a - * browser, and it handles everything — self-contained site restoration. + * browser, and it handles everything — self-contained site restoration + * with a Joomla-styled wizard interface. */ namespace Joomla\Component\MokoBackup\Administrator\Engine; @@ -56,39 +57,50 @@ class MokoRestore /** * Generate the standalone restore.php script. * - * This is a self-contained PHP file that: - * 1. Provides a web UI for configuration (DB credentials, etc.) + * This is a self-contained PHP file with a Joomla-styled wizard UI that: + * 1. Runs pre-installation checks (PHP, extensions, permissions) * 2. Extracts site-backup.zip to the current directory - * 3. Imports database.sql using the provided credentials - * 4. Updates configuration.php with new settings - * 5. Cleans up after itself + * 3. Imports database.sql using provided credentials + * 4. Creates or updates configuration.php + * 5. Resets super admin password (optional) + * 6. Runs client provisioning tasks (optional) + * 7. Cleans up restore artifacts */ private static function generateRestoreScript(): string { - return <<<'RESTORE_PHP' + $php = self::generateBackend(); + $html = self::generateFrontend(); + + return $php . $html; + } + + /** + * Generate the PHP backend portion of the restore script. + */ + private static function generateBackend(): string + { + return <<<'PHP_BACKEND' false, 'message' => 'Invalid token']); + if (!isset($_POST['token']) || !hash_equals($token, $_POST['token'])) { + echo json_encode(['success' => false, 'message' => 'Invalid security token. Reload the page.']); exit; } - $action = $_POST['action']; + @set_time_limit(0); + @ini_set('max_execution_time', '0'); + @ini_set('memory_limit', '512M'); + @ignore_user_abort(true); try { - switch ($action) { - case 'preflight': - // Override PHP limits for restore - @set_time_limit(0); - @ini_set('max_execution_time', '0'); - @ini_set('memory_limit', '512M'); - @ignore_user_abort(true); - - $checks = []; - $checks['php_version'] = ['value' => PHP_VERSION, 'ok' => version_compare(PHP_VERSION, '8.1', '>=')]; - $checks['zip_ext'] = ['value' => extension_loaded('zip') ? 'Yes' : 'No', 'ok' => extension_loaded('zip')]; - $checks['pdo_mysql'] = ['value' => extension_loaded('pdo_mysql') ? 'Yes' : 'No', 'ok' => extension_loaded('pdo_mysql')]; - $checks['mbstring'] = ['value' => extension_loaded('mbstring') ? 'Yes' : 'No', 'ok' => extension_loaded('mbstring')]; - $checks['backup_exists'] = ['value' => file_exists(BACKUP_FILE) ? 'Yes' : 'No', 'ok' => file_exists(BACKUP_FILE)]; - $checks['writable'] = ['value' => is_writable(RESTORE_DIR) ? 'Yes' : 'No', 'ok' => is_writable(RESTORE_DIR)]; - $checks['max_execution_time'] = ['value' => ini_get('max_execution_time') ?: 'unlimited', 'ok' => true]; - $checks['memory_limit'] = ['value' => ini_get('memory_limit'), 'ok' => true]; - - if (file_exists(BACKUP_FILE)) { - $checks['backup_size'] = ['value' => number_format(filesize(BACKUP_FILE) / 1048576, 2) . ' MB', 'ok' => true]; - } - - $allOk = true; - foreach ($checks as $c) { - if (!$c['ok']) $allOk = false; - } - - echo json_encode(['success' => $allOk, 'checks' => $checks]); - break; - - case 'extract': - $zip = new ZipArchive(); - - if ($zip->open(BACKUP_FILE) !== true) { - throw new RuntimeException('Cannot open backup archive'); - } - - // Set decryption password if provided - $archivePassword = $_POST['archive_password'] ?? ''; - if (!empty($archivePassword)) { - $zip->setPassword($archivePassword); - } - - if (!$zip->extractTo(RESTORE_DIR)) { - $zip->close(); - throw new RuntimeException( - 'Extraction failed. ' . (!empty($archivePassword) ? 'Check the decryption password.' : 'The archive may be encrypted.') - ); - } - - $count = $zip->numFiles; - $zip->close(); - - echo json_encode(['success' => true, 'message' => "Extracted {$count} files"]); - break; - - case 'database': - $host = $_POST['db_host'] ?? 'localhost'; - $name = $_POST['db_name'] ?? ''; - $user = $_POST['db_user'] ?? ''; - $pass = $_POST['db_pass'] ?? ''; - $prefix = $_POST['db_prefix'] ?? 'moko_'; - - if (empty($name) || empty($user)) { - throw new RuntimeException('Database name and user are required'); - } - - $pdo = new PDO( - "mysql:host={$host};dbname={$name};charset=utf8mb4", - $user, - $pass, - [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] - ); - - $sqlFile = RESTORE_DIR . '/database.sql'; - - if (!file_exists($sqlFile)) { - echo json_encode(['success' => true, 'message' => 'No database.sql found — skipped']); - break; - } - - $sql = file_get_contents($sqlFile); - $statements = 0; - $errors = 0; - - // Split on semicolons (simple splitter for our dump format) - $parts = explode(";\n", $sql); - - foreach ($parts as $part) { - $part = trim($part); - - if (empty($part) || strpos($part, '--') === 0) { - continue; - } - - try { - $pdo->exec($part); - $statements++; - } catch (PDOException $e) { - $errors++; - } - } - - echo json_encode([ - 'success' => true, - 'message' => "Executed {$statements} statements" . ($errors ? " ({$errors} warnings)" : ''), - ]); - break; - - case 'config': - $host = $_POST['db_host'] ?? 'localhost'; - $name = $_POST['db_name'] ?? ''; - $user = $_POST['db_user'] ?? ''; - $pass = $_POST['db_pass'] ?? ''; - $prefix = $_POST['db_prefix'] ?? 'moko_'; - $sitename = $_POST['sitename'] ?? 'Restored Site'; - $livesite = $_POST['live_site'] ?? ''; - $tmpPath = $_POST['tmp_path'] ?? RESTORE_DIR . '/tmp'; - $logPath = $_POST['log_path'] ?? RESTORE_DIR . '/administrator/logs'; - - $configFile = RESTORE_DIR . '/configuration.php'; - - if (file_exists($configFile)) { - // Update existing configuration.php - $config = file_get_contents($configFile); - $replacements = [ - '/\$host\s*=\s*\'[^\']*\'/' => "\$host = '{$host}'", - '/\$db\s*=\s*\'[^\']*\'/' => "\$db = '{$name}'", - '/\$user\s*=\s*\'[^\']*\'/' => "\$user = '{$user}'", - '/\$password\s*=\s*\'[^\']*\'/' => "\$password = '{$pass}'", - '/\$dbprefix\s*=\s*\'[^\']*\'/' => "\$dbprefix = '{$prefix}'", - '/\$tmp_path\s*=\s*\'[^\']*\'/' => "\$tmp_path = '{$tmpPath}'", - '/\$log_path\s*=\s*\'[^\']*\'/' => "\$log_path = '{$logPath}'", - ]; - - if (!empty($livesite)) { - $replacements['/\$live_site\s*=\s*\'[^\']*\'/'] = "\$live_site = '{$livesite}'"; - } - - foreach ($replacements as $pattern => $replacement) { - $config = preg_replace($pattern, $replacement, $config); - } - - file_put_contents($configFile, $config); - } - - echo json_encode(['success' => true, 'message' => 'Configuration updated']); - break; - - case 'cleanup': - // Remove restore artifacts - @unlink(RESTORE_DIR . '/database.sql'); - @unlink(BACKUP_FILE); - // Don't delete restore.php here — user does it manually - - echo json_encode(['success' => true, 'message' => 'Cleanup complete. DELETE restore.php manually!']); - break; - - default: - echo json_encode(['success' => false, 'message' => 'Unknown action']); - } + $result = handleAction($_POST['action'], $_POST); + echo json_encode($result); } catch (Throwable $e) { echo json_encode(['success' => false, 'message' => $e->getMessage()]); } @@ -277,164 +133,1098 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { exit; } -// ── HTML UI ──────────────────────────────────────────────────────── +function handleAction(string $action, array $data): array +{ + return match ($action) { + 'preflight' => actionPreflight(), + 'extract' => actionExtract($data), + 'testdb' => actionTestDb($data), + 'database' => actionDatabase($data), + 'config' => actionConfig($data), + 'listAdmins' => actionListAdmins($data), + 'resetAdmin' => actionResetAdmin($data), + 'provision' => actionProvision($data), + 'cleanup' => actionCleanup(), + default => ['success' => false, 'message' => 'Unknown action: ' . $action], + }; +} + +function actionPreflight(): array +{ + $checks = []; + + $checks[] = [ + 'label' => 'PHP Version', + 'value' => PHP_VERSION, + 'ok' => version_compare(PHP_VERSION, '8.1', '>='), + 'hint' => 'Joomla 4/5 requires PHP 8.1+', + ]; + + $checks[] = [ + 'label' => 'ZipArchive Extension', + 'value' => extension_loaded('zip') ? 'Available' : 'Missing', + 'ok' => extension_loaded('zip'), + 'hint' => 'Required to extract backup archives', + ]; + + $checks[] = [ + 'label' => 'PDO MySQL', + 'value' => extension_loaded('pdo_mysql') ? 'Available' : 'Missing', + 'ok' => extension_loaded('pdo_mysql'), + 'hint' => 'Required for database import', + ]; + + $checks[] = [ + 'label' => 'Multibyte String', + 'value' => extension_loaded('mbstring') ? 'Available' : 'Missing', + 'ok' => extension_loaded('mbstring'), + 'hint' => 'Required by Joomla for UTF-8 handling', + ]; + + $checks[] = [ + 'label' => 'JSON Extension', + 'value' => extension_loaded('json') ? 'Available' : 'Missing', + 'ok' => extension_loaded('json'), + 'hint' => 'Required by Joomla', + ]; + + $checks[] = [ + 'label' => 'Backup Archive', + 'value' => file_exists(BACKUP_FILE) ? number_format(filesize(BACKUP_FILE) / 1048576, 2) . ' MB' : 'Not found', + 'ok' => file_exists(BACKUP_FILE), + 'hint' => 'site-backup.zip must be in the same directory as restore.php', + ]; + + $checks[] = [ + 'label' => 'Directory Writable', + 'value' => is_writable(RESTORE_DIR) ? 'Yes' : 'No', + 'ok' => is_writable(RESTORE_DIR), + 'hint' => 'The restore directory must be writable', + ]; + + $freeSpace = @disk_free_space(RESTORE_DIR); + $freeGB = $freeSpace ? round($freeSpace / 1073741824, 1) : 0; + + $checks[] = [ + 'label' => 'Free Disk Space', + 'value' => $freeGB . ' GB', + 'ok' => $freeGB >= 0.5, + 'hint' => 'At least 500 MB free space recommended', + ]; + + $checks[] = [ + 'label' => 'Memory Limit', + 'value' => ini_get('memory_limit') ?: 'Unknown', + 'ok' => true, + 'hint' => 'Informational', + ]; + + $allOk = true; + + foreach ($checks as $c) { + if (!$c['ok']) { + $allOk = false; + } + } + + return ['success' => $allOk, 'checks' => $checks]; +} + +function actionExtract(array $data): array +{ + if (!file_exists(BACKUP_FILE)) { + throw new RuntimeException('Backup file not found: site-backup.zip'); + } + + $zip = new ZipArchive(); + + if ($zip->open(BACKUP_FILE) !== true) { + throw new RuntimeException('Cannot open backup archive'); + } + + $password = trim($data['archive_password'] ?? ''); + + if ($password !== '') { + $zip->setPassword($password); + } + + if (!$zip->extractTo(RESTORE_DIR)) { + $zip->close(); + throw new RuntimeException( + 'Extraction failed. ' . ($password !== '' ? 'Check the decryption password.' : 'The archive may be encrypted — provide a password.') + ); + } + + $count = $zip->numFiles; + $zip->close(); + + // Try to read existing configuration.php for pre-filling + $existingConfig = []; + $configFile = RESTORE_DIR . '/configuration.php'; + + if (is_file($configFile)) { + $content = file_get_contents($configFile); + + if (preg_match('/\$host\s*=\s*\'([^\']*)\'/', $content, $m)) { + $existingConfig['db_host'] = $m[1]; + } + + if (preg_match('/\$db\s*=\s*\'([^\']*)\'/', $content, $m)) { + $existingConfig['db_name'] = $m[1]; + } + + if (preg_match('/\$user\s*=\s*\'([^\']*)\'/', $content, $m)) { + $existingConfig['db_user'] = $m[1]; + } + + if (preg_match('/\$dbprefix\s*=\s*\'([^\']*)\'/', $content, $m)) { + $existingConfig['db_prefix'] = $m[1]; + } + + if (preg_match('/\$sitename\s*=\s*\'([^\']*)\'/', $content, $m)) { + $existingConfig['sitename'] = $m[1]; + } + } + + return [ + 'success' => true, + 'message' => "Extracted {$count} files", + 'config' => $existingConfig, + 'has_db' => is_file(RESTORE_DIR . '/database.sql'), + ]; +} + +function actionTestDb(array $data): array +{ + $host = $data['db_host'] ?? 'localhost'; + $name = $data['db_name'] ?? ''; + $user = $data['db_user'] ?? ''; + $pass = $data['db_pass'] ?? ''; + + if (empty($name) || empty($user)) { + throw new RuntimeException('Database name and user are required'); + } + + $pdo = new PDO( + "mysql:host={$host};dbname={$name};charset=utf8mb4", + $user, + $pass, + [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5] + ); + + $version = $pdo->query('SELECT VERSION()')->fetchColumn(); + + return ['success' => true, 'message' => 'Connected — MySQL ' . $version]; +} + +function actionDatabase(array $data): array +{ + $host = $data['db_host'] ?? 'localhost'; + $name = $data['db_name'] ?? ''; + $user = $data['db_user'] ?? ''; + $pass = $data['db_pass'] ?? ''; + + if (empty($name) || empty($user)) { + throw new RuntimeException('Database name and user are required'); + } + + $sqlFile = RESTORE_DIR . '/database.sql'; + + if (!is_file($sqlFile)) { + return ['success' => true, 'message' => 'No database.sql found — skipped', 'statements' => 0, 'errors' => 0]; + } + + $pdo = new PDO( + "mysql:host={$host};dbname={$name};charset=utf8mb4", + $user, + $pass, + [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] + ); + + $pdo->exec('SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"'); + $pdo->exec("SET time_zone = '+00:00'"); + $pdo->exec('SET FOREIGN_KEY_CHECKS = 0'); + + $sql = file_get_contents($sqlFile); + $parts = explode(";\n", $sql); + $statements = 0; + $errors = 0; + $errorList = []; + + foreach ($parts as $part) { + $part = trim($part); + + if ($part === '' || str_starts_with($part, '--') || str_starts_with($part, 'SET ')) { + continue; + } + + try { + $pdo->exec($part); + $statements++; + } catch (PDOException $e) { + $errors++; + + if (count($errorList) < 5) { + $errorList[] = substr($e->getMessage(), 0, 120); + } + } + } + + $pdo->exec('SET FOREIGN_KEY_CHECKS = 1'); + + return [ + 'success' => true, + 'message' => "Executed {$statements} statements" . ($errors ? " ({$errors} warnings)" : ''), + 'statements' => $statements, + 'errors' => $errors, + 'errorList' => $errorList, + ]; +} + +function actionConfig(array $data): array +{ + $host = $data['db_host'] ?? 'localhost'; + $dbName = $data['db_name'] ?? ''; + $dbUser = $data['db_user'] ?? ''; + $dbPass = $data['db_pass'] ?? ''; + $prefix = $data['db_prefix'] ?? 'moko_'; + $sitename = $data['sitename'] ?? 'Joomla Site'; + $livesite = $data['live_site'] ?? ''; + $tmpPath = RESTORE_DIR . '/tmp'; + $logPath = RESTORE_DIR . '/administrator/logs'; + + $configFile = RESTORE_DIR . '/configuration.php'; + + if (is_file($configFile)) { + // Update existing configuration.php + $config = file_get_contents($configFile); + + $replacements = [ + '/\$host\s*=\s*\'[^\']*\'/' => "\$host = '{$host}'", + '/\$db\s*=\s*\'[^\']*\'/' => "\$db = '{$dbName}'", + '/\$user\s*=\s*\'[^\']*\'/' => "\$user = '{$dbUser}'", + '/\$password\s*=\s*\'[^\']*\'/' => "\$password = '" . addcslashes($dbPass, "'\\") . "'", + '/\$dbprefix\s*=\s*\'[^\']*\'/' => "\$dbprefix = '{$prefix}'", + '/\$tmp_path\s*=\s*\'[^\']*\'/' => "\$tmp_path = '{$tmpPath}'", + '/\$log_path\s*=\s*\'[^\']*\'/' => "\$log_path = '{$logPath}'", + '/\$sitename\s*=\s*\'[^\']*\'/' => "\$sitename = '" . addcslashes($sitename, "'\\") . "'", + '/\$secret\s*=\s*\'[^\']*\'/' => "\$secret = '" . bin2hex(random_bytes(16)) . "'", + ]; + + if ($livesite !== '') { + $replacements['/\$live_site\s*=\s*\'[^\']*\'/'] = "\$live_site = '{$livesite}'"; + } + + foreach ($replacements as $pattern => $replacement) { + $config = preg_replace($pattern, $replacement, $config); + } + + file_put_contents($configFile, $config); + + return ['success' => true, 'message' => 'configuration.php updated with new settings and fresh secret']; + } + + // Create new configuration.php from scratch + $secret = bin2hex(random_bytes(16)); + $newConfig = << true, 'message' => 'configuration.php created from scratch with fresh secret']; +} + +function actionListAdmins(array $data): array +{ + $pdo = getDbConnection($data); + $prefix = $data['db_prefix'] ?? 'moko_'; + + // Find super admin users (group 8 = Super Users in Joomla) + $stmt = $pdo->prepare( + "SELECT u.id, u.name, u.username, u.email + FROM {$prefix}users u + INNER JOIN {$prefix}user_usergroup_map m ON m.user_id = u.id + WHERE m.group_id = 8 + ORDER BY u.id ASC" + ); + $stmt->execute(); + $admins = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if (empty($admins)) { + return ['success' => true, 'admins' => [], 'message' => 'No super admin users found']; + } + + return ['success' => true, 'admins' => $admins]; +} + +function actionResetAdmin(array $data): array +{ + $pdo = getDbConnection($data); + $prefix = $data['db_prefix'] ?? 'moko_'; + $userId = (int) ($data['admin_id'] ?? 0); + $password = $data['new_password'] ?? ''; + + if ($userId < 1 || strlen($password) < 8) { + throw new RuntimeException('Select an admin and enter a password (8+ characters)'); + } + + $hash = password_hash($password, PASSWORD_DEFAULT); + + $stmt = $pdo->prepare("UPDATE {$prefix}users SET password = ?, requireReset = 0 WHERE id = ?"); + $stmt->execute([$hash, $userId]); + + if ($stmt->rowCount() === 0) { + throw new RuntimeException('User not found or password unchanged'); + } + + return ['success' => true, 'message' => 'Admin password updated successfully']; +} + +function actionProvision(array $data): array +{ + $pdo = getDbConnection($data); + $prefix = $data['db_prefix'] ?? 'moko_'; + $tasks = json_decode($data['tasks'] ?? '[]', true) ?: []; + $results = []; + + foreach ($tasks as $task) { + try { + switch ($task) { + case 'reset_hits': + $pdo->exec("UPDATE {$prefix}content SET hits = 0"); + $results[] = 'Content hits reset to 0'; + break; + + case 'clear_sessions': + $pdo->exec("TRUNCATE TABLE {$prefix}session"); + $results[] = 'Sessions cleared'; + break; + + case 'clear_cache': + // Clear Joomla cache tables + foreach (['cache', 'cache_extension'] as $tbl) { + try { + $pdo->exec("TRUNCATE TABLE {$prefix}{$tbl}"); + } catch (PDOException $e) { + // Table may not exist + } + } + $results[] = 'Cache tables cleared'; + break; + + case 'clear_update_keys': + $pdo->exec("UPDATE {$prefix}update_sites SET extra_query = ''"); + $results[] = 'Update site download keys cleared'; + break; + + case 'clear_updates': + $pdo->exec("DELETE FROM {$prefix}updates"); + $results[] = 'Pending updates cleared'; + break; + + case 'clear_api_tokens': + try { + $pdo->exec("TRUNCATE TABLE {$prefix}user_keys"); + $results[] = 'API tokens cleared'; + } catch (PDOException $e) { + $results[] = 'API tokens: table not found (skipped)'; + } + break; + + case 'clear_action_logs': + try { + $pdo->exec("TRUNCATE TABLE {$prefix}action_logs"); + $results[] = 'Action logs cleared'; + } catch (PDOException $e) { + $results[] = 'Action logs: table not found (skipped)'; + } + break; + + case 'clear_mail_queue': + try { + $pdo->exec("TRUNCATE TABLE {$prefix}mail_queue"); + $results[] = 'Mail queue cleared'; + } catch (PDOException $e) { + $results[] = 'Mail queue: table not found (skipped)'; + } + break; + + default: + $results[] = "Unknown task: {$task}"; + } + } catch (Throwable $e) { + $results[] = "Error ({$task}): " . $e->getMessage(); + } + } + + return ['success' => true, 'results' => $results, 'message' => count($results) . ' provisioning tasks completed']; +} + +function actionCleanup(): array +{ + $removed = []; + + foreach (['database.sql', 'site-backup.zip'] as $file) { + $path = RESTORE_DIR . '/' . $file; + + if (is_file($path) && @unlink($path)) { + $removed[] = $file; + } + } + + return [ + 'success' => true, + 'message' => 'Removed: ' . (empty($removed) ? '(none)' : implode(', ', $removed)) + . '. IMPORTANT: Delete restore.php manually!', + ]; +} + +function getDbConnection(array $data): PDO +{ + $host = $data['db_host'] ?? 'localhost'; + $name = $data['db_name'] ?? ''; + $user = $data['db_user'] ?? ''; + $pass = $data['db_pass'] ?? ''; + + return new PDO( + "mysql:host={$host};dbname={$name};charset=utf8mb4", + $user, + $pass, + [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] + ); +} + +// ── Render HTML UI ────────────────────────────────────────────────── ?> +PHP_BACKEND; + } + + /** + * Generate the HTML/CSS/JS frontend of the restore script. + */ + private static function generateFrontend(): string + { + return <<<'HTML_FRONTEND' - - - MokoJoomBackup — Site Restore - + + +MokoRestore — Site Installer + -
-

MokoJoomBackup Restore

-

Standalone site restoration tool

+
+

MokoRestore

+

Standalone Site Installer — MokoJoomBackup

+
-
DELETE this file (restore.php) immediately after restoration is complete!
- -
-

Step 1: Pre-flight Checks

-
- +
+
+ Security: Delete restore.php immediately after installation is complete.
-
-

Step 2: Extract Files

- - - + +
+
1Checks
+
2Extract
+
3Database
+
4Configuration
+
5Admin
+
6Provisioning
+
7Complete
-
-

Step 3: Database Restore

- - - - - - + +
+

Pre-Installation Checks

+

Verify your server meets the requirements for Joomla and MokoRestore.

+
    +
    + + +
    -
    -

    Step 4: Update Configuration

    - - - + +
    +

    Extract Backup

    +

    Extract site-backup.zip into the current directory.

    +
    + + +
    +
    +
    +
    + + +
    -
    -

    Step 5: Cleanup

    - + +
    +

    Database Configuration

    +

    Enter the database credentials for this server. The SQL dump will be imported.

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    -
    Ready. Click "Run Checks" to begin.
    + +
    +

    Site Configuration

    +

    Update or create configuration.php with the correct settings for this server.

    +
    +
    + + +
    Leave blank to auto-detect. Set this if using a reverse proxy or custom domain.
    +
    +
    + A new Joomla secret will be generated automatically for security. +
    +
    +
    + + +
    +
    + + +
    +

    Super Admin Password

    +

    Reset the password for a super administrator account. This is optional but recommended after restoring to a new server.

    +
    + + +
    +
    + + +
    +
    +
    + +
    + + +
    +
    +
    + + +
    +

    Client Provisioning

    +

    Optional cleanup tasks for deploying this backup as a new client site. Check the tasks you want to run.

    +
      +
    • Reset content hitsSet all article hit counters to 0
    • +
    • Clear sessionsRemove all active user sessions
    • +
    • Clear cacheTruncate Joomla cache tables
    • +
    • Clear download keysRemove update site extra_query keys
    • +
    • Clear pending updatesRemove cached update records
    • +
    • Clear API tokensRemove all personal access tokens
    • +
    • Clear action logsRemove admin action log history
    • +
    • Clear mail queueRemove pending outbound emails
    • +
    +
    +
    + +
    + + +
    +
    +
    + + +
    +

    Installation Complete

    +

    Your Joomla site has been restored and configured.

    +
    + Success! The site restoration is complete. +
    +
    + Important: Delete restore.php and site-backup.zip from your server immediately for security. +
    +
    + + Open Joomla Admin + View Site +
    +
    +
    + + +
    +
    + +
    +
    + + -RESTORE_PHP; +HTML_FRONTEND; } } -- 2.52.0 From 42c995853f63ef68db32b90e106530bb1341602d Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 13:32:21 -0500 Subject: [PATCH 52/81] docs: update changelog with all unreleased changes Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009ae6f..64160d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,34 @@ ## [Unreleased] +### Added +- Joomla-styled standalone installer (MokoRestore) with 7-step wizard, admin password reset, and client provisioning +- Placeholder support for backup directories and archive filenames ([host], [date], [profile_name], etc.) +- Archive Name Format field on backup profiles with customizable filename templates +- Interactive directory tree browser for exclude filters (replaces plain text input) +- Backup log viewer modal in backup records list and inline in detail view +- Clickable dashboard status tiles linking to backup records, detail views, and scheduled tasks +- Table exclusion now supports separate Data and Structure checkboxes (backward compatible) +- Tar.gz archive format support +- User group notifications for backup events +- Folder picker field with live server directory browsing +- Default directory dashboard warning when backups are stored inside web root +- Backup log files written alongside archives (.log) +- Backup detail view with checksum, file path, DB size, and embedded log + +### Changed +- Renamed source directory from src/ to source/ per MokoStandards convention +- Dashboard health check shows actual resolved backup directory path from profiles +- Update site post-install notice links to filtered list view (avoids Joomla core bug) + +### Fixed +- Download ERR_INVALID_RESPONSE — flush output buffers before sending file headers +- Backup directory path resolution for absolute paths outside web root +- Schema migrations consolidated to version 01.01.02 (within extension version range) +- Console plugin namespace and quickicon translation keys +- CLI exit codes and SQL schema defaults +- Component Options page (added config.xml) + ## 01.01 — 2026-06-04 ### Added -- 2.52.0 From 62262603a99c3f5c6b0b1a20989ee8a47cb4f250 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 14:07:14 -0500 Subject: [PATCH 53/81] fix: remove double-quotes from pre-release workflow shell commands Act Runner v0.6.x has a quoting bug in Docker mode that double-wraps quoted strings, causing test -f and git clone to fail. Remove all unnecessary double-quotes from the Setup moko-platform tools step. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/workflows/pre-release.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 1a9eeef..04c3ef5 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -64,20 +64,20 @@ jobs: MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting run: | # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) - if [ -f “/opt/moko-platform/cli/version_bump.php” ] && [ -f “/opt/moko-platform/vendor/autoload.php” ]; then - echo “Using pre-installed /opt/moko-platform” - echo “MOKO_CLI=/opt/moko-platform/cli” >> “$GITHUB_ENV” + # Note: avoid double-quoted strings — Act Runner v0.6.x has a quoting bug in Docker mode + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV else - echo “Falling back to fresh clone” + echo Falling back to fresh clone if ! command -v composer &> /dev/null; then sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 fi rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - “https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git” \ - /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet ${CLONE_URL} /tmp/moko-platform-api cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet - echo “MOKO_CLI=/tmp/moko-platform-api/cli” >> “$GITHUB_ENV” + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV fi - name: Detect platform -- 2.52.0 From 8aae10d1eccd163b6a8df399bbcdaca5b2881444 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 19:17:29 +0000 Subject: [PATCH 54/81] chore(version): pre-release bump to 01.01.08-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokobackup/mokobackup.xml | 2 +- source/packages/plg_actionlog_mokobackup/mokobackup.xml | 2 +- source/packages/plg_console_mokobackup/mokobackup.xml | 2 +- source/packages/plg_content_mokobackup/mokobackup.xml | 2 +- source/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- source/packages/plg_system_mokobackup/mokobackup.xml | 2 +- source/packages/plg_task_mokobackup/mokobackup.xml | 2 +- source/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- source/pkg_mokobackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 498e7fc..f6a028c 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.07-dev + 01.01.08-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 825b392..2bb9112 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.07 +# VERSION: 01.01.08 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index 547489b..ef1ebb8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokobackup/mokobackup.xml b/source/packages/com_mokobackup/mokobackup.xml index 9bf599f..1b1d9a9 100644 --- a/source/packages/com_mokobackup/mokobackup.xml +++ b/source/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.07-dev + 01.01.08-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokobackup/mokobackup.xml b/source/packages/plg_actionlog_mokobackup/mokobackup.xml index 343dc1a..f804bda 100644 --- a/source/packages/plg_actionlog_mokobackup/mokobackup.xml +++ b/source/packages/plg_actionlog_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokobackup - 01.01.07-dev + 01.01.08-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokobackup/mokobackup.xml b/source/packages/plg_console_mokobackup/mokobackup.xml index 262f1c7..afc88d9 100644 --- a/source/packages/plg_console_mokobackup/mokobackup.xml +++ b/source/packages/plg_console_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokobackup - 01.01.07-dev + 01.01.08-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokobackup/mokobackup.xml b/source/packages/plg_content_mokobackup/mokobackup.xml index 8548bda..ebed85b 100644 --- a/source/packages/plg_content_mokobackup/mokobackup.xml +++ b/source/packages/plg_content_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokobackup - 01.01.07-dev + 01.01.08-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokobackup/mokobackup.xml b/source/packages/plg_quickicon_mokobackup/mokobackup.xml index 4341a06..bd9c33d 100644 --- a/source/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/source/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.07-dev + 01.01.08-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokobackup/mokobackup.xml b/source/packages/plg_system_mokobackup/mokobackup.xml index 899f088..a724dce 100644 --- a/source/packages/plg_system_mokobackup/mokobackup.xml +++ b/source/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.07-dev + 01.01.08-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokobackup/mokobackup.xml b/source/packages/plg_task_mokobackup/mokobackup.xml index cfb1ffc..8bf9e57 100644 --- a/source/packages/plg_task_mokobackup/mokobackup.xml +++ b/source/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.07-dev + 01.01.08-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokobackup/mokobackup.xml b/source/packages/plg_webservices_mokobackup/mokobackup.xml index 5ae3520..5dd3999 100644 --- a/source/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/source/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.07-dev + 01.01.08-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokobackup.xml b/source/pkg_mokobackup.xml index 86dd028..28f9f3a 100644 --- a/source/pkg_mokobackup.xml +++ b/source/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.07-dev + 01.01.08-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 7c609dd3f12db634675679e977ffff552ade9a36 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 19:32:38 +0000 Subject: [PATCH 55/81] chore(version): pre-release bump to 01.01.09-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokobackup/mokobackup.xml | 2 +- source/packages/plg_actionlog_mokobackup/mokobackup.xml | 2 +- source/packages/plg_console_mokobackup/mokobackup.xml | 2 +- source/packages/plg_content_mokobackup/mokobackup.xml | 2 +- source/packages/plg_quickicon_mokobackup/mokobackup.xml | 2 +- source/packages/plg_system_mokobackup/mokobackup.xml | 2 +- source/packages/plg_task_mokobackup/mokobackup.xml | 2 +- source/packages/plg_webservices_mokobackup/mokobackup.xml | 2 +- source/pkg_mokobackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index f6a028c..1022eb7 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.08-dev + 01.01.09-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 2bb9112..dd54aff 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.08 +# VERSION: 01.01.09 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index ef1ebb8..b91a717 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokobackup/mokobackup.xml b/source/packages/com_mokobackup/mokobackup.xml index 1b1d9a9..bccba9f 100644 --- a/source/packages/com_mokobackup/mokobackup.xml +++ b/source/packages/com_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> com_mokobackup - 01.01.08-dev + 01.01.09-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokobackup/mokobackup.xml b/source/packages/plg_actionlog_mokobackup/mokobackup.xml index f804bda..c45e66c 100644 --- a/source/packages/plg_actionlog_mokobackup/mokobackup.xml +++ b/source/packages/plg_actionlog_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokobackup - 01.01.08-dev + 01.01.09-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokobackup/mokobackup.xml b/source/packages/plg_console_mokobackup/mokobackup.xml index afc88d9..b583077 100644 --- a/source/packages/plg_console_mokobackup/mokobackup.xml +++ b/source/packages/plg_console_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokobackup - 01.01.08-dev + 01.01.09-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokobackup/mokobackup.xml b/source/packages/plg_content_mokobackup/mokobackup.xml index ebed85b..b254f08 100644 --- a/source/packages/plg_content_mokobackup/mokobackup.xml +++ b/source/packages/plg_content_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokobackup - 01.01.08-dev + 01.01.09-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokobackup/mokobackup.xml b/source/packages/plg_quickicon_mokobackup/mokobackup.xml index bd9c33d..928feed 100644 --- a/source/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/source/packages/plg_quickicon_mokobackup/mokobackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokobackup - 01.01.08-dev + 01.01.09-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokobackup/mokobackup.xml b/source/packages/plg_system_mokobackup/mokobackup.xml index a724dce..8d422e9 100644 --- a/source/packages/plg_system_mokobackup/mokobackup.xml +++ b/source/packages/plg_system_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokobackup - 01.01.08-dev + 01.01.09-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokobackup/mokobackup.xml b/source/packages/plg_task_mokobackup/mokobackup.xml index 8bf9e57..523cefd 100644 --- a/source/packages/plg_task_mokobackup/mokobackup.xml +++ b/source/packages/plg_task_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokobackup - 01.01.08-dev + 01.01.09-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokobackup/mokobackup.xml b/source/packages/plg_webservices_mokobackup/mokobackup.xml index 5dd3999..0b7e1e0 100644 --- a/source/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/source/packages/plg_webservices_mokobackup/mokobackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokobackup - 01.01.08-dev + 01.01.09-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokobackup.xml b/source/pkg_mokobackup.xml index 28f9f3a..ce73908 100644 --- a/source/pkg_mokobackup.xml +++ b/source/pkg_mokobackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokobackup - 01.01.08-dev + 01.01.09-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From dc06336b04f721f869989d8bfcbf203fee6a2f9c Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 6 Jun 2026 19:49:15 +0000 Subject: [PATCH 56/81] chore: sync .mokogitea/workflows/pre-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/pre-release.yml | 484 ++++++++++++++------------- 1 file changed, 243 insertions(+), 241 deletions(-) diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 04c3ef5..9615a4e 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -1,241 +1,243 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform -# PATH: /templates/workflows/universal/pre-release.yml.template -# VERSION: 05.01.00 -# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch - -name: "Universal: Pre-Release" - -on: - pull_request: - types: [closed] - branches: - - dev - pull_request_target: - types: [synchronize, opened, reopened] - branches: - - main - workflow_dispatch: - inputs: - stability: - description: 'Pre-release channel' - required: true - type: choice - options: - - development - - alpha - - beta - - release-candidate - -permissions: - contents: write - -env: - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -jobs: - build: - name: "Build Pre-Release (${{ inputs.stability || 'development' }})" - runs-on: release - if: >- - github.event_name == 'workflow_dispatch' || - (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') || - (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main') - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.MOKOGITEA_TOKEN }} - ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }} - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - run: | - # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) - # Note: avoid double-quoted strings — Act Runner v0.6.x has a quoting bug in Docker mode - if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then - echo Using pre-installed /opt/moko-platform - echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV - else - echo Falling back to fresh clone - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - rm -rf /tmp/moko-platform-api - CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git - git clone --depth 1 --branch main --quiet ${CLONE_URL} /tmp/moko-platform-api - cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet - echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV - fi - - - name: Detect platform - id: platform - run: | - php ${MOKO_CLI}/manifest_read.php --path . --github-output - - - name: Resolve metadata and bump version - id: meta - run: | - # Auto-detect stability: RC for PRs targeting main, else use input or default to development - if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then - STABILITY="release-candidate" - else - STABILITY="${{ inputs.stability || 'development' }}" - fi - - case "$STABILITY" in - development) SUFFIX="-dev"; TAG="development" ;; - alpha) SUFFIX="-alpha"; TAG="alpha" ;; - beta) SUFFIX="-beta"; TAG="beta" ;; - release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; - esac - - # Bump version via CLI: patch for dev/alpha/beta, minor for RC - case "$STABILITY" in - release-candidate) BUMP="minor" ;; - *) BUMP="patch" ;; - esac - - php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true - - # Set stability suffix and verify consistency - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") - VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') - - php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true - php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - - # Append suffix for output - if [ -n "$SUFFIX" ]; then - VERSION="${VERSION}${SUFFIX}" - fi - - # Commit version bump - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - git add -A - git diff --cached --quiet || { - git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" - git push origin HEAD 2>&1 - } - - # Auto-detect element via manifest_element.php - php ${MOKO_CLI}/manifest_element.php \ - --path . --version "$VERSION" --stability "$STABILITY" \ - --repo "${GITEA_REPO}" --github-output - - # Read back element outputs - EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) - ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) - [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" - - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" - echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" - echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" - - echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" - - - name: Create release - id: release - run: | - TAG="${{ steps.meta.outputs.tag }}" - VERSION="${{ steps.meta.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_create.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch dev --prerelease - - - name: Update release notes from CHANGELOG.md - run: | - TAG="${{ steps.meta.outputs.tag }}" - VERSION="${{ steps.meta.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) - if [ -f "CHANGELOG.md" ]; then - NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) - [ -z "$NOTES" ] && NOTES="Release ${VERSION}" - else - NOTES="Release ${VERSION}" - fi - - # Update release body via API - RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) - - if [ -n "$RELEASE_ID" ]; then - python3 -c " - import json, urllib.request - body = open('/dev/stdin').read() - payload = json.dumps({'body': body}).encode() - req = urllib.request.Request( - '${API_BASE}/releases/${RELEASE_ID}', - data=payload, method='PATCH', - headers={ - 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', - 'Content-Type': 'application/json' - }) - urllib.request.urlopen(req) - " <<< "$NOTES" - echo "Release notes updated from CHANGELOG.md" - fi - - - name: Build package and upload - id: package - run: | - VERSION="${{ steps.meta.outputs.version }}" - TAG="${{ steps.meta.outputs.tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_package.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp || true - - # updates.xml is generated dynamically by MokoGitea license server - # No need to build, commit, or sync updates.xml from workflows - - - name: "Delete lesser pre-release channels (cascade)" - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - php ${MOKO_CLI}/release_cascade.php \ - --stability "${{ steps.meta.outputs.stability }}" \ - --token "${TOKEN}" \ - --api-base "${API_BASE}" - - - name: Summary - if: always() - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - ZIP_NAME="${{ steps.meta.outputs.zip_name }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY - echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY - echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /templates/workflows/universal/pre-release.yml.template +# VERSION: 05.01.00 +# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch + +name: "Universal: Pre-Release" + +on: + pull_request: + types: [closed] + branches: + - dev + pull_request_target: + types: [synchronize, opened, reopened] + branches: + - main + workflow_dispatch: + inputs: + stability: + description: 'Pre-release channel' + required: true + type: choice + options: + - development + - alpha + - beta + - release-candidate + +permissions: + contents: write + +env: + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} + GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} + +jobs: + build: + name: "Build Pre-Release (${{ inputs.stability || 'development' }})" + runs-on: release + if: >- + github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') || + (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main') + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.MOKOGITEA_TOKEN }} + ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }} + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + fi + + - name: Detect platform + id: platform + run: | + php ${MOKO_CLI}/manifest_read.php --path . --github-output + + - name: Resolve metadata and bump version + id: meta + run: | + # Auto-detect stability: RC for PRs targeting main, else use input or default to development + if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then + STABILITY="release-candidate" + else + STABILITY="${{ inputs.stability || 'development' }}" + fi + + case "$STABILITY" in + development) SUFFIX="-dev"; TAG="development" ;; + alpha) SUFFIX="-alpha"; TAG="alpha" ;; + beta) SUFFIX="-beta"; TAG="beta" ;; + release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; + esac + + # Bump version via CLI: patch for dev/alpha/beta, minor for RC + case "$STABILITY" in + release-candidate) BUMP="minor" ;; + *) BUMP="patch" ;; + esac + + php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true + + # Set stability suffix and verify consistency + VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") + VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') + + php ${MOKO_CLI}/version_set_platform.php \ + --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true + php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true + + # Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml + php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true + + # Append suffix for output + if [ -n "$SUFFIX" ]; then + VERSION="${VERSION}${SUFFIX}" + fi + + # Commit version bump + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + git add -A + git diff --cached --quiet || { + git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" + git push origin HEAD 2>&1 + } + + # Auto-detect element via manifest_element.php + php ${MOKO_CLI}/manifest_element.php \ + --path . --version "$VERSION" --stability "$STABILITY" \ + --repo "${GITEA_REPO}" --github-output + + # Read back element outputs + EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') + [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" + + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" + echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" + echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" + + echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" + + - name: Create release + id: release + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_create.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --branch dev --prerelease + + - name: Update release notes from CHANGELOG.md + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) + if [ -f "CHANGELOG.md" ]; then + NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) + [ -z "$NOTES" ] && NOTES="Release ${VERSION}" + else + NOTES="Release ${VERSION}" + fi + + # Update release body via API + RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + + if [ -n "$RELEASE_ID" ]; then + python3 -c " + import json, urllib.request + body = open('/dev/stdin').read() + payload = json.dumps({'body': body}).encode() + req = urllib.request.Request( + '${API_BASE}/releases/${RELEASE_ID}', + data=payload, method='PATCH', + headers={ + 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', + 'Content-Type': 'application/json' + }) + urllib.request.urlopen(req) + " <<< "$NOTES" + echo "Release notes updated from CHANGELOG.md" + fi + + - name: Build package and upload + id: package + run: | + VERSION="${{ steps.meta.outputs.version }}" + TAG="${{ steps.meta.outputs.tag }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_package.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --output /tmp || true + + # updates.xml is generated dynamically by MokoGitea license server + # No need to build, commit, or sync updates.xml from workflows + + - name: "Delete lesser pre-release channels (cascade)" + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + php ${MOKO_CLI}/release_cascade.php \ + --stability "${{ steps.meta.outputs.stability }}" \ + --token "${TOKEN}" \ + --api-base "${API_BASE}" + + - name: Summary + if: always() + run: | + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + ZIP_NAME="${{ steps.meta.outputs.zip_name }}" + SHA256="${{ steps.package.outputs.sha256_zip }}" + echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY + echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY + echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY -- 2.52.0 From a7458a693e4527d1428114a9c030d997e7aead7f Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 6 Jun 2026 19:51:46 +0000 Subject: [PATCH 57/81] chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/auto-release.yml | 609 ++++++++++++++------------ 1 file changed, 324 insertions(+), 285 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 141fdcc..ca40435 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -1,285 +1,324 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform -# PATH: /templates/workflows/universal/auto-release.yml.template -# VERSION: 05.00.00 -# BRIEF: Universal build & release � detects platform from manifest.xml -# -# +========================================================================+ -# | UNIVERSAL BUILD & RELEASE PIPELINE | -# +========================================================================+ -# | | -# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | -# | | -# | Platform-specific: | -# | joomla: XML manifest, updates.xml, type-prefixed packages | -# | dolibarr: mod*.class.php, update.txt, dev version reset | -# | generic: README-only, no update stream | -# | | -# +========================================================================+ - -name: "Universal: Build & Release" - -on: - pull_request: - types: [opened, closed] - branches: - - main - workflow_dispatch: - inputs: - action: - description: 'Action to perform' - required: false - type: choice - default: release - options: - - release - - promote-rc - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -permissions: - contents: write - -jobs: - # ── PR Opened → Rename branch to RC and build RC release ───────────────────── - promote-rc: - name: Promote to RC - runs-on: release - if: >- - (github.event.action == 'opened' && github.event.pull_request.merged != true) || - (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 1 - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - run: | - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - - - name: Rename branch to rc - run: | - php /tmp/moko-platform-api/cli/branch_rename.php \ - --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ - --pr "${{ github.event.pull_request.number }}" - - - name: Checkout rc and configure git - run: | - git fetch origin rc - git checkout rc - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - - name: Publish RC release - run: | - php /tmp/moko-platform-api/cli/release_publish.php \ - --path . --stability rc --bump minor --branch rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --skip-update-stream - - - name: Summary - if: always() - run: | - echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY - - # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── - release: - name: Build & Release Pipeline - runs-on: release - if: >- - github.event.pull_request.merged == true || - (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 0 - - - name: Configure git for bot pushes - run: | - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - - name: Check for merge conflict markers - run: | - CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) - if [ -n "$CONFLICTS" ]; then - echo "::error::Merge conflict markers found — aborting release" - echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - exit 1 - fi - echo "No conflict markers found" - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' - run: | - # Ensure PHP + Composer are available - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - - - - name: "Publish stable release" - run: | - php /tmp/moko-platform-api/cli/release_publish.php \ - --path . --stability stable --bump minor --branch main \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --skip-update-stream - - # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - - name: "Step 9: Mirror release to GitHub" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_mirror.php \ - --version "$VERSION" --tag "$RELEASE_TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ - --branch main 2>&1 || true - echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY - - # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- - - name: "Step 10: Push main to GitHub mirror" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) - GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) - git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ - git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" - git fetch origin main --depth=1 - git push github origin/main:refs/heads/main --force 2>/dev/null \ - && echo "main branch pushed to GitHub mirror" \ - || echo "WARNING: GitHub mirror push failed" - - - name: "Step 11: Delete rc branch and recreate dev from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - # Delete rc branch (ephemeral — created by promote-rc) - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/rc" 2>/dev/null \ - && echo "Deleted rc branch" || echo "rc branch not found" - - # Delete dev branch - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" - - # Recreate dev from main (now includes version bump + changelog promotion) - curl -sf -X POST -H "Authorization: token ${TOKEN}" \ - -H "Content-Type: application/json" \ - "${API_BASE}/branches" \ - -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" - - echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY - - - name: "Step 12: Create version branch from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - BRANCH_NAME="version/${VERSION}" - MAIN_SHA=$(git rev-parse HEAD) - - # Delete old version branch if it exists (same version re-release) - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" - - # Create version/XX.YY.ZZ from main - curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" - - echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY - - - - # -- Dolibarr post-release: Reset dev version ----------------------------- - - name: "Post-release: Reset dev version" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/version_reset_dev.php \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ - --branch dev --path . 2>&1 || true - - # -- Summary -------------------------------------------------------------- - - name: Pipeline Summary - if: always() - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - PLATFORM="${{ steps.platform.outputs.platform }}" - if [ "${{ steps.version.outputs.skip }}" = "true" ]; then - echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY - echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY - elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then - echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY - echo "|------|--------|" >> $GITHUB_STEP_SUMMARY - echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY - fi +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform +# PATH: /templates/workflows/universal/auto-release.yml.template +# VERSION: 05.00.00 +# BRIEF: Universal build & release � detects platform from manifest.xml +# +# +========================================================================+ +# | UNIVERSAL BUILD & RELEASE PIPELINE | +# +========================================================================+ +# | | +# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | +# | | +# | Platform-specific: | +# | joomla: XML manifest, type-prefixed packages | +# | dolibarr: mod*.class.php, update.txt, dev version reset | +# | generic: README-only, no update stream | +# | | +# +========================================================================+ + +name: "Universal: Build & Release" + +on: + pull_request: + types: [opened, closed] + branches: + - main + workflow_dispatch: + inputs: + action: + description: 'Action to perform' + required: false + type: choice + default: release + options: + - release + - promote-rc + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} + GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} + +permissions: + contents: write + +jobs: + # ── PR Opened → Rename branch to RC and build RC release ───────────────────── + promote-rc: + name: Promote to RC + runs-on: release + if: >- + (github.event.action == 'opened' && github.event.pull_request.merged != true) || + (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 1 + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + fi + + - name: Rename branch to rc + run: | + php ${MOKO_CLI}/branch_rename.php \ + --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ + --pr "${{ github.event.pull_request.number }}" + + - name: Checkout rc and configure git + run: | + git fetch origin rc + git checkout rc + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + + - name: Publish RC release + run: | + php ${MOKO_CLI}/release_publish.php \ + --path . --stability rc --bump minor --branch rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" + + - name: Summary + if: always() + run: | + echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY + echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY + + # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── + release: + name: Build & Release Pipeline + runs-on: release + if: >- + github.event.pull_request.merged == true || + (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 0 + + - name: Configure git for bot pushes + run: | + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + + - name: Check for merge conflict markers + run: | + CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) + if [ -n "$CONFLICTS" ]; then + echo "::error::Merge conflict markers found — aborting release" + echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "No conflict markers found" + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' + run: | + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + fi + + - name: "Publish stable release" + run: | + php ${MOKO_CLI}/release_publish.php \ + --path . --stability stable --bump minor --branch main \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" + + - name: Update release notes from CHANGELOG.md + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Extract [Unreleased] section from changelog + if [ -f "CHANGELOG.md" ]; then + NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) + [ -z "$NOTES" ] && NOTES="Stable release" + else + NOTES="Stable release" + fi + + # Update release body via API + RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + + if [ -n "$RELEASE_ID" ]; then + python3 -c " + import json, urllib.request + body = open('/dev/stdin').read() + payload = json.dumps({'body': body}).encode() + req = urllib.request.Request( + '${API_BASE}/releases/${RELEASE_ID}', + data=payload, method='PATCH', + headers={ + 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', + 'Content-Type': 'application/json' + }) + urllib.request.urlopen(req) + " <<< "$NOTES" + echo "Release notes updated from CHANGELOG.md" + fi + + # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- + - name: "Step 9: Mirror release to GitHub" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + RELEASE_TAG="${{ steps.version.outputs.release_tag }}" + GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_mirror.php \ + --version "$VERSION" --tag "$RELEASE_TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ + --branch main 2>&1 || true + echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY + + # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- + - name: "Step 10: Push main to GitHub mirror" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" + GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) + GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) + git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ + git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" + git fetch origin main --depth=1 + git push github origin/main:refs/heads/main --force 2>/dev/null \ + && echo "main branch pushed to GitHub mirror" \ + || echo "WARNING: GitHub mirror push failed" + + - name: "Step 11: Delete rc branch and recreate dev from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + # Delete rc branch (ephemeral — created by promote-rc) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/rc" 2>/dev/null \ + && echo "Deleted rc branch" || echo "rc branch not found" + + # Delete dev branch + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" + + # Recreate dev from main (now includes version bump + changelog promotion) + curl -sf -X POST -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${API_BASE}/branches" \ + -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" + + echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY + + - name: "Step 12: Create version branch from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + BRANCH_NAME="version/${VERSION}" + MAIN_SHA=$(git rev-parse HEAD) + + # Delete old version branch if it exists (same version re-release) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" + + # Create version/XX.YY.ZZ from main + curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" + + echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY + + + + # -- Dolibarr post-release: Reset dev version ----------------------------- + - name: "Post-release: Reset dev version" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/version_reset_dev.php \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ + --branch dev --path . 2>&1 || true + + # -- Summary -------------------------------------------------------------- + - name: Pipeline Summary + if: always() + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + PLATFORM="${{ steps.platform.outputs.platform }}" + if [ "${{ steps.version.outputs.skip }}" = "true" ]; then + echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY + echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY + elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then + echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY + echo "|------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY + fi -- 2.52.0 From 37d325f1ed7e7044689ac7e545a402aeb63aaf2c Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 14:52:27 -0500 Subject: [PATCH 58/81] chore: rename mokobackup to mokojoombackup throughout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full rename of all extension elements, namespaces, language keys, database tables, file names, and directory names: - pkg_mokobackup → pkg_mokojoombackup - com_mokobackup → com_mokojoombackup - plg_*_mokobackup → plg_*_mokojoombackup - MokoBackup namespace → MokoJoomBackup - #__mokobackup_ tables → #__mokojoombackup_ - COM_MOKOBACKUP_ keys → COM_MOKOJOOMBACKUP_ Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/CLAUDE.md | 28 +- CHANGELOG.md | 12 +- Makefile | 4 +- source/language/en-GB/pkg_mokobackup.sys.ini | 10 - .../language/en-GB/pkg_mokojoombackup.sys.ini | 10 + source/language/en-US/pkg_mokobackup.sys.ini | 10 - .../language/en-US/pkg_mokojoombackup.sys.ini | 10 + .../packages/com_mokobackup/forms/backup.xml | 15 - .../com_mokobackup/forms/filter_backups.xml | 47 --- .../packages/com_mokobackup/forms/profile.xml | 373 ------------------ .../language/en-GB/com_mokobackup.ini | 264 ------------- .../language/en-GB/com_mokobackup.sys.ini | 10 - .../language/en-US/com_mokobackup.ini | 69 ---- .../language/en-US/com_mokobackup.sys.ini | 10 - .../com_mokobackup/sql/uninstall.mysql.sql | 2 - .../sql/updates/mysql/01.01.01.sql | 1 - .../sql/updates/mysql/01.01.02.sql | 12 - .../cli/mokojoombackup.php} | 6 +- .../com_mokojoombackup}/api/index.html | 0 .../api/src/Controller/BackupsController.php | 12 +- .../api/src/Controller/index.html | 0 .../api/src/View/Backups/JsonapiView.php | 4 +- .../api/src/View/Backups/index.html | 0 .../api/src/View/index.html | 0 .../com_mokojoombackup}/api/src/index.html | 0 .../com_mokojoombackup}/cli/index.html | 0 .../com_mokojoombackup}/config.xml | 48 +-- .../com_mokojoombackup/forms/backup.xml | 15 + .../forms/filter_backups.xml | 47 +++ .../forms/filter_profiles.xml | 6 +- .../com_mokojoombackup}/forms/index.html | 0 .../com_mokojoombackup/forms/profile.xml | 373 ++++++++++++++++++ .../com_mokojoombackup}/index.html | 0 .../language/en-GB/index.html | 0 .../language/en-US/index.html | 0 .../com_mokojoombackup}/language/index.html | 0 .../com_mokojoombackup}/services/index.html | 0 .../com_mokojoombackup}/services/provider.php | 10 +- .../com_mokojoombackup}/sql/index.html | 0 .../com_mokojoombackup}/sql/install.mysql.sql | 12 +- .../com_mokojoombackup}/sql/mysql/index.html | 0 .../sql/uninstall.mysql.sql | 2 + .../sql/updates/index.html | 0 .../sql/updates/mysql/01.00.00.sql | 0 .../sql/updates/mysql/01.01.01.sql | 1 + .../sql/updates/mysql/01.01.02.sql | 12 + .../sql/updates/mysql/index.html | 0 .../src/Controller/AjaxController.php | 8 +- .../src/Controller/BackupController.php | 6 +- .../src/Controller/BackupsController.php | 44 +-- .../src/Controller/DisplayController.php | 4 +- .../src/Controller/ProfileController.php | 6 +- .../src/Controller/ProfilesController.php | 14 +- .../src/Controller/index.html | 0 .../src/Engine/AkeebaImporter.php | 16 +- .../src/Engine/ArchiverInterface.php | 4 +- .../src/Engine/BackupEngine.php | 22 +- .../src/Engine/DatabaseDumper.php | 4 +- .../src/Engine/DatabaseImporter.php | 8 +- .../src/Engine/DifferentialScanner.php | 4 +- .../src/Engine/FileRestorer.php | 4 +- .../src/Engine/FileScanner.php | 4 +- .../src/Engine/FtpUploader.php | 4 +- .../src/Engine/GoogleDriveUploader.php | 4 +- .../src/Engine/JpaUnarchiver.php | 4 +- .../src/Engine/MokoRestore.php | 6 +- .../src/Engine/NotificationSender.php | 8 +- .../src/Engine/PlaceholderResolver.php | 4 +- .../src/Engine/RemoteUploaderInterface.php | 4 +- .../src/Engine/RestoreEngine.php | 8 +- .../src/Engine/S3Uploader.php | 4 +- .../src/Engine/SteppedBackupEngine.php | 20 +- .../src/Engine/SteppedSession.php | 6 +- .../src/Engine/TarGzArchiver.php | 4 +- .../src/Engine/ZipArchiver.php | 4 +- .../com_mokojoombackup}/src/Engine/index.html | 0 .../src/Extension/MokoBackupComponent.php | 6 +- .../src/Extension/index.html | 0 .../src/Field/DatabaseTablesField.php | 12 +- .../src/Field/DirectoryFilterField.php | 12 +- .../src/Field/ExcludeListField.php | 4 +- .../src/Field/FolderPickerField.php | 12 +- .../src/Model/BackupModel.php | 8 +- .../src/Model/BackupsModel.php | 8 +- .../src/Model/DashboardModel.php | 26 +- .../src/Model/ProfileModel.php | 8 +- .../src/Model/ProfilesModel.php | 6 +- .../com_mokojoombackup}/src/Model/index.html | 0 .../src/Table/BackupTable.php | 6 +- .../src/Table/ProfileTable.php | 6 +- .../com_mokojoombackup}/src/Table/index.html | 0 .../src/View/Backup/HtmlView.php | 8 +- .../src/View/Backup/index.html | 0 .../src/View/Backups/HtmlView.php | 24 +- .../src/View/Backups/index.html | 0 .../src/View/Dashboard/HtmlView.php | 10 +- .../src/View/Profile/HtmlView.php | 6 +- .../src/View/Profile/index.html | 0 .../src/View/Profiles/HtmlView.php | 12 +- .../src/View/Profiles/index.html | 0 .../com_mokojoombackup}/src/View/index.html | 0 .../com_mokojoombackup}/src/index.html | 0 .../tmpl/backup/default.php | 32 +- .../tmpl/backup/index.html | 0 .../tmpl/backups/default.php | 50 +-- .../tmpl/backups/index.html | 0 .../tmpl/dashboard/default.php | 60 +-- .../com_mokojoombackup}/tmpl/index.html | 0 .../com_mokojoombackup}/tmpl/profile/edit.php | 14 +- .../tmpl/profile/index.html | 0 .../tmpl/profiles/default.php | 14 +- .../tmpl/profiles/index.html | 0 .../language/en-GB/com_mokojoombackup.ini | 264 +++++++++++++ .../language/en-GB/com_mokojoombackup.sys.ini | 10 + .../language/en-US/com_mokojoombackup.ini | 69 ++++ .../language/en-US/com_mokojoombackup.sys.ini | 10 + .../mokojoombackup.xml} | 22 +- .../en-GB/plg_actionlog_mokobackup.ini | 9 - .../en-GB/plg_actionlog_mokobackup.sys.ini | 3 - .../en-US/plg_actionlog_mokobackup.ini | 9 - .../en-US/plg_actionlog_mokobackup.sys.ini | 3 - .../en-GB/plg_actionlog_mokojoombackup.ini | 9 + .../plg_actionlog_mokojoombackup.sys.ini | 3 + .../en-US/plg_actionlog_mokojoombackup.ini | 9 + .../plg_actionlog_mokojoombackup.sys.ini | 3 + .../mokojoombackup.php | 11 + .../mokojoombackup.xml} | 16 +- .../services/provider.php | 8 +- .../src/Extension/MokoBackupActionlog.php | 42 +- .../language/en-GB/plg_console_mokobackup.ini | 3 - .../en-GB/plg_console_mokobackup.sys.ini | 3 - .../language/en-US/plg_console_mokobackup.ini | 3 - .../en-US/plg_console_mokobackup.sys.ini | 3 - .../en-GB/plg_console_mokojoombackup.ini | 3 + .../en-GB/plg_console_mokojoombackup.sys.ini | 3 + .../en-US/plg_console_mokojoombackup.ini | 3 + .../en-US/plg_console_mokojoombackup.sys.ini | 3 + .../mokojoombackup.php} | 2 +- .../mokojoombackup.xml} | 16 +- .../services/provider.php | 8 +- .../src/Command/CleanupCommand.php | 16 +- .../src/Command/ListCommand.php | 10 +- .../src/Command/ProfilesCommand.php | 8 +- .../src/Command/RestoreCommand.php | 12 +- .../src/Command/RunCommand.php | 10 +- .../src/Extension/MokoBackupConsole.php | 16 +- .../language/en-GB/plg_content_mokobackup.ini | 9 - .../en-GB/plg_content_mokobackup.sys.ini | 3 - .../language/en-US/plg_content_mokobackup.ini | 9 - .../en-US/plg_content_mokobackup.sys.ini | 3 - .../en-GB/plg_content_mokojoombackup.ini | 9 + .../en-GB/plg_content_mokojoombackup.sys.ini | 3 + .../en-US/plg_content_mokojoombackup.ini | 9 + .../en-US/plg_content_mokojoombackup.sys.ini | 3 + .../mokojoombackup.php} | 2 +- .../mokojoombackup.xml} | 30 +- .../services/provider.php | 8 +- .../src/Extension/MokoBackupContent.php | 14 +- .../en-GB/plg_quickicon_mokobackup.ini | 6 - .../en-GB/plg_quickicon_mokobackup.sys.ini | 2 - .../en-US/plg_quickicon_mokobackup.ini | 6 - .../en-US/plg_quickicon_mokobackup.sys.ini | 2 - .../en-GB/plg_quickicon_mokojoombackup.ini | 6 + .../plg_quickicon_mokojoombackup.sys.ini | 2 + .../en-US/plg_quickicon_mokojoombackup.ini | 6 + .../plg_quickicon_mokojoombackup.sys.ini | 2 + .../mokojoombackup.php} | 0 .../mokojoombackup.xml} | 14 +- .../plg_quickicon_mokojoombackup}/index.html | 0 .../language/en-GB/index.html | 0 .../language/en-US/index.html | 0 .../language/index.html | 0 .../services/index.html | 0 .../services/provider.php | 6 +- .../src/Extension/MokoBackupQuickicon.php | 24 +- .../src/Extension/index.html | 0 .../src/index.html | 0 .../language/en-GB/plg_system_mokobackup.ini | 9 - .../en-GB/plg_system_mokobackup.sys.ini | 3 - .../language/en-US/plg_system_mokobackup.ini | 9 - .../en-US/plg_system_mokobackup.sys.ini | 3 - .../en-GB/plg_system_mokojoombackup.ini | 9 + .../en-GB/plg_system_mokojoombackup.sys.ini | 3 + .../en-US/plg_system_mokojoombackup.ini | 9 + .../en-US/plg_system_mokojoombackup.sys.ini | 3 + .../mokojoombackup.php} | 2 +- .../mokojoombackup.xml} | 28 +- .../plg_system_mokojoombackup}/index.html | 0 .../language/en-GB/index.html | 0 .../language/en-US/index.html | 0 .../language/index.html | 0 .../services/index.html | 0 .../services/provider.php | 8 +- .../src/Extension/MokoBackup.php | 20 +- .../src/Extension/index.html | 0 .../plg_system_mokojoombackup}/src/index.html | 0 .../language/en-GB/plg_task_mokobackup.ini | 12 - .../en-GB/plg_task_mokobackup.sys.ini | 3 - .../language/en-US/plg_task_mokobackup.ini | 8 - .../en-US/plg_task_mokobackup.sys.ini | 3 - .../plg_task_mokobackup/mokobackup.php | 11 - .../plg_task_mokobackup/services/provider.php | 37 -- .../en-GB/plg_task_mokojoombackup.ini | 12 + .../en-GB/plg_task_mokojoombackup.sys.ini | 3 + .../en-US/plg_task_mokojoombackup.ini | 8 + .../en-US/plg_task_mokojoombackup.sys.ini | 3 + .../mokojoombackup.php} | 2 +- .../mokojoombackup.xml} | 16 +- .../plg_task_mokojoombackup}/forms/index.html | 0 .../forms/run_profile.xml | 8 +- .../plg_task_mokojoombackup}/index.html | 0 .../language/en-GB/index.html | 0 .../language/en-US/index.html | 0 .../language/index.html | 0 .../services/index.html | 0 .../services/provider.php | 8 +- .../src/Extension/MokoBackupTask.php | 16 +- .../src/Extension/index.html | 0 .../plg_task_mokojoombackup}/src/index.html | 0 .../en-GB/plg_webservices_mokobackup.ini | 3 - .../en-GB/plg_webservices_mokobackup.sys.ini | 3 - .../en-US/plg_webservices_mokobackup.ini | 3 - .../en-US/plg_webservices_mokobackup.sys.ini | 3 - .../plg_webservices_mokobackup/mokobackup.php | 11 - .../en-GB/plg_webservices_mokojoombackup.ini | 3 + .../plg_webservices_mokojoombackup.sys.ini | 3 + .../en-US/plg_webservices_mokojoombackup.ini | 3 + .../plg_webservices_mokojoombackup.sys.ini | 3 + .../mokojoombackup.php | 11 + .../mokojoombackup.xml} | 16 +- .../index.html | 0 .../language/en-GB/index.html | 0 .../language/en-US/index.html | 0 .../language/index.html | 0 .../services/index.html | 0 .../services/provider.php | 37 ++ .../src/Extension/MokoBackupWebServices.php | 30 +- .../src/Extension/index.html | 0 .../src/index.html | 0 ..._mokobackup.xml => pkg_mokojoombackup.xml} | 24 +- source/script.php | 26 +- 241 files changed, 1621 insertions(+), 1621 deletions(-) delete mode 100644 source/language/en-GB/pkg_mokobackup.sys.ini create mode 100644 source/language/en-GB/pkg_mokojoombackup.sys.ini delete mode 100644 source/language/en-US/pkg_mokobackup.sys.ini create mode 100644 source/language/en-US/pkg_mokojoombackup.sys.ini delete mode 100644 source/packages/com_mokobackup/forms/backup.xml delete mode 100644 source/packages/com_mokobackup/forms/filter_backups.xml delete mode 100644 source/packages/com_mokobackup/forms/profile.xml delete mode 100644 source/packages/com_mokobackup/language/en-GB/com_mokobackup.ini delete mode 100644 source/packages/com_mokobackup/language/en-GB/com_mokobackup.sys.ini delete mode 100644 source/packages/com_mokobackup/language/en-US/com_mokobackup.ini delete mode 100644 source/packages/com_mokobackup/language/en-US/com_mokobackup.sys.ini delete mode 100644 source/packages/com_mokobackup/sql/uninstall.mysql.sql delete mode 100644 source/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql delete mode 100644 source/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql rename source/packages/{com_mokobackup/cli/mokobackup.php => com_mokojoombackup/cli/mokojoombackup.php} (89%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/api/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/api/src/Controller/BackupsController.php (84%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/api/src/Controller/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/api/src/View/Backups/JsonapiView.php (90%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/api/src/View/Backups/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/api/src/View/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/api/src/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/cli/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/config.xml (52%) create mode 100644 source/packages/com_mokojoombackup/com_mokojoombackup/forms/backup.xml create mode 100644 source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_backups.xml rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/forms/filter_profiles.xml (81%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/forms/index.html (100%) create mode 100644 source/packages/com_mokojoombackup/com_mokojoombackup/forms/profile.xml rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/language/en-GB/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/language/en-US/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/language/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/services/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/services/provider.php (82%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/sql/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/sql/install.mysql.sql (93%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/sql/mysql/index.html (100%) create mode 100644 source/packages/com_mokojoombackup/com_mokojoombackup/sql/uninstall.mysql.sql rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/sql/updates/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/sql/updates/mysql/01.00.00.sql (100%) create mode 100644 source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.01.sql create mode 100644 source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.02.sql rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/sql/updates/mysql/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Controller/AjaxController.php (95%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Controller/BackupController.php (70%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Controller/BackupsController.php (67%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Controller/DisplayController.php (80%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Controller/ProfileController.php (70%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Controller/ProfilesController.php (85%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Controller/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/AkeebaImporter.php (96%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/ArchiverInterface.php (89%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/BackupEngine.php (95%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/DatabaseDumper.php (98%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/DatabaseImporter.php (92%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/DifferentialScanner.php (96%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/FileRestorer.php (96%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/FileScanner.php (96%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/FtpUploader.php (97%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/GoogleDriveUploader.php (98%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/JpaUnarchiver.php (98%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/MokoRestore.php (99%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/NotificationSender.php (94%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/PlaceholderResolver.php (97%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/RemoteUploaderInterface.php (89%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/RestoreEngine.php (96%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/S3Uploader.php (99%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/SteppedBackupEngine.php (96%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/SteppedSession.php (96%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/TarGzArchiver.php (93%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/ZipArchiver.php (90%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Engine/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Extension/MokoBackupComponent.php (68%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Extension/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Field/DatabaseTablesField.php (90%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Field/DirectoryFilterField.php (95%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Field/ExcludeListField.php (97%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Field/FolderPickerField.php (94%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Model/BackupModel.php (78%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Model/BackupsModel.php (88%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Model/DashboardModel.php (86%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Model/ProfileModel.php (78%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Model/ProfilesModel.php (90%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Model/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Table/BackupTable.php (85%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Table/ProfileTable.php (83%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/Table/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/Backup/HtmlView.php (78%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/Backup/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/Backups/HtmlView.php (82%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/Backups/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/Dashboard/HtmlView.php (77%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/Profile/HtmlView.php (82%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/Profile/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/Profiles/HtmlView.php (79%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/Profiles/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/View/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/src/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/backup/default.php (70%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/backup/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/backups/default.php (82%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/backups/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/dashboard/default.php (76%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/profile/edit.php (84%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/profile/index.html (100%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/profiles/default.php (81%) rename source/packages/{com_mokobackup => com_mokojoombackup/com_mokojoombackup}/tmpl/profiles/index.html (100%) create mode 100644 source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.ini create mode 100644 source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.sys.ini create mode 100644 source/packages/com_mokojoombackup/language/en-US/com_mokojoombackup.ini create mode 100644 source/packages/com_mokojoombackup/language/en-US/com_mokojoombackup.sys.ini rename source/packages/{com_mokobackup/mokobackup.xml => com_mokojoombackup/mokojoombackup.xml} (63%) delete mode 100644 source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini delete mode 100644 source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini delete mode 100644 source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini delete mode 100644 source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini create mode 100644 source/packages/plg_actionlog_mokojoombackup/language/en-GB/plg_actionlog_mokojoombackup.ini create mode 100644 source/packages/plg_actionlog_mokojoombackup/language/en-GB/plg_actionlog_mokojoombackup.sys.ini create mode 100644 source/packages/plg_actionlog_mokojoombackup/language/en-US/plg_actionlog_mokojoombackup.ini create mode 100644 source/packages/plg_actionlog_mokojoombackup/language/en-US/plg_actionlog_mokojoombackup.sys.ini create mode 100644 source/packages/plg_actionlog_mokojoombackup/mokojoombackup.php rename source/packages/{plg_actionlog_mokobackup/mokobackup.xml => plg_actionlog_mokojoombackup/mokojoombackup.xml} (60%) rename source/packages/{plg_actionlog_mokobackup => plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup}/services/provider.php (78%) rename source/packages/{plg_actionlog_mokobackup => plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup}/src/Extension/MokoBackupActionlog.php (77%) delete mode 100644 source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini delete mode 100644 source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini delete mode 100644 source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini delete mode 100644 source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini create mode 100644 source/packages/plg_console_mokojoombackup/language/en-GB/plg_console_mokojoombackup.ini create mode 100644 source/packages/plg_console_mokojoombackup/language/en-GB/plg_console_mokojoombackup.sys.ini create mode 100644 source/packages/plg_console_mokojoombackup/language/en-US/plg_console_mokojoombackup.ini create mode 100644 source/packages/plg_console_mokojoombackup/language/en-US/plg_console_mokojoombackup.sys.ini rename source/packages/{plg_content_mokobackup/mokobackup.php => plg_console_mokojoombackup/mokojoombackup.php} (86%) rename source/packages/{plg_console_mokobackup/mokobackup.xml => plg_console_mokojoombackup/mokojoombackup.xml} (60%) rename source/packages/{plg_webservices_mokobackup => plg_console_mokojoombackup/plg_console_mokojoombackup}/services/provider.php (79%) rename source/packages/{plg_console_mokobackup => plg_console_mokojoombackup/plg_console_mokojoombackup}/src/Command/CleanupCommand.php (88%) rename source/packages/{plg_console_mokobackup => plg_console_mokojoombackup/plg_console_mokojoombackup}/src/Command/ListCommand.php (87%) rename source/packages/{plg_console_mokobackup => plg_console_mokojoombackup/plg_console_mokojoombackup}/src/Command/ProfilesCommand.php (86%) rename source/packages/{plg_console_mokobackup => plg_console_mokojoombackup/plg_console_mokojoombackup}/src/Command/RestoreCommand.php (86%) rename source/packages/{plg_console_mokobackup => plg_console_mokojoombackup/plg_console_mokojoombackup}/src/Command/RunCommand.php (83%) rename source/packages/{plg_console_mokobackup => plg_console_mokojoombackup/plg_console_mokojoombackup}/src/Extension/MokoBackupConsole.php (63%) delete mode 100644 source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini delete mode 100644 source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini delete mode 100644 source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini delete mode 100644 source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini create mode 100644 source/packages/plg_content_mokojoombackup/language/en-GB/plg_content_mokojoombackup.ini create mode 100644 source/packages/plg_content_mokojoombackup/language/en-GB/plg_content_mokojoombackup.sys.ini create mode 100644 source/packages/plg_content_mokojoombackup/language/en-US/plg_content_mokojoombackup.ini create mode 100644 source/packages/plg_content_mokojoombackup/language/en-US/plg_content_mokojoombackup.sys.ini rename source/packages/{plg_console_mokobackup/mokobackup.php => plg_content_mokojoombackup/mokojoombackup.php} (86%) rename source/packages/{plg_content_mokobackup/mokobackup.xml => plg_content_mokojoombackup/mokojoombackup.xml} (58%) rename source/packages/{plg_content_mokobackup => plg_content_mokojoombackup/plg_content_mokojoombackup}/services/provider.php (78%) rename source/packages/{plg_content_mokobackup => plg_content_mokojoombackup/plg_content_mokojoombackup}/src/Extension/MokoBackupContent.php (79%) delete mode 100644 source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.ini delete mode 100644 source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.sys.ini delete mode 100644 source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.ini delete mode 100644 source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.sys.ini create mode 100644 source/packages/plg_quickicon_mokojoombackup/language/en-GB/plg_quickicon_mokojoombackup.ini create mode 100644 source/packages/plg_quickicon_mokojoombackup/language/en-GB/plg_quickicon_mokojoombackup.sys.ini create mode 100644 source/packages/plg_quickicon_mokojoombackup/language/en-US/plg_quickicon_mokojoombackup.ini create mode 100644 source/packages/plg_quickicon_mokojoombackup/language/en-US/plg_quickicon_mokojoombackup.sys.ini rename source/packages/{plg_quickicon_mokobackup/mokobackup.php => plg_quickicon_mokojoombackup/mokojoombackup.php} (100%) rename source/packages/{plg_quickicon_mokobackup/mokobackup.xml => plg_quickicon_mokojoombackup/mokojoombackup.xml} (52%) rename source/packages/{plg_quickicon_mokobackup => plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup}/index.html (100%) rename source/packages/{plg_quickicon_mokobackup => plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup}/language/en-GB/index.html (100%) rename source/packages/{plg_quickicon_mokobackup => plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup}/language/en-US/index.html (100%) rename source/packages/{plg_quickicon_mokobackup => plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup}/language/index.html (100%) rename source/packages/{plg_quickicon_mokobackup => plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup}/services/index.html (100%) rename source/packages/{plg_quickicon_mokobackup => plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup}/services/provider.php (75%) rename source/packages/{plg_quickicon_mokobackup => plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup}/src/Extension/MokoBackupQuickicon.php (81%) rename source/packages/{plg_quickicon_mokobackup => plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup}/src/Extension/index.html (100%) rename source/packages/{plg_quickicon_mokobackup => plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup}/src/index.html (100%) delete mode 100644 source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.ini delete mode 100644 source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.sys.ini delete mode 100644 source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.ini delete mode 100644 source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.sys.ini create mode 100644 source/packages/plg_system_mokojoombackup/language/en-GB/plg_system_mokojoombackup.ini create mode 100644 source/packages/plg_system_mokojoombackup/language/en-GB/plg_system_mokojoombackup.sys.ini create mode 100644 source/packages/plg_system_mokojoombackup/language/en-US/plg_system_mokojoombackup.ini create mode 100644 source/packages/plg_system_mokojoombackup/language/en-US/plg_system_mokojoombackup.sys.ini rename source/packages/{plg_actionlog_mokobackup/mokobackup.php => plg_system_mokojoombackup/mokojoombackup.php} (86%) rename source/packages/{plg_system_mokobackup/mokobackup.xml => plg_system_mokojoombackup/mokojoombackup.xml} (60%) rename source/packages/{plg_system_mokobackup => plg_system_mokojoombackup/plg_system_mokojoombackup}/index.html (100%) rename source/packages/{plg_system_mokobackup => plg_system_mokojoombackup/plg_system_mokojoombackup}/language/en-GB/index.html (100%) rename source/packages/{plg_system_mokobackup => plg_system_mokojoombackup/plg_system_mokojoombackup}/language/en-US/index.html (100%) rename source/packages/{plg_system_mokobackup => plg_system_mokojoombackup/plg_system_mokojoombackup}/language/index.html (100%) rename source/packages/{plg_system_mokobackup => plg_system_mokojoombackup/plg_system_mokojoombackup}/services/index.html (100%) rename source/packages/{plg_console_mokobackup => plg_system_mokojoombackup/plg_system_mokojoombackup}/services/provider.php (80%) rename source/packages/{plg_system_mokobackup => plg_system_mokojoombackup/plg_system_mokojoombackup}/src/Extension/MokoBackup.php (83%) rename source/packages/{plg_system_mokobackup => plg_system_mokojoombackup/plg_system_mokojoombackup}/src/Extension/index.html (100%) rename source/packages/{plg_system_mokobackup => plg_system_mokojoombackup/plg_system_mokojoombackup}/src/index.html (100%) delete mode 100644 source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini delete mode 100644 source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini delete mode 100644 source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini delete mode 100644 source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini delete mode 100644 source/packages/plg_task_mokobackup/mokobackup.php delete mode 100644 source/packages/plg_task_mokobackup/services/provider.php create mode 100644 source/packages/plg_task_mokojoombackup/language/en-GB/plg_task_mokojoombackup.ini create mode 100644 source/packages/plg_task_mokojoombackup/language/en-GB/plg_task_mokojoombackup.sys.ini create mode 100644 source/packages/plg_task_mokojoombackup/language/en-US/plg_task_mokojoombackup.ini create mode 100644 source/packages/plg_task_mokojoombackup/language/en-US/plg_task_mokojoombackup.sys.ini rename source/packages/{plg_system_mokobackup/mokobackup.php => plg_task_mokojoombackup/mokojoombackup.php} (87%) rename source/packages/{plg_task_mokobackup/mokobackup.xml => plg_task_mokojoombackup/mokojoombackup.xml} (63%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/forms/index.html (100%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/forms/run_profile.xml (55%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/index.html (100%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/language/en-GB/index.html (100%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/language/en-US/index.html (100%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/language/index.html (100%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/services/index.html (100%) rename source/packages/{plg_system_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/services/provider.php (80%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/src/Extension/MokoBackupTask.php (81%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/src/Extension/index.html (100%) rename source/packages/{plg_task_mokobackup => plg_task_mokojoombackup/plg_task_mokojoombackup}/src/index.html (100%) delete mode 100644 source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.ini delete mode 100644 source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.sys.ini delete mode 100644 source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.ini delete mode 100644 source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.sys.ini delete mode 100644 source/packages/plg_webservices_mokobackup/mokobackup.php create mode 100644 source/packages/plg_webservices_mokojoombackup/language/en-GB/plg_webservices_mokojoombackup.ini create mode 100644 source/packages/plg_webservices_mokojoombackup/language/en-GB/plg_webservices_mokojoombackup.sys.ini create mode 100644 source/packages/plg_webservices_mokojoombackup/language/en-US/plg_webservices_mokojoombackup.ini create mode 100644 source/packages/plg_webservices_mokojoombackup/language/en-US/plg_webservices_mokojoombackup.sys.ini create mode 100644 source/packages/plg_webservices_mokojoombackup/mokojoombackup.php rename source/packages/{plg_webservices_mokobackup/mokobackup.xml => plg_webservices_mokojoombackup/mokojoombackup.xml} (59%) rename source/packages/{plg_webservices_mokobackup => plg_webservices_mokojoombackup/plg_webservices_mokojoombackup}/index.html (100%) rename source/packages/{plg_webservices_mokobackup => plg_webservices_mokojoombackup/plg_webservices_mokojoombackup}/language/en-GB/index.html (100%) rename source/packages/{plg_webservices_mokobackup => plg_webservices_mokojoombackup/plg_webservices_mokojoombackup}/language/en-US/index.html (100%) rename source/packages/{plg_webservices_mokobackup => plg_webservices_mokojoombackup/plg_webservices_mokojoombackup}/language/index.html (100%) rename source/packages/{plg_webservices_mokobackup => plg_webservices_mokojoombackup/plg_webservices_mokojoombackup}/services/index.html (100%) create mode 100644 source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/provider.php rename source/packages/{plg_webservices_mokobackup => plg_webservices_mokojoombackup/plg_webservices_mokojoombackup}/src/Extension/MokoBackupWebServices.php (60%) rename source/packages/{plg_webservices_mokobackup => plg_webservices_mokojoombackup/plg_webservices_mokojoombackup}/src/Extension/index.html (100%) rename source/packages/{plg_webservices_mokobackup => plg_webservices_mokojoombackup/plg_webservices_mokojoombackup}/src/index.html (100%) rename source/{pkg_mokobackup.xml => pkg_mokojoombackup.xml} (53%) diff --git a/.mokogitea/CLAUDE.md b/.mokogitea/CLAUDE.md index 7fcb82c..c5dda15 100644 --- a/.mokogitea/CLAUDE.md +++ b/.mokogitea/CLAUDE.md @@ -6,7 +6,7 @@ Full-site backup and restore for Joomla — database, files, and configuration. | Field | Value | |---|---| -| **Package** | `pkg_mokobackup` | +| **Package** | `pkg_mokojoombackup` | | **Language** | PHP 8.1+ | | **Branch** | develop on `dev`, merge to `main` (protected) | | **Wiki** | [MokoJoomBackup Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoJoomBackup/wiki) | @@ -26,32 +26,32 @@ composer install # Install PHP dependencies Joomla **package** with four sub-extensions: -### com_mokobackup (Component) +### com_mokojoombackup (Component) - Admin backend for managing backup profiles and records - Backup engine: `Engine/BackupEngine`, `Engine/DatabaseDumper`, `Engine/FileScanner`, `Engine/Archiver` - Joomla 4/5 MVC: Controllers, Models, Views, Tables -- Namespace: `Joomla\Component\MokoBackup\Administrator` -- DB tables: `#__mokobackup_profiles`, `#__mokobackup_records` -- CLI: `cli/mokobackup.php` for cron-based backups +- Namespace: `Joomla\Component\MokoJoomBackup\Administrator` +- DB tables: `#__mokojoombackup_profiles`, `#__mokojoombackup_records` +- CLI: `cli/mokojoombackup.php` for cron-based backups -### plg_system_mokobackup (System Plugin) +### plg_system_mokojoombackup (System Plugin) - Cleanup of expired backup archives (age + count limits) -- Namespace: `Joomla\Plugin\System\MokoBackup` +- Namespace: `Joomla\Plugin\System\MokoJoomBackup` -### plg_task_mokobackup (Task Plugin) +### plg_task_mokojoombackup (Task Plugin) - Integrates with Joomla's Scheduled Tasks (com_scheduler) - Registers "Run Backup Profile" task type -- Namespace: `Joomla\Plugin\Task\MokoBackup` +- Namespace: `Joomla\Plugin\Task\MokoJoomBackup` -### plg_webservices_mokobackup (WebServices Plugin) -- REST API for remote backup management (wire-compatible with mcp_mokobackup) +### plg_webservices_mokojoombackup (WebServices Plugin) +- REST API for remote backup management (wire-compatible with mcp_mokojoombackup) - Endpoints: backup, backups, profiles, download, delete -- Namespace: `Joomla\Plugin\WebServices\MokoBackup` +- Namespace: `Joomla\Plugin\WebServices\MokoJoomBackup` ### Database Schema -- `#__mokobackup_profiles` — backup profiles (name, description, config JSON, filters JSON) -- `#__mokobackup_records` — backup records (profile_id, status, origin, archive path, sizes, timestamps) +- `#__mokojoombackup_profiles` — backup profiles (name, description, config JSON, filters JSON) +- `#__mokojoombackup_records` — backup records (profile_id, status, origin, archive path, sizes, timestamps) ## Rules diff --git a/CHANGELOG.md b/CHANGELOG.md index 64160d9..2299355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,10 +34,10 @@ ### Added - Admin dashboard view as default landing page with status cards, quick actions, and system health checklist (#28) -- Console plugin (plg_console_mokobackup) — CLI commands: run, list, profiles, restore, cleanup (#29) -- Content plugin (plg_content_mokobackup) — auto-backup before extension install/update (#30) -- Actionlog plugin (plg_actionlog_mokobackup) — logs backup and profile actions to User Action Logs (#31) -- BackupEngine dispatches onMokoBackupAfterRun event for plugin listeners +- Console plugin (plg_console_mokojoombackup) — CLI commands: run, list, profiles, restore, cleanup (#29) +- Content plugin (plg_content_mokojoombackup) — auto-backup before extension install/update (#30) +- Actionlog plugin (plg_actionlog_mokojoombackup) — logs backup and profile actions to User Action Logs (#31) +- BackupEngine dispatches onMokoJoomBackupAfterRun event for plugin listeners - Update site notice on dashboard and post-install ### Changed @@ -52,7 +52,7 @@ ### Added - Initial package structure with component, system plugin, task plugin, and webservices plugin -- Joomla Scheduled Tasks integration (plg_task_mokobackup) — create multiple tasks, each running a different backup profile on its own schedule +- Joomla Scheduled Tasks integration (plg_task_mokojoombackup) — create multiple tasks, each running a different backup profile on its own schedule - Individual form fields for all profile settings (no raw JSON) - FTP/FTPS uploader with recursive directory creation, passive mode, SSL, and size verification - Google Drive uploader using OAuth2 refresh tokens and resumable upload API @@ -80,5 +80,5 @@ - Backup profiles with independent configurations - Backup record management (list, download, delete) - CLI script for cron/scheduled backups -- REST API compatible with MokoBackup MCP server +- REST API compatible with MokoJoomBackup MCP server - System plugin for automatic backup cleanup with configurable retention diff --git a/Makefile b/Makefile index f38765e..bafba81 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ # CONFIGURATION # ============================================================================== -EXTENSION_NAME := mokobackup +EXTENSION_NAME := mokojoombackup EXTENSION_TYPE := package SRC_DIR := source @@ -158,7 +158,7 @@ release-rc: validate validate-xml ## Trigger release-candidate build via CI work .PHONY: version version: ## Display version from package manifest - @VERSION=$$(grep '' $(SRC_DIR)/pkg_mokobackup.xml | sed 's/.*\(.*\)<\/version>.*/\1/'); \ + @VERSION=$$(grep '' $(SRC_DIR)/pkg_mokojoombackup.xml | sed 's/.*\(.*\)<\/version>.*/\1/'); \ echo "$(COLOR_BLUE)$(EXTENSION_NAME)$(COLOR_RESET) v$$VERSION ($(EXTENSION_TYPE))" # Default target diff --git a/source/language/en-GB/pkg_mokobackup.sys.ini b/source/language/en-GB/pkg_mokobackup.sys.ini deleted file mode 100644 index 75457fd..0000000 --- a/source/language/en-GB/pkg_mokobackup.sys.ini +++ /dev/null @@ -1,10 +0,0 @@ -; MokoJoomBackup — Package language file (en-GB) -; @package MokoJoomBackup -; @author Moko Consulting -; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. -; @license GPL-3.0-or-later - -PKG_MOKOBACKUP="Package - MokoJoomBackup" -PKG_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration. Includes admin component, system plugin, and REST API." -PKG_MOKOBACKUP_PHP_VERSION_ERROR="MokoJoomBackup requires PHP %s or later." -PKG_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." diff --git a/source/language/en-GB/pkg_mokojoombackup.sys.ini b/source/language/en-GB/pkg_mokojoombackup.sys.ini new file mode 100644 index 0000000..bf27f12 --- /dev/null +++ b/source/language/en-GB/pkg_mokojoombackup.sys.ini @@ -0,0 +1,10 @@ +; MokoJoomBackup — Package language file (en-GB) +; @package MokoJoomBackup +; @author Moko Consulting +; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. +; @license GPL-3.0-or-later + +PKG_MOKOJOOMBACKUP="Package - MokoJoomBackup" +PKG_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration. Includes admin component, system plugin, and REST API." +PKG_MOKOJOOMBACKUP_PHP_VERSION_ERROR="MokoJoomBackup requires PHP %s or later." +PKG_MOKOJOOMBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." diff --git a/source/language/en-US/pkg_mokobackup.sys.ini b/source/language/en-US/pkg_mokobackup.sys.ini deleted file mode 100644 index 4936ac7..0000000 --- a/source/language/en-US/pkg_mokobackup.sys.ini +++ /dev/null @@ -1,10 +0,0 @@ -; MokoJoomBackup — Package language file (en-US) -; @package MokoJoomBackup -; @author Moko Consulting -; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. -; @license GPL-3.0-or-later - -PKG_MOKOBACKUP="Package - MokoJoomBackup" -PKG_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration. Includes admin component, system plugin, and REST API." -PKG_MOKOBACKUP_PHP_VERSION_ERROR="MokoJoomBackup requires PHP %s or later." -PKG_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." diff --git a/source/language/en-US/pkg_mokojoombackup.sys.ini b/source/language/en-US/pkg_mokojoombackup.sys.ini new file mode 100644 index 0000000..07d63b6 --- /dev/null +++ b/source/language/en-US/pkg_mokojoombackup.sys.ini @@ -0,0 +1,10 @@ +; MokoJoomBackup — Package language file (en-US) +; @package MokoJoomBackup +; @author Moko Consulting +; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. +; @license GPL-3.0-or-later + +PKG_MOKOJOOMBACKUP="Package - MokoJoomBackup" +PKG_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration. Includes admin component, system plugin, and REST API." +PKG_MOKOJOOMBACKUP_PHP_VERSION_ERROR="MokoJoomBackup requires PHP %s or later." +PKG_MOKOJOOMBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." diff --git a/source/packages/com_mokobackup/forms/backup.xml b/source/packages/com_mokobackup/forms/backup.xml deleted file mode 100644 index 6d1e1e8..0000000 --- a/source/packages/com_mokobackup/forms/backup.xml +++ /dev/null @@ -1,15 +0,0 @@ - -
    -
    - - - - - - - - - - -
    -
    diff --git a/source/packages/com_mokobackup/forms/filter_backups.xml b/source/packages/com_mokobackup/forms/filter_backups.xml deleted file mode 100644 index d22dcb8..0000000 --- a/source/packages/com_mokobackup/forms/filter_backups.xml +++ /dev/null @@ -1,47 +0,0 @@ - -
    - - - - - - - - - - - - - - - - - - - - - - -
    diff --git a/source/packages/com_mokobackup/forms/profile.xml b/source/packages/com_mokobackup/forms/profile.xml deleted file mode 100644 index 5c3fd2d..0000000 --- a/source/packages/com_mokobackup/forms/profile.xml +++ /dev/null @@ -1,373 +0,0 @@ - -
    -
    - - - - - - - - -
    - -
    - - - - - - - - - - - - - - - - - - -
    - -
    - - - - - - -
    - -
    - - - -
    - -
    - - - - - - - - - - -
    - -
    - - - - - - - - - - -
    - -
    - - - - - - - - - - - - - -
    - -
    - - - - -
    - -
    - - - - - - -
    -
    diff --git a/source/packages/com_mokobackup/language/en-GB/com_mokobackup.ini b/source/packages/com_mokobackup/language/en-GB/com_mokobackup.ini deleted file mode 100644 index caf865b..0000000 --- a/source/packages/com_mokobackup/language/en-GB/com_mokobackup.ini +++ /dev/null @@ -1,264 +0,0 @@ -; MokoJoomBackup — Component language file (en-GB) -; @package MokoJoomBackup -; @author Moko Consulting -; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. -; @license GPL-3.0-or-later - -COM_MOKOBACKUP="MokoJoomBackup" -COM_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla" - -; Submenu -COM_MOKOBACKUP_SUBMENU_DASHBOARD="Dashboard" -COM_MOKOBACKUP_SUBMENU_BACKUPS="Backup Records" -COM_MOKOBACKUP_SUBMENU_PROFILES="Backup Profiles" - -; Dashboard view -COM_MOKOBACKUP_DASHBOARD_TITLE="MokoJoomBackup Dashboard" -COM_MOKOBACKUP_DASHBOARD_LAST_BACKUP="Last Backup" -COM_MOKOBACKUP_DASHBOARD_NO_BACKUPS="No backups yet" -COM_MOKOBACKUP_DASHBOARD_NEXT_SCHEDULED="Next Scheduled" -COM_MOKOBACKUP_DASHBOARD_NO_SCHEDULED="No tasks scheduled" -COM_MOKOBACKUP_DASHBOARD_TOTAL_BACKUPS="Total Backups" -COM_MOKOBACKUP_DASHBOARD_STORAGE="Storage Used" -COM_MOKOBACKUP_DASHBOARD_FAILURES_7D="%d failures (7 days)" -COM_MOKOBACKUP_DASHBOARD_QUICK_ACTIONS="Quick Actions" -COM_MOKOBACKUP_DASHBOARD_SCHEDULED_TASKS="Scheduled Tasks" -COM_MOKOBACKUP_DASHBOARD_UPDATE_SITE="Update Site" -COM_MOKOBACKUP_DASHBOARD_SYSTEM_HEALTH="System Health" - -; Backups view -COM_MOKOBACKUP_BACKUPS_TITLE="Backup Records" -COM_MOKOBACKUP_BACKUPS_TABLE_CAPTION="Table of backup records" -COM_MOKOBACKUP_NO_BACKUPS="No backups found. Click 'Backup Now' to create your first backup." -COM_MOKOBACKUP_TOOLBAR_BACKUP_NOW="Backup Now" -COM_MOKOBACKUP_DOWNLOAD="Download" - -; Backup detail view -COM_MOKOBACKUP_BACKUP_DETAIL="Backup Detail" -COM_MOKOBACKUP_VIEW_LOG="Backup Log" -COM_MOKOBACKUP_FIELD_CHECKSUM="SHA-256 Checksum" -COM_MOKOBACKUP_FIELD_PATH="File Path" -COM_MOKOBACKUP_FIELD_DB_SIZE="DB Size" -COM_MOKOBACKUP_FIELD_REMOTE="Remote Path" - -; Profiles view -COM_MOKOBACKUP_PROFILES_TITLE="Backup Profiles" -COM_MOKOBACKUP_PROFILES_TABLE_CAPTION="Table of backup profiles" -COM_MOKOBACKUP_NO_PROFILES="No backup profiles found." -COM_MOKOBACKUP_PROFILE_NEW="New Profile" -COM_MOKOBACKUP_PROFILE_EDIT="Edit Profile" - -; Table headings -COM_MOKOBACKUP_HEADING_DESCRIPTION="Description" -COM_MOKOBACKUP_HEADING_PROFILE="Profile" -COM_MOKOBACKUP_HEADING_STATUS="Status" -COM_MOKOBACKUP_HEADING_TYPE="Type" -COM_MOKOBACKUP_HEADING_SIZE="Size" -COM_MOKOBACKUP_HEADING_DATE="Date" -COM_MOKOBACKUP_HEADING_ACTIONS="Actions" -COM_MOKOBACKUP_HEADING_TITLE="Title" -COM_MOKOBACKUP_HEADING_DATE_DESC="Date descending" -COM_MOKOBACKUP_HEADING_DATE_ASC="Date ascending" -COM_MOKOBACKUP_HEADING_SIZE_DESC="Size descending" -COM_MOKOBACKUP_HEADING_SIZE_ASC="Size ascending" -COM_MOKOBACKUP_HEADING_TITLE_ASC="Title ascending" -COM_MOKOBACKUP_HEADING_TITLE_DESC="Title descending" - -; General fields -COM_MOKOBACKUP_FIELD_TITLE="Title" -COM_MOKOBACKUP_FIELD_TITLE_DESC="Profile name" -COM_MOKOBACKUP_FIELD_DESCRIPTION="Description" -COM_MOKOBACKUP_FIELD_DESCRIPTION_DESC="Brief description of this profile" -COM_MOKOBACKUP_FIELD_BACKUP_TYPE="Backup Type" -COM_MOKOBACKUP_FIELD_BACKUP_TYPE_DESC="What to include in the backup" -COM_MOKOBACKUP_FIELD_STATUS="Status" -COM_MOKOBACKUP_FIELD_ORIGIN="Origin" -COM_MOKOBACKUP_FIELD_SIZE="Total Size" -COM_MOKOBACKUP_FIELD_START="Start Time" -COM_MOKOBACKUP_FIELD_END="End Time" -COM_MOKOBACKUP_FIELD_ARCHIVE="Archive Name" -COM_MOKOBACKUP_FIELD_FILES_COUNT="Files Count" -COM_MOKOBACKUP_FIELD_TABLES_COUNT="Tables Count" - -; Archive settings -COM_MOKOBACKUP_FIELD_ARCHIVE_FORMAT="Archive Format" -COM_MOKOBACKUP_FIELD_ARCHIVE_FORMAT_DESC="Format for the backup archive file" -COM_MOKOBACKUP_FIELD_COMPRESSION="Compression Level" -COM_MOKOBACKUP_FIELD_COMPRESSION_DESC="Higher compression = smaller file but slower" -COM_MOKOBACKUP_COMPRESSION_NONE="None (fastest)" -COM_MOKOBACKUP_COMPRESSION_FASTEST="Low (fast)" -COM_MOKOBACKUP_COMPRESSION_NORMAL="Normal (balanced)" -COM_MOKOBACKUP_COMPRESSION_BEST="Maximum (smallest)" -COM_MOKOBACKUP_FIELD_ENCRYPTION_PASSWORD="Encryption Password" -COM_MOKOBACKUP_FIELD_ENCRYPTION_PASSWORD_DESC="Set a password to encrypt the backup archive with AES-256. Leave blank for no encryption. Required to restore encrypted backups." -COM_MOKOBACKUP_FIELD_SPLIT_SIZE="Split Size (MB)" -COM_MOKOBACKUP_FIELD_SPLIT_SIZE_DESC="Split archive into parts of this size in MB. 0 = no splitting." -COM_MOKOBACKUP_FIELD_BACKUP_DIR="Backup Directory" -COM_MOKOBACKUP_FIELD_BACKUP_DIR_DESC="Directory where backup archives are stored. Supports placeholders: [host], [date], [year], [month], [day], [profile_name], [site_name], [type]. Absolute paths (starting with /) are used as-is; relative paths resolve from the Joomla root." -COM_MOKOBACKUP_FIELD_ARCHIVE_NAME_FORMAT="Archive Name Format" -COM_MOKOBACKUP_FIELD_ARCHIVE_NAME_FORMAT_DESC="Filename template for backup archives (without extension). Placeholders: [host] hostname, [date] Ymd, [time] His, [datetime] Ymd_His, [year] [month] [day] [hour] [minute] [second], [profile_id], [profile_name], [site_name], [type], [random]." -COM_MOKOBACKUP_FIELD_INCLUDE_MOKORESTORE="Include Restore Script" -COM_MOKOBACKUP_FIELD_INCLUDE_MOKORESTORE_DESC="Include MokoRestore (standalone restore.php) inside the backup archive. Creates a self-contained package that can restore the site on a blank server without Joomla installed." - -; Exclusion filter fields -COM_MOKOBACKUP_FIELD_EXCLUDE_DIRS="Exclude Directories" -COM_MOKOBACKUP_FIELD_EXCLUDE_DIRS_DESC="Browse and check directories to exclude from file backup. You can also type paths manually." -COM_MOKOBACKUP_FILTER_EXCLUDED="Excluded" -COM_MOKOBACKUP_FILTER_INCLUDED="Included" -COM_MOKOBACKUP_FILTER_ADD_MANUAL="Add Path" -COM_MOKOBACKUP_FIELD_EXCLUDE_FILES="Exclude Files" -COM_MOKOBACKUP_FIELD_EXCLUDE_FILES_DESC="One filename or pattern per line. Supports wildcards (e.g. *.bak, *.tmp)." -COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES="Exclude Tables" -COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES_DESC="One table name per line (use #__ prefix). These tables will be skipped during database dump." - -; Remote storage fields -COM_MOKOBACKUP_FIELD_REMOTE_STORAGE="Remote Storage" -COM_MOKOBACKUP_FIELD_REMOTE_STORAGE_DESC="Optionally upload backup archives to a remote location after creation" -COM_MOKOBACKUP_REMOTE_NONE="None (local only)" -COM_MOKOBACKUP_REMOTE_FTP="FTP / FTPS" -COM_MOKOBACKUP_REMOTE_GDRIVE="Google Drive" -COM_MOKOBACKUP_FIELD_KEEP_LOCAL="Keep Local Copy" -COM_MOKOBACKUP_FIELD_KEEP_LOCAL_DESC="Keep the local backup file after uploading to remote storage" - -; FTP fields -COM_MOKOBACKUP_FIELD_FTP_HOST="FTP Host" -COM_MOKOBACKUP_FIELD_FTP_HOST_DESC="FTP server hostname or IP address" -COM_MOKOBACKUP_FIELD_FTP_PORT="FTP Port" -COM_MOKOBACKUP_FIELD_FTP_PORT_DESC="FTP server port (default: 21)" -COM_MOKOBACKUP_FIELD_FTP_USERNAME="FTP Username" -COM_MOKOBACKUP_FIELD_FTP_PASSWORD="FTP Password" -COM_MOKOBACKUP_FIELD_FTP_PATH="Remote Path" -COM_MOKOBACKUP_FIELD_FTP_PATH_DESC="Directory on the FTP server to upload backups to" -COM_MOKOBACKUP_FIELD_FTP_PASSIVE="Passive Mode" -COM_MOKOBACKUP_FIELD_FTP_PASSIVE_DESC="Use passive mode for FTP connections (recommended)" -COM_MOKOBACKUP_FIELD_FTP_SSL="Use FTPS (SSL)" -COM_MOKOBACKUP_FIELD_FTP_SSL_DESC="Connect using FTPS (FTP over SSL/TLS)" - -; Google Drive fields -COM_MOKOBACKUP_FIELD_GDRIVE_CLIENT_ID="Google Client ID" -COM_MOKOBACKUP_FIELD_GDRIVE_CLIENT_ID_DESC="OAuth 2.0 Client ID from Google Cloud Console" -COM_MOKOBACKUP_FIELD_GDRIVE_CLIENT_SECRET="Google Client Secret" -COM_MOKOBACKUP_FIELD_GDRIVE_REFRESH_TOKEN="Refresh Token" -COM_MOKOBACKUP_FIELD_GDRIVE_REFRESH_TOKEN_DESC="OAuth 2.0 refresh token for offline access" -COM_MOKOBACKUP_FIELD_GDRIVE_FOLDER_ID="Drive Folder ID" -COM_MOKOBACKUP_FIELD_GDRIVE_FOLDER_ID_DESC="Google Drive folder ID where backups will be uploaded. Find this in the folder URL." - -; Backup types -COM_MOKOBACKUP_TYPE_FULL="Full Site (Database + Files)" -COM_MOKOBACKUP_TYPE_DATABASE="Database Only" -COM_MOKOBACKUP_TYPE_FILES="Files Only" -COM_MOKOBACKUP_TYPE_DIFFERENTIAL="Differential (changed files + full DB)" - -; Status labels -COM_MOKOBACKUP_STATUS_COMPLETE="Complete" -COM_MOKOBACKUP_STATUS_RUNNING="Running" -COM_MOKOBACKUP_STATUS_FAIL="Failed" -COM_MOKOBACKUP_STATUS_PENDING="Pending" - -; Filters -COM_MOKOBACKUP_FILTER_SEARCH="Search" -COM_MOKOBACKUP_FILTER_STATUS="Status" -COM_MOKOBACKUP_FILTER_STATUS_ALL="- Select Status -" - -; Tabs and fieldsets -COM_MOKOBACKUP_TAB_GENERAL="General" -COM_MOKOBACKUP_TAB_ARCHIVE="Archive Settings" -COM_MOKOBACKUP_TAB_FILTERS="Exclusion Filters" -COM_MOKOBACKUP_TAB_REMOTE="Remote Storage" -COM_MOKOBACKUP_FIELDSET_GENERAL="General" -COM_MOKOBACKUP_FIELDSET_ARCHIVE="Archive Settings" -COM_MOKOBACKUP_FIELDSET_STATUS="Status" -COM_MOKOBACKUP_FIELDSET_FILTERS="Exclusion Filters" -COM_MOKOBACKUP_FIELDSET_REMOTE="Remote Storage" -COM_MOKOBACKUP_FIELDSET_FTP="FTP Settings" -COM_MOKOBACKUP_FIELDSET_GDRIVE="Google Drive Settings" - -; Backup profile selector -COM_MOKOBACKUP_BACKUP_PROFILE="Backup Profile" - -; Restore -COM_MOKOBACKUP_TOOLBAR_RESTORE="Restore" -COM_MOKOBACKUP_RESTORE_CONFIRM="WARNING: Restoring will overwrite your current site files and/or database. Are you sure you want to continue?" - -; Notifications -COM_MOKOBACKUP_TAB_NOTIFICATIONS="Notifications" -COM_MOKOBACKUP_FIELDSET_NOTIFICATIONS="Email Notifications" -COM_MOKOBACKUP_FIELD_NOTIFY_EMAIL="Notification Email(s)" -COM_MOKOBACKUP_FIELD_NOTIFY_EMAIL_DESC="Comma-separated list of email addresses to notify. Leave empty to disable notifications." -COM_MOKOBACKUP_FIELD_NOTIFY_SUCCESS="Notify on Success" -COM_MOKOBACKUP_FIELD_NOTIFY_SUCCESS_DESC="Send an email when a backup completes successfully." -COM_MOKOBACKUP_FIELD_NOTIFY_FAILURE="Notify on Failure" -COM_MOKOBACKUP_FIELD_NOTIFY_FAILURE_DESC="Send an email when a backup fails. Includes log excerpt for debugging." - -; Integrity verification -COM_MOKOBACKUP_TOOLBAR_VERIFY="Verify Integrity" -COM_MOKOBACKUP_VERIFY_OK="Archive integrity verified — SHA-256 checksum matches." -COM_MOKOBACKUP_VERIFY_FAILED="INTEGRITY CHECK FAILED — archive has been modified or corrupted since backup." -COM_MOKOBACKUP_VERIFY_NO_CHECKSUM="No checksum stored for this backup. Only backups created after this update can be verified." - -; S3 storage -COM_MOKOBACKUP_REMOTE_S3="Amazon S3 / S3-Compatible" -COM_MOKOBACKUP_FIELDSET_S3="S3 Storage Settings" -COM_MOKOBACKUP_FIELD_S3_ENDPOINT="S3 Endpoint" -COM_MOKOBACKUP_FIELD_S3_ENDPOINT_DESC="S3 API endpoint URL. Leave blank for AWS S3. For Wasabi, MinIO, Backblaze B2, enter their endpoint URL." -COM_MOKOBACKUP_FIELD_S3_REGION="Region" -COM_MOKOBACKUP_FIELD_S3_REGION_DESC="AWS region (e.g. us-east-1, eu-west-1). Required for AWS Signature V4." -COM_MOKOBACKUP_FIELD_S3_ACCESS_KEY="Access Key" -COM_MOKOBACKUP_FIELD_S3_SECRET_KEY="Secret Key" -COM_MOKOBACKUP_FIELD_S3_BUCKET="Bucket Name" -COM_MOKOBACKUP_FIELD_S3_BUCKET_DESC="S3 bucket name where backups will be stored." -COM_MOKOBACKUP_FIELD_S3_PATH="Path Prefix" -COM_MOKOBACKUP_FIELD_S3_PATH_DESC="Optional path prefix inside the bucket (e.g. /backups or /sites/mysite)." - -; Akeeba Import -COM_MOKOBACKUP_TOOLBAR_IMPORT_AKEEBA="Import from Akeeba" -COM_MOKOBACKUP_AKEEBA_NOT_FOUND="Akeeba Backup tables not found. Is Akeeba Backup Pro installed?" - -; Update site notice -COM_MOKOBACKUP_UPDATE_SITE_NOTICE="To receive automatic updates, configure your Update Site with your download key." -COM_MOKOBACKUP_UPDATE_SITE_MISSING="MokoJoomBackup update site not found. Reinstall the package to register the update server." -COM_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." - -; Component Options (config.xml) -COM_MOKOBACKUP_CONFIG_GENERAL="General" -COM_MOKOBACKUP_CONFIG_DEFAULT_BACKUP_DIR="Default Backup Directory" -COM_MOKOBACKUP_CONFIG_DEFAULT_BACKUP_DIR_DESC="Default directory for backup archives, relative to Joomla root. Can be overridden per profile." -COM_MOKOBACKUP_CONFIG_DEFAULT_PROFILE="Default Profile" -COM_MOKOBACKUP_CONFIG_DEFAULT_PROFILE_DESC="Default backup profile used by quick actions and CLI when no profile is specified." -COM_MOKOBACKUP_CONFIG_SHOW_UPDATE_NOTICE="Show Update Site Notice" -COM_MOKOBACKUP_CONFIG_SHOW_UPDATE_NOTICE_DESC="Display the update site configuration notice on the Backup Records view." -COM_MOKOBACKUP_CONFIG_CLEANUP="Cleanup Defaults" -COM_MOKOBACKUP_CONFIG_MAX_AGE="Max Backup Age (days)" -COM_MOKOBACKUP_CONFIG_MAX_AGE_DESC="Default maximum age for backup records. Used by the system plugin and CLI cleanup command." -COM_MOKOBACKUP_CONFIG_MAX_BACKUPS="Max Backup Count" -COM_MOKOBACKUP_CONFIG_MAX_BACKUPS_DESC="Default maximum number of completed backups to retain." -COM_MOKOBACKUP_CONFIG_NOTIFICATIONS="Notifications" -COM_MOKOBACKUP_CONFIG_NOTIFY_EMAIL="Global Notification Email(s)" -COM_MOKOBACKUP_CONFIG_NOTIFY_EMAIL_DESC="Comma-separated list of email addresses for global backup notifications. Per-profile settings override this." -COM_MOKOBACKUP_CONFIG_NOTIFY_SUCCESS="Notify on Success" -COM_MOKOBACKUP_CONFIG_NOTIFY_SUCCESS_DESC="Send email when any backup completes successfully (unless overridden by profile)." -COM_MOKOBACKUP_CONFIG_NOTIFY_FAILURE="Notify on Failure" -COM_MOKOBACKUP_CONFIG_NOTIFY_FAILURE_DESC="Send email when any backup fails (unless overridden by profile)." - -; Folder picker -COM_MOKOBACKUP_FOLDER_EXISTS="Directory exists" -COM_MOKOBACKUP_FOLDER_NOT_FOUND="Directory not found" -COM_MOKOBACKUP_BACKUP_DIR_DEFAULT="Default (inside web root)" - -; Exclude fields -COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES_HELP="Check tables to exclude from database backup. Use Data to skip row data (keeps structure), Structure to skip CREATE TABLE, or both to fully exclude." -COM_MOKOBACKUP_FIELD_EXCLUDE_DATA="Data" -COM_MOKOBACKUP_FIELD_EXCLUDE_STRUCTURE="Structure" -COM_MOKOBACKUP_FIELD_TABLE_NAME="Table Name" - -; User group notifications -COM_MOKOBACKUP_FIELD_NOTIFY_USER_GROUPS="Notify User Groups" -COM_MOKOBACKUP_FIELD_NOTIFY_USER_GROUPS_DESC="Select Joomla user groups whose members will receive backup notifications. Combined with email addresses above." - -; Dashboard warnings -COM_MOKOBACKUP_DASHBOARD_DEFAULT_DIR_WARNING_TITLE="Backup directory is inside the web root" -COM_MOKOBACKUP_DASHBOARD_DEFAULT_DIR_WARNING="One or more profiles store backups in the default directory inside the web root. This may expose backup archives if .htaccess is not supported. Move backups to a directory outside the web root for better security." - -; Errors -COM_MOKOBACKUP_ERROR_FILE_NOT_FOUND="Backup archive file not found or has been deleted." -COM_MOKOBACKUP_ERROR_NO_RECORD_SELECTED="No backup record selected for restore." diff --git a/source/packages/com_mokobackup/language/en-GB/com_mokobackup.sys.ini b/source/packages/com_mokobackup/language/en-GB/com_mokobackup.sys.ini deleted file mode 100644 index 17e8576..0000000 --- a/source/packages/com_mokobackup/language/en-GB/com_mokobackup.sys.ini +++ /dev/null @@ -1,10 +0,0 @@ -; MokoJoomBackup — Component system language file (en-GB) -; @package MokoJoomBackup -; @author Moko Consulting -; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. -; @license GPL-3.0-or-later - -COM_MOKOBACKUP="MokoJoomBackup" -COM_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration." -COM_MOKOBACKUP_SUBMENU_BACKUPS="Backup Records" -COM_MOKOBACKUP_SUBMENU_PROFILES="Backup Profiles" diff --git a/source/packages/com_mokobackup/language/en-US/com_mokobackup.ini b/source/packages/com_mokobackup/language/en-US/com_mokobackup.ini deleted file mode 100644 index 8e72912..0000000 --- a/source/packages/com_mokobackup/language/en-US/com_mokobackup.ini +++ /dev/null @@ -1,69 +0,0 @@ -; MokoJoomBackup — Component language file (en-US) -; @package MokoJoomBackup -; @author Moko Consulting -; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. -; @license GPL-3.0-or-later - -COM_MOKOBACKUP="MokoJoomBackup" -COM_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla" -COM_MOKOBACKUP_SUBMENU_DASHBOARD="Dashboard" -COM_MOKOBACKUP_SUBMENU_BACKUPS="Backup Records" -COM_MOKOBACKUP_SUBMENU_PROFILES="Backup Profiles" -COM_MOKOBACKUP_DASHBOARD_TITLE="MokoJoomBackup Dashboard" -COM_MOKOBACKUP_DASHBOARD_LAST_BACKUP="Last Backup" -COM_MOKOBACKUP_DASHBOARD_NO_BACKUPS="No backups yet" -COM_MOKOBACKUP_DASHBOARD_NEXT_SCHEDULED="Next Scheduled" -COM_MOKOBACKUP_DASHBOARD_NO_SCHEDULED="No tasks scheduled" -COM_MOKOBACKUP_DASHBOARD_TOTAL_BACKUPS="Total Backups" -COM_MOKOBACKUP_DASHBOARD_STORAGE="Storage Used" -COM_MOKOBACKUP_DASHBOARD_FAILURES_7D="%d failures (7 days)" -COM_MOKOBACKUP_DASHBOARD_QUICK_ACTIONS="Quick Actions" -COM_MOKOBACKUP_DASHBOARD_SCHEDULED_TASKS="Scheduled Tasks" -COM_MOKOBACKUP_DASHBOARD_UPDATE_SITE="Update Site" -COM_MOKOBACKUP_DASHBOARD_SYSTEM_HEALTH="System Health" -COM_MOKOBACKUP_BACKUPS_TITLE="Backup Records" -COM_MOKOBACKUP_PROFILES_TITLE="Backup Profiles" -COM_MOKOBACKUP_TOOLBAR_BACKUP_NOW="Backup Now" -COM_MOKOBACKUP_NO_BACKUPS="No backups found. Click 'Backup Now' to create your first backup." -COM_MOKOBACKUP_NO_PROFILES="No backup profiles found." -COM_MOKOBACKUP_UPDATE_SITE_NOTICE="To receive automatic updates, configure your Update Site with your download key." -COM_MOKOBACKUP_UPDATE_SITE_MISSING="MokoJoomBackup update site not found. Reinstall the package to register the update server." -COM_MOKOBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." -COM_MOKOBACKUP_CONFIG_GENERAL="General" -COM_MOKOBACKUP_CONFIG_DEFAULT_BACKUP_DIR="Default Backup Directory" -COM_MOKOBACKUP_CONFIG_DEFAULT_BACKUP_DIR_DESC="Default directory for backup archives, relative to Joomla root. Can be overridden per profile." -COM_MOKOBACKUP_CONFIG_DEFAULT_PROFILE="Default Profile" -COM_MOKOBACKUP_CONFIG_DEFAULT_PROFILE_DESC="Default backup profile used by quick actions and CLI when no profile is specified." -COM_MOKOBACKUP_CONFIG_SHOW_UPDATE_NOTICE="Show Update Site Notice" -COM_MOKOBACKUP_CONFIG_SHOW_UPDATE_NOTICE_DESC="Display the update site configuration notice on the Backup Records view." -COM_MOKOBACKUP_CONFIG_CLEANUP="Cleanup Defaults" -COM_MOKOBACKUP_CONFIG_MAX_AGE="Max Backup Age (days)" -COM_MOKOBACKUP_CONFIG_MAX_AGE_DESC="Default maximum age for backup records. Used by the system plugin and CLI cleanup command." -COM_MOKOBACKUP_CONFIG_MAX_BACKUPS="Max Backup Count" -COM_MOKOBACKUP_CONFIG_MAX_BACKUPS_DESC="Default maximum number of completed backups to retain." -COM_MOKOBACKUP_CONFIG_NOTIFICATIONS="Notifications" -COM_MOKOBACKUP_CONFIG_NOTIFY_EMAIL="Global Notification Email(s)" -COM_MOKOBACKUP_CONFIG_NOTIFY_EMAIL_DESC="Comma-separated list of email addresses for global backup notifications. Per-profile settings override this." -COM_MOKOBACKUP_CONFIG_NOTIFY_SUCCESS="Notify on Success" -COM_MOKOBACKUP_CONFIG_NOTIFY_SUCCESS_DESC="Send email when any backup completes successfully (unless overridden by profile)." -COM_MOKOBACKUP_CONFIG_NOTIFY_FAILURE="Notify on Failure" -COM_MOKOBACKUP_CONFIG_NOTIFY_FAILURE_DESC="Send email when any backup fails (unless overridden by profile)." -COM_MOKOBACKUP_FOLDER_EXISTS="Directory exists" -COM_MOKOBACKUP_FOLDER_NOT_FOUND="Directory not found" -COM_MOKOBACKUP_BACKUP_DIR_DEFAULT="Default (inside web root)" -COM_MOKOBACKUP_DASHBOARD_DEFAULT_DIR_WARNING_TITLE="Backup directory is inside the web root" -COM_MOKOBACKUP_DASHBOARD_DEFAULT_DIR_WARNING="One or more profiles store backups in the default directory inside the web root. This may expose backup archives if .htaccess is not supported. Move backups to a directory outside the web root for better security." -COM_MOKOBACKUP_FOLDER_EXISTS="Directory exists" -COM_MOKOBACKUP_FOLDER_NOT_FOUND="Directory not found" -COM_MOKOBACKUP_BACKUP_DIR_DEFAULT="Default (inside web root)" -COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES_HELP="Check tables to exclude from database backup. Use Data to skip row data (keeps structure), Structure to skip CREATE TABLE, or both to fully exclude." -COM_MOKOBACKUP_FIELD_EXCLUDE_DATA="Data" -COM_MOKOBACKUP_FIELD_EXCLUDE_STRUCTURE="Structure" -COM_MOKOBACKUP_FIELD_TABLE_NAME="Table Name" -COM_MOKOBACKUP_VIEW_LOG="Backup Log" -COM_MOKOBACKUP_FIELD_CHECKSUM="SHA-256 Checksum" -COM_MOKOBACKUP_FIELD_PATH="File Path" -COM_MOKOBACKUP_FIELD_DB_SIZE="DB Size" -COM_MOKOBACKUP_FIELD_REMOTE="Remote Path" -COM_MOKOBACKUP_FIELD_NOTIFY_USER_GROUPS="Notify User Groups" -COM_MOKOBACKUP_FIELD_NOTIFY_USER_GROUPS_DESC="Select Joomla user groups whose members will receive backup notifications. Combined with email addresses above." diff --git a/source/packages/com_mokobackup/language/en-US/com_mokobackup.sys.ini b/source/packages/com_mokobackup/language/en-US/com_mokobackup.sys.ini deleted file mode 100644 index 96e51f2..0000000 --- a/source/packages/com_mokobackup/language/en-US/com_mokobackup.sys.ini +++ /dev/null @@ -1,10 +0,0 @@ -; MokoJoomBackup — Component system language file (en-US) -; @package MokoJoomBackup -; @author Moko Consulting -; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. -; @license GPL-3.0-or-later - -COM_MOKOBACKUP="MokoJoomBackup" -COM_MOKOBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration." -COM_MOKOBACKUP_SUBMENU_BACKUPS="Backup Records" -COM_MOKOBACKUP_SUBMENU_PROFILES="Backup Profiles" diff --git a/source/packages/com_mokobackup/sql/uninstall.mysql.sql b/source/packages/com_mokobackup/sql/uninstall.mysql.sql deleted file mode 100644 index 8df7cde..0000000 --- a/source/packages/com_mokobackup/sql/uninstall.mysql.sql +++ /dev/null @@ -1,2 +0,0 @@ -DROP TABLE IF EXISTS `#__mokobackup_records`; -DROP TABLE IF EXISTS `#__mokobackup_profiles`; diff --git a/source/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql b/source/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql deleted file mode 100644 index ef33f11..0000000 --- a/source/packages/com_mokobackup/sql/updates/mysql/01.01.01.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE `#__mokobackup_profiles` CHANGE `include_kickstart` `include_mokorestore` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Include MokoRestore standalone restore script in archive'; diff --git a/source/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql b/source/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql deleted file mode 100644 index ddc43d7..0000000 --- a/source/packages/com_mokobackup/sql/updates/mysql/01.01.02.sql +++ /dev/null @@ -1,12 +0,0 @@ --- MokoJoomBackup 01.01.02 --- Consolidated schema updates: NULL defaults, notifications, archive name format - --- Fix: allow NULL defaults for manifest and log columns -ALTER TABLE `#__mokobackup_records` MODIFY `manifest` LONGTEXT DEFAULT NULL; -ALTER TABLE `#__mokobackup_records` MODIFY `log` MEDIUMTEXT DEFAULT NULL; - --- Add user group notifications column to profiles -ALTER TABLE `#__mokobackup_profiles` ADD COLUMN `notify_user_groups` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Comma-separated Joomla user group IDs' AFTER `notify_email`; - --- Add archive_name_format column with placeholder support -ALTER TABLE `#__mokobackup_profiles` ADD COLUMN `archive_name_format` VARCHAR(512) NOT NULL DEFAULT '[host]_[datetime]_profile[profile_id]' COMMENT 'Filename format with placeholders' AFTER `backup_dir`; diff --git a/source/packages/com_mokobackup/cli/mokobackup.php b/source/packages/com_mokojoombackup/cli/mokojoombackup.php similarity index 89% rename from source/packages/com_mokobackup/cli/mokobackup.php rename to source/packages/com_mokojoombackup/cli/mokojoombackup.php index 47f030e..9135706 100644 --- a/source/packages/com_mokobackup/cli/mokobackup.php +++ b/source/packages/com_mokojoombackup/cli/mokojoombackup.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -10,7 +10,7 @@ * CLI backup script for cron/scheduled use. * * Usage: - * php cli/mokobackup.php --profile=1 --description="Scheduled backup" + * php cli/mokojoombackup.php --profile=1 --description="Scheduled backup" * * Must be run from the Joomla root directory. */ @@ -30,7 +30,7 @@ if (!defined('JPATH_BASE')) { require_once JPATH_BASE . '/includes/framework.php'; use Joomla\CMS\Factory; -use Joomla\Component\MokoBackup\Administrator\Engine\BackupEngine; +use Joomla\Component\MokoJoomBackup\Administrator\Engine\BackupEngine; // Parse CLI arguments $profileId = 1; diff --git a/source/packages/com_mokobackup/api/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/api/index.html similarity index 100% rename from source/packages/com_mokobackup/api/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/api/index.html diff --git a/source/packages/com_mokobackup/api/src/Controller/BackupsController.php b/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/Controller/BackupsController.php similarity index 84% rename from source/packages/com_mokobackup/api/src/Controller/BackupsController.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/api/src/Controller/BackupsController.php index ab7c746..1f5b829 100644 --- a/source/packages/com_mokobackup/api/src/Controller/BackupsController.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/Controller/BackupsController.php @@ -2,18 +2,18 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Api\Controller; +namespace Joomla\Component\MokoJoomBackup\Api\Controller; defined('_JEXEC') or die; use Joomla\CMS\MVC\Controller\ApiController; -use Joomla\Component\MokoBackup\Administrator\Engine\BackupEngine; +use Joomla\Component\MokoJoomBackup\Administrator\Engine\BackupEngine; class BackupsController extends ApiController { @@ -21,7 +21,7 @@ class BackupsController extends ApiController protected $default_view = 'backups'; /** - * Start a new backup (POST /api/index.php/v1/mokobackup/backup) + * Start a new backup (POST /api/index.php/v1/mokojoombackup/backup) */ public function backup(): static { @@ -47,7 +47,7 @@ class BackupsController extends ApiController } /** - * Download a backup archive (GET /api/index.php/v1/mokobackup/backup/:id/download) + * Download a backup archive (GET /api/index.php/v1/mokojoombackup/backup/:id/download) */ public function download(): static { @@ -74,7 +74,7 @@ class BackupsController extends ApiController } /** - * List backup profiles (GET /api/index.php/v1/mokobackup/profiles) + * List backup profiles (GET /api/index.php/v1/mokojoombackup/profiles) */ public function profiles(): static { diff --git a/source/packages/com_mokobackup/api/src/Controller/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/Controller/index.html similarity index 100% rename from source/packages/com_mokobackup/api/src/Controller/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/api/src/Controller/index.html diff --git a/source/packages/com_mokobackup/api/src/View/Backups/JsonapiView.php b/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/Backups/JsonapiView.php similarity index 90% rename from source/packages/com_mokobackup/api/src/View/Backups/JsonapiView.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/Backups/JsonapiView.php index d9e8a8a..f39925f 100644 --- a/source/packages/com_mokobackup/api/src/View/Backups/JsonapiView.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/Backups/JsonapiView.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Api\View\Backups; +namespace Joomla\Component\MokoJoomBackup\Api\View\Backups; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/api/src/View/Backups/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/Backups/index.html similarity index 100% rename from source/packages/com_mokobackup/api/src/View/Backups/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/Backups/index.html diff --git a/source/packages/com_mokobackup/api/src/View/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/index.html similarity index 100% rename from source/packages/com_mokobackup/api/src/View/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/index.html diff --git a/source/packages/com_mokobackup/api/src/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/index.html similarity index 100% rename from source/packages/com_mokobackup/api/src/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/api/src/index.html diff --git a/source/packages/com_mokobackup/cli/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/cli/index.html similarity index 100% rename from source/packages/com_mokobackup/cli/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/cli/index.html diff --git a/source/packages/com_mokobackup/config.xml b/source/packages/com_mokojoombackup/com_mokojoombackup/config.xml similarity index 52% rename from source/packages/com_mokobackup/config.xml rename to source/packages/com_mokojoombackup/com_mokojoombackup/config.xml index d1a0011..089c63b 100644 --- a/source/packages/com_mokobackup/config.xml +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/config.xml @@ -1,27 +1,27 @@ -
    +
    @@ -29,8 +29,8 @@ @@ -39,12 +39,12 @@
    -
    +
    -
    +
    @@ -83,8 +83,8 @@ @@ -101,7 +101,7 @@ label="JCONFIG_PERMISSIONS_LABEL" filter="rules" validate="rules" - component="com_mokobackup" + component="com_mokojoombackup" section="component" />
    diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/forms/backup.xml b/source/packages/com_mokojoombackup/com_mokojoombackup/forms/backup.xml new file mode 100644 index 0000000..207f50b --- /dev/null +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/forms/backup.xml @@ -0,0 +1,15 @@ + +
    +
    + + + + + + + + + + +
    +
    diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_backups.xml b/source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_backups.xml new file mode 100644 index 0000000..11af4cc --- /dev/null +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_backups.xml @@ -0,0 +1,47 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/source/packages/com_mokobackup/forms/filter_profiles.xml b/source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_profiles.xml similarity index 81% rename from source/packages/com_mokobackup/forms/filter_profiles.xml rename to source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_profiles.xml index 0025a94..b3ec39a 100644 --- a/source/packages/com_mokobackup/forms/filter_profiles.xml +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_profiles.xml @@ -4,7 +4,7 @@ - - + + diff --git a/source/packages/com_mokobackup/forms/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/forms/index.html similarity index 100% rename from source/packages/com_mokobackup/forms/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/forms/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/forms/profile.xml b/source/packages/com_mokojoombackup/com_mokojoombackup/forms/profile.xml new file mode 100644 index 0000000..34701a8 --- /dev/null +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/forms/profile.xml @@ -0,0 +1,373 @@ + +
    +
    + + + + + + + + +
    + +
    + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + +
    + + + +
    + +
    + + + + + + + + + + +
    + +
    + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + +
    + + + + +
    + +
    + + + + + + +
    +
    diff --git a/source/packages/com_mokobackup/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/index.html similarity index 100% rename from source/packages/com_mokobackup/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/index.html diff --git a/source/packages/com_mokobackup/language/en-GB/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/language/en-GB/index.html similarity index 100% rename from source/packages/com_mokobackup/language/en-GB/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/language/en-GB/index.html diff --git a/source/packages/com_mokobackup/language/en-US/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/language/en-US/index.html similarity index 100% rename from source/packages/com_mokobackup/language/en-US/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/language/en-US/index.html diff --git a/source/packages/com_mokobackup/language/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/language/index.html similarity index 100% rename from source/packages/com_mokobackup/language/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/language/index.html diff --git a/source/packages/com_mokobackup/services/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/services/index.html similarity index 100% rename from source/packages/com_mokobackup/services/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/services/index.html diff --git a/source/packages/com_mokobackup/services/provider.php b/source/packages/com_mokojoombackup/com_mokojoombackup/services/provider.php similarity index 82% rename from source/packages/com_mokobackup/services/provider.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/services/provider.php index cd6bc5b..af40114 100644 --- a/source/packages/com_mokobackup/services/provider.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/services/provider.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -15,20 +15,20 @@ use Joomla\CMS\Extension\ComponentInterface; use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory; use Joomla\CMS\Extension\Service\Provider\MVCFactory; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; -use Joomla\Component\MokoBackup\Administrator\Extension\MokoBackupComponent; +use Joomla\Component\MokoJoomBackup\Administrator\Extension\MokoJoomBackupComponent; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; return new class () implements ServiceProviderInterface { public function register(Container $container): void { - $container->registerServiceProvider(new MVCFactory('\\Joomla\\Component\\MokoBackup')); - $container->registerServiceProvider(new ComponentDispatcherFactory('\\Joomla\\Component\\MokoBackup')); + $container->registerServiceProvider(new MVCFactory('\\Joomla\\Component\\MokoJoomBackup')); + $container->registerServiceProvider(new ComponentDispatcherFactory('\\Joomla\\Component\\MokoJoomBackup')); $container->set( ComponentInterface::class, function (Container $container) { - $component = new MokoBackupComponent( + $component = new MokoJoomBackupComponent( $container->get(ComponentDispatcherFactoryInterface::class) ); $component->setMVCFactory($container->get(MVCFactoryInterface::class)); diff --git a/source/packages/com_mokobackup/sql/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/index.html similarity index 100% rename from source/packages/com_mokobackup/sql/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/sql/index.html diff --git a/source/packages/com_mokobackup/sql/install.mysql.sql b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/install.mysql.sql similarity index 93% rename from source/packages/com_mokobackup/sql/install.mysql.sql rename to source/packages/com_mokojoombackup/com_mokojoombackup/sql/install.mysql.sql index c9c25d3..f4af653 100644 --- a/source/packages/com_mokobackup/sql/install.mysql.sql +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/install.mysql.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS `#__mokobackup_profiles` ( +CREATE TABLE IF NOT EXISTS `#__mokojoombackup_profiles` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `title` VARCHAR(255) NOT NULL DEFAULT '', `description` TEXT NOT NULL, @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS `#__mokobackup_profiles` ( `archive_format` VARCHAR(10) NOT NULL DEFAULT 'zip', `compression_level` TINYINT(1) UNSIGNED NOT NULL DEFAULT 5 COMMENT '0=none, 9=max', `split_size` INT(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '0=no split, otherwise MB per part', - `backup_dir` VARCHAR(512) NOT NULL DEFAULT 'administrator/components/com_mokobackup/backups', + `backup_dir` VARCHAR(512) NOT NULL DEFAULT 'administrator/components/com_mokojoombackup/backups', `archive_name_format` VARCHAR(512) NOT NULL DEFAULT '[host]_[datetime]_profile[profile_id]' COMMENT 'Filename format with placeholders', `exclude_dirs` TEXT NOT NULL COMMENT 'Newline-separated directory paths to exclude', `exclude_files` TEXT NOT NULL COMMENT 'Newline-separated filename patterns to exclude', @@ -44,7 +44,7 @@ CREATE TABLE IF NOT EXISTS `#__mokobackup_profiles` ( KEY `idx_published` (`published`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -CREATE TABLE IF NOT EXISTS `#__mokobackup_records` ( +CREATE TABLE IF NOT EXISTS `#__mokojoombackup_records` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `profile_id` INT(11) UNSIGNED NOT NULL DEFAULT 1, `description` VARCHAR(255) NOT NULL DEFAULT '', @@ -74,15 +74,15 @@ CREATE TABLE IF NOT EXISTS `#__mokobackup_records` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Insert default backup profile -INSERT INTO `#__mokobackup_profiles` ( +INSERT INTO `#__mokojoombackup_profiles` ( `id`, `title`, `description`, `backup_type`, `archive_format`, `compression_level`, `split_size`, `backup_dir`, `exclude_dirs`, `exclude_files`, `exclude_tables`, `published`, `ordering`, `created`, `modified` ) VALUES ( 1, 'Default Backup Profile', 'Full site backup with default settings', 'full', - 'zip', 5, 0, 'administrator/components/com_mokobackup/backups', - 'administrator/components/com_mokobackup/backups\ntmp\ncache\nlogs\nadministrator/logs', + 'zip', 5, 0, 'administrator/components/com_mokojoombackup/backups', + 'administrator/components/com_mokojoombackup/backups\ntmp\ncache\nlogs\nadministrator/logs', '.gitignore\n.htaccess.bak', '#__session', 1, 1, NOW(), NOW() diff --git a/source/packages/com_mokobackup/sql/mysql/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/mysql/index.html similarity index 100% rename from source/packages/com_mokobackup/sql/mysql/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/sql/mysql/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/uninstall.mysql.sql b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/uninstall.mysql.sql new file mode 100644 index 0000000..974f591 --- /dev/null +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/uninstall.mysql.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS `#__mokojoombackup_records`; +DROP TABLE IF EXISTS `#__mokojoombackup_profiles`; diff --git a/source/packages/com_mokobackup/sql/updates/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/index.html similarity index 100% rename from source/packages/com_mokobackup/sql/updates/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/index.html diff --git a/source/packages/com_mokobackup/sql/updates/mysql/01.00.00.sql b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.00.00.sql similarity index 100% rename from source/packages/com_mokobackup/sql/updates/mysql/01.00.00.sql rename to source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.00.00.sql diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.01.sql b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.01.sql new file mode 100644 index 0000000..ec8fb68 --- /dev/null +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.01.sql @@ -0,0 +1 @@ +ALTER TABLE `#__mokojoombackup_profiles` CHANGE `include_kickstart` `include_mokorestore` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Include MokoRestore standalone restore script in archive'; diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.02.sql b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.02.sql new file mode 100644 index 0000000..8b86fb7 --- /dev/null +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.02.sql @@ -0,0 +1,12 @@ +-- MokoJoomBackup 01.01.02 +-- Consolidated schema updates: NULL defaults, notifications, archive name format + +-- Fix: allow NULL defaults for manifest and log columns +ALTER TABLE `#__mokojoombackup_records` MODIFY `manifest` LONGTEXT DEFAULT NULL; +ALTER TABLE `#__mokojoombackup_records` MODIFY `log` MEDIUMTEXT DEFAULT NULL; + +-- Add user group notifications column to profiles +ALTER TABLE `#__mokojoombackup_profiles` ADD COLUMN `notify_user_groups` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Comma-separated Joomla user group IDs' AFTER `notify_email`; + +-- Add archive_name_format column with placeholder support +ALTER TABLE `#__mokojoombackup_profiles` ADD COLUMN `archive_name_format` VARCHAR(512) NOT NULL DEFAULT '[host]_[datetime]_profile[profile_id]' COMMENT 'Filename format with placeholders' AFTER `backup_dir`; diff --git a/source/packages/com_mokobackup/sql/updates/mysql/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/index.html similarity index 100% rename from source/packages/com_mokobackup/sql/updates/mysql/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/index.html diff --git a/source/packages/com_mokobackup/src/Controller/AjaxController.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/AjaxController.php similarity index 95% rename from source/packages/com_mokobackup/src/Controller/AjaxController.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/AjaxController.php index 7a01ebb..9e5c637 100644 --- a/source/packages/com_mokobackup/src/Controller/AjaxController.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/AjaxController.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -11,13 +11,13 @@ * Handles init and step requests from the admin UI JavaScript. */ -namespace Joomla\Component\MokoBackup\Administrator\Controller; +namespace Joomla\Component\MokoJoomBackup\Administrator\Controller; defined('_JEXEC') or die; use Joomla\CMS\MVC\Controller\BaseController; use Joomla\CMS\Session\Session; -use Joomla\Component\MokoBackup\Administrator\Engine\SteppedBackupEngine; +use Joomla\Component\MokoJoomBackup\Administrator\Engine\SteppedBackupEngine; class AjaxController extends BaseController { @@ -148,7 +148,7 @@ class AjaxController extends BaseController $db = \Joomla\CMS\Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName(['absolute_path', 'log'])) - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('id') . ' = ' . $id); $db->setQuery($query); $record = $db->loadObject(); diff --git a/source/packages/com_mokobackup/src/Controller/BackupController.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupController.php similarity index 70% rename from source/packages/com_mokobackup/src/Controller/BackupController.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupController.php index 459fe3f..7844749 100644 --- a/source/packages/com_mokobackup/src/Controller/BackupController.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupController.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Controller; +namespace Joomla\Component\MokoJoomBackup\Administrator\Controller; defined('_JEXEC') or die; @@ -16,5 +16,5 @@ use Joomla\CMS\MVC\Controller\FormController; class BackupController extends FormController { - protected $text_prefix = 'COM_MOKOBACKUP_BACKUP'; + protected $text_prefix = 'COM_MOKOJOOMBACKUP_BACKUP'; } diff --git a/source/packages/com_mokobackup/src/Controller/BackupsController.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupsController.php similarity index 67% rename from source/packages/com_mokobackup/src/Controller/BackupsController.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupsController.php index c8a3f15..c1a99ca 100644 --- a/source/packages/com_mokobackup/src/Controller/BackupsController.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupsController.php @@ -2,24 +2,24 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Controller; +namespace Joomla\Component\MokoJoomBackup\Administrator\Controller; defined('_JEXEC') or die; use Joomla\CMS\MVC\Controller\AdminController; use Joomla\CMS\Router\Route; -use Joomla\Component\MokoBackup\Administrator\Engine\BackupEngine; -use Joomla\Component\MokoBackup\Administrator\Engine\RestoreEngine; +use Joomla\Component\MokoJoomBackup\Administrator\Engine\BackupEngine; +use Joomla\Component\MokoJoomBackup\Administrator\Engine\RestoreEngine; class BackupsController extends AdminController { - protected $text_prefix = 'COM_MOKOBACKUP_BACKUPS'; + protected $text_prefix = 'COM_MOKOJOOMBACKUP_BACKUPS'; public function getModel($name = 'Backup', $prefix = 'Administrator', $config = ['ignore_request' => true]) { @@ -47,7 +47,7 @@ class BackupsController extends AdminController $this->setMessage($result['message'], 'error'); } - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=backups', false)); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=backups', false)); } /** @@ -62,8 +62,8 @@ class BackupsController extends AdminController $item = $model->getItem($id); if (!$item || !$item->id || !$item->filesexist || !is_file($item->absolute_path)) { - $this->setMessage('COM_MOKOBACKUP_ERROR_FILE_NOT_FOUND', 'error'); - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=backups', false)); + $this->setMessage('COM_MOKOJOOMBACKUP_ERROR_FILE_NOT_FOUND', 'error'); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=backups', false)); return; } @@ -108,8 +108,8 @@ class BackupsController extends AdminController $password = $this->input->getString('encryption_password', ''); if (!$id) { - $this->setMessage('COM_MOKOBACKUP_ERROR_NO_RECORD_SELECTED', 'error'); - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=backups', false)); + $this->setMessage('COM_MOKOJOOMBACKUP_ERROR_NO_RECORD_SELECTED', 'error'); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=backups', false)); return; } @@ -123,7 +123,7 @@ class BackupsController extends AdminController $this->setMessage($result['message'], 'error'); } - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=backups', false)); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=backups', false)); } /** @@ -137,8 +137,8 @@ class BackupsController extends AdminController $id = !empty($cid) ? (int) $cid[0] : $this->input->getInt('id', 0); if (!$id) { - $this->setMessage('COM_MOKOBACKUP_ERROR_NO_RECORD_SELECTED', 'error'); - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=backups', false)); + $this->setMessage('COM_MOKOJOOMBACKUP_ERROR_NO_RECORD_SELECTED', 'error'); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=backups', false)); return; } @@ -147,22 +147,22 @@ class BackupsController extends AdminController $item = $model->getItem($id); if (!$item || !$item->id) { - $this->setMessage('COM_MOKOBACKUP_ERROR_NO_RECORD_SELECTED', 'error'); - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=backups', false)); + $this->setMessage('COM_MOKOJOOMBACKUP_ERROR_NO_RECORD_SELECTED', 'error'); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=backups', false)); return; } if (!is_file($item->absolute_path)) { - $this->setMessage('COM_MOKOBACKUP_ERROR_FILE_NOT_FOUND', 'error'); - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=backups', false)); + $this->setMessage('COM_MOKOJOOMBACKUP_ERROR_FILE_NOT_FOUND', 'error'); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=backups', false)); return; } if (empty($item->checksum)) { - $this->setMessage('COM_MOKOBACKUP_VERIFY_NO_CHECKSUM', 'warning'); - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=backups', false)); + $this->setMessage('COM_MOKOJOOMBACKUP_VERIFY_NO_CHECKSUM', 'warning'); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=backups', false)); return; } @@ -170,11 +170,11 @@ class BackupsController extends AdminController $currentHash = hash_file('sha256', $item->absolute_path); if ($currentHash === $item->checksum) { - $this->setMessage('COM_MOKOBACKUP_VERIFY_OK'); + $this->setMessage('COM_MOKOJOOMBACKUP_VERIFY_OK'); } else { - $this->setMessage('COM_MOKOBACKUP_VERIFY_FAILED', 'error'); + $this->setMessage('COM_MOKOJOOMBACKUP_VERIFY_FAILED', 'error'); } - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=backups', false)); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=backups', false)); } } diff --git a/source/packages/com_mokobackup/src/Controller/DisplayController.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/DisplayController.php similarity index 80% rename from source/packages/com_mokobackup/src/Controller/DisplayController.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/DisplayController.php index 5425324..cac40fd 100644 --- a/source/packages/com_mokobackup/src/Controller/DisplayController.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/DisplayController.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Controller; +namespace Joomla\Component\MokoJoomBackup\Administrator\Controller; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Controller/ProfileController.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfileController.php similarity index 70% rename from source/packages/com_mokobackup/src/Controller/ProfileController.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfileController.php index 5a84e2e..d7540e8 100644 --- a/source/packages/com_mokobackup/src/Controller/ProfileController.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfileController.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Controller; +namespace Joomla\Component\MokoJoomBackup\Administrator\Controller; defined('_JEXEC') or die; @@ -16,5 +16,5 @@ use Joomla\CMS\MVC\Controller\FormController; class ProfileController extends FormController { - protected $text_prefix = 'COM_MOKOBACKUP_PROFILE'; + protected $text_prefix = 'COM_MOKOJOOMBACKUP_PROFILE'; } diff --git a/source/packages/com_mokobackup/src/Controller/ProfilesController.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfilesController.php similarity index 85% rename from source/packages/com_mokobackup/src/Controller/ProfilesController.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfilesController.php index 9470eb4..fa0ad47 100644 --- a/source/packages/com_mokobackup/src/Controller/ProfilesController.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfilesController.php @@ -2,24 +2,24 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Controller; +namespace Joomla\Component\MokoJoomBackup\Administrator\Controller; defined('_JEXEC') or die; use Joomla\CMS\Factory; use Joomla\CMS\MVC\Controller\AdminController; use Joomla\CMS\Router\Route; -use Joomla\Component\MokoBackup\Administrator\Engine\AkeebaImporter; +use Joomla\Component\MokoJoomBackup\Administrator\Engine\AkeebaImporter; class ProfilesController extends AdminController { - protected $text_prefix = 'COM_MOKOBACKUP_PROFILES'; + protected $text_prefix = 'COM_MOKOJOOMBACKUP_PROFILES'; public function getModel($name = 'Profile', $prefix = 'Administrator', $config = ['ignore_request' => true]) { @@ -39,8 +39,8 @@ class ProfilesController extends AdminController $detection = $importer->detect(); if (!$detection['profiles']) { - $this->setMessage('COM_MOKOBACKUP_AKEEBA_NOT_FOUND', 'error'); - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=profiles', false)); + $this->setMessage('COM_MOKOJOOMBACKUP_AKEEBA_NOT_FOUND', 'error'); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=profiles', false)); return; } @@ -55,7 +55,7 @@ class ProfilesController extends AdminController $this->setMessage($result['message'], 'error'); } - $this->setRedirect(Route::_('index.php?option=com_mokobackup&view=profiles', false)); + $this->setRedirect(Route::_('index.php?option=com_mokojoombackup&view=profiles', false)); } /** diff --git a/source/packages/com_mokobackup/src/Controller/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/index.html similarity index 100% rename from source/packages/com_mokobackup/src/Controller/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/index.html diff --git a/source/packages/com_mokobackup/src/Engine/AkeebaImporter.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/AkeebaImporter.php similarity index 96% rename from source/packages/com_mokobackup/src/Engine/AkeebaImporter.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/AkeebaImporter.php index 3edbe14..c1fab5b 100644 --- a/source/packages/com_mokobackup/src/Engine/AkeebaImporter.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/AkeebaImporter.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -25,7 +25,7 @@ * "databases": {"include": {...}, "exclude": {...}}} */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; @@ -119,7 +119,7 @@ class AkeebaImporter $akProfiles = $db->loadObjectList(); $profilesImported = 0; - $profileIdMap = []; // akeeba_id => mokobackup_id + $profileIdMap = []; // akeeba_id => mokojoombackup_id foreach ($akProfiles as $akProfile) { $config = $this->parseAkeebaConfig($akProfile->configuration ?? ''); @@ -127,11 +127,11 @@ class AkeebaImporter $mokoProfile = $this->mapToMokoProfile($akProfile, $config, $filters); - $db->insertObject('#__mokobackup_profiles', $mokoProfile, 'id'); + $db->insertObject('#__mokojoombackup_profiles', $mokoProfile, 'id'); $profileIdMap[$akProfile->id] = $mokoProfile->id; $profilesImported++; - $this->log('Imported profile: "' . $akProfile->description . '" (Akeeba #' . $akProfile->id . ' → MokoBackup #' . $mokoProfile->id . ')'); + $this->log('Imported profile: "' . $akProfile->description . '" (Akeeba #' . $akProfile->id . ' → MokoJoomBackup #' . $mokoProfile->id . ')'); } // Import backup history @@ -200,7 +200,7 @@ class AkeebaImporter 'log' => 'Imported from Akeeba Backup record #' . $stat->id, ]; - $db->insertObject('#__mokobackup_records', $record, 'id'); + $db->insertObject('#__mokojoombackup_records', $record, 'id'); $imported++; } @@ -484,7 +484,7 @@ class AkeebaImporter $dir = $config['akeeba.basic.output_directory'] ?? ''; if (empty($dir) || $dir === '[DEFAULT_OUTPUT]') { - return 'administrator/components/com_mokobackup/backups'; + return 'administrator/components/com_mokojoombackup/backups'; } // Convert absolute path to relative @@ -492,7 +492,7 @@ class AkeebaImporter $dir = ltrim(substr($dir, strlen(JPATH_ROOT)), '/\\'); } - return $dir ?: 'administrator/components/com_mokobackup/backups'; + return $dir ?: 'administrator/components/com_mokojoombackup/backups'; } private function mapRemoteStorage(array $config): string diff --git a/source/packages/com_mokobackup/src/Engine/ArchiverInterface.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ArchiverInterface.php similarity index 89% rename from source/packages/com_mokobackup/src/Engine/ArchiverInterface.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ArchiverInterface.php index 8edfdfb..b496059 100644 --- a/source/packages/com_mokobackup/src/Engine/ArchiverInterface.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ArchiverInterface.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/BackupEngine.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/BackupEngine.php similarity index 95% rename from source/packages/com_mokobackup/src/Engine/BackupEngine.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/BackupEngine.php index 59254f4..43d5d91 100644 --- a/source/packages/com_mokobackup/src/Engine/BackupEngine.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/BackupEngine.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; @@ -46,7 +46,7 @@ class BackupEngine // Load profile $query = $db->getQuery(true) ->select('*') - ->from($db->quoteName('#__mokobackup_profiles')) + ->from($db->quoteName('#__mokojoombackup_profiles')) ->where($db->quoteName('id') . ' = ' . $profileId); $db->setQuery($query); $profile = $db->loadObject(); @@ -63,7 +63,7 @@ class BackupEngine // Resolve placeholders in directory and filename $resolver = new PlaceholderResolver($profile); - $configuredDir = $profile->backup_dir ?: 'administrator/components/com_mokobackup/backups'; + $configuredDir = $profile->backup_dir ?: 'administrator/components/com_mokojoombackup/backups'; $this->backupDir = $this->resolveBackupDir($resolver->resolve($configuredDir)); if (!is_dir($this->backupDir)) { @@ -104,7 +104,7 @@ class BackupEngine 'log' => '', ]; - $db->insertObject('#__mokobackup_records', $record, 'id'); + $db->insertObject('#__mokojoombackup_records', $record, 'id'); $recordId = $record->id; try { @@ -257,7 +257,7 @@ class BackupEngine 'log' => $logContent, ]; - $db->updateObject('#__mokobackup_records', $update, 'id'); + $db->updateObject('#__mokojoombackup_records', $update, 'id'); // Send success notification NotificationSender::send($profile, $update, true, implode("\n", $this->log)); @@ -285,7 +285,7 @@ class BackupEngine 'log' => implode("\n", $this->log), ]; - $db->updateObject('#__mokobackup_records', $update, 'id'); + $db->updateObject('#__mokojoombackup_records', $update, 'id'); // Send failure notification NotificationSender::send($profile, $update, false, implode("\n", $this->log)); @@ -405,7 +405,7 @@ class BackupEngine { $query = $db->getQuery(true) ->select($db->quoteName('manifest')) - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('profile_id') . ' = ' . $profileId) ->where($db->quoteName('status') . ' = ' . $db->quote('complete')) ->where($db->quoteName('manifest') . ' != ' . $db->quote('')) @@ -476,14 +476,14 @@ class BackupEngine } /** - * Dispatch the onMokoBackupAfterRun event so plugins (actionlog, etc.) can react. + * Dispatch the onMokoJoomBackupAfterRun event so plugins (actionlog, etc.) can react. */ private function dispatchAfterRun(bool $success, int $recordId, string $description, int $profileId, string $origin): void { try { $app = Factory::getApplication(); - $event = new Event('onMokoBackupAfterRun', [ + $event = new Event('onMokoJoomBackupAfterRun', [ 'success' => $success, 'record_id' => $recordId, 'description' => $description, @@ -491,7 +491,7 @@ class BackupEngine 'origin' => $origin, ]); - $app->getDispatcher()->dispatch('onMokoBackupAfterRun', $event); + $app->getDispatcher()->dispatch('onMokoJoomBackupAfterRun', $event); } catch (\Throwable $e) { // Never let a listener failure break the backup result } diff --git a/source/packages/com_mokobackup/src/Engine/DatabaseDumper.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseDumper.php similarity index 98% rename from source/packages/com_mokobackup/src/Engine/DatabaseDumper.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseDumper.php index 05661c5..f4bf538 100644 --- a/source/packages/com_mokobackup/src/Engine/DatabaseDumper.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseDumper.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/DatabaseImporter.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseImporter.php similarity index 92% rename from source/packages/com_mokobackup/src/Engine/DatabaseImporter.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseImporter.php index ec806d6..4e178de 100644 --- a/source/packages/com_mokobackup/src/Engine/DatabaseImporter.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseImporter.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -12,7 +12,7 @@ * and DROP TABLE before CREATE TABLE for clean restores. */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; @@ -101,7 +101,7 @@ class DatabaseImporter // Log but don't abort — some statements may fail on // different MySQL versions (e.g. charset differences) // but the overall restore should continue. - error_log('MokoBackup SQL import warning: ' . $e->getMessage()); + error_log('MokoJoomBackup SQL import warning: ' . $e->getMessage()); } } } @@ -115,7 +115,7 @@ class DatabaseImporter $db->execute(); $statementsExecuted++; } catch (\Exception $e) { - error_log('MokoBackup SQL import warning (final): ' . $e->getMessage()); + error_log('MokoJoomBackup SQL import warning (final): ' . $e->getMessage()); } } } finally { diff --git a/source/packages/com_mokobackup/src/Engine/DifferentialScanner.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DifferentialScanner.php similarity index 96% rename from source/packages/com_mokobackup/src/Engine/DifferentialScanner.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DifferentialScanner.php index ce477f5..0996b9b 100644 --- a/source/packages/com_mokobackup/src/Engine/DifferentialScanner.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DifferentialScanner.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -15,7 +15,7 @@ * {"path/to/file": {"size": 1234, "mtime": 1717350000}, ...} */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/FileRestorer.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileRestorer.php similarity index 96% rename from source/packages/com_mokobackup/src/Engine/FileRestorer.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileRestorer.php index fc2e72a..0bad937 100644 --- a/source/packages/com_mokobackup/src/Engine/FileRestorer.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileRestorer.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -11,7 +11,7 @@ * Skips database.sql and sensitive files that should not be overwritten. */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/FileScanner.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileScanner.php similarity index 96% rename from source/packages/com_mokobackup/src/Engine/FileScanner.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileScanner.php index aaa0577..f64884a 100644 --- a/source/packages/com_mokobackup/src/Engine/FileScanner.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileScanner.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/FtpUploader.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FtpUploader.php similarity index 97% rename from source/packages/com_mokobackup/src/Engine/FtpUploader.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FtpUploader.php index b370f34..9d585b3 100644 --- a/source/packages/com_mokobackup/src/Engine/FtpUploader.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FtpUploader.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/GoogleDriveUploader.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/GoogleDriveUploader.php similarity index 98% rename from source/packages/com_mokobackup/src/Engine/GoogleDriveUploader.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/GoogleDriveUploader.php index 1197ec6..3cf9c80 100644 --- a/source/packages/com_mokobackup/src/Engine/GoogleDriveUploader.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/GoogleDriveUploader.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -12,7 +12,7 @@ * No SDK dependency — pure PHP with cURL. */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/JpaUnarchiver.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/JpaUnarchiver.php similarity index 98% rename from source/packages/com_mokobackup/src/Engine/JpaUnarchiver.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/JpaUnarchiver.php index e732237..768cff8 100644 --- a/source/packages/com_mokobackup/src/Engine/JpaUnarchiver.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/JpaUnarchiver.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -19,7 +19,7 @@ * The RestoreEngine can then restore from the extracted files. */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/MokoRestore.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/MokoRestore.php similarity index 99% rename from source/packages/com_mokobackup/src/Engine/MokoRestore.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/MokoRestore.php index 5321485..e17020b 100644 --- a/source/packages/com_mokobackup/src/Engine/MokoRestore.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/MokoRestore.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -21,7 +21,7 @@ * with a Joomla-styled wizard interface. */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; @@ -97,7 +97,7 @@ class MokoRestore error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED); ini_set('display_errors', 0); -define('MOKOBACKUP_RESTORE', 1); +define('MOKOJOOMBACKUP_RESTORE', 1); define('RESTORE_DIR', __DIR__); define('BACKUP_FILE', RESTORE_DIR . '/site-backup.zip'); diff --git a/source/packages/com_mokobackup/src/Engine/NotificationSender.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/NotificationSender.php similarity index 94% rename from source/packages/com_mokobackup/src/Engine/NotificationSender.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/NotificationSender.php index 9008aa2..b26f7c0 100644 --- a/source/packages/com_mokobackup/src/Engine/NotificationSender.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/NotificationSender.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -11,7 +11,7 @@ * Uses Joomla's built-in mail system (Factory::getMailer()). */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; @@ -73,7 +73,7 @@ class NotificationSender // Build subject $statusLabel = $success ? 'SUCCESS' : 'FAILED'; - $mailer->setSubject("[MokoBackup] {$statusLabel}: {$record->description} — {$siteName}"); + $mailer->setSubject("[MokoJoomBackup] {$statusLabel}: {$record->description} — {$siteName}"); // Build body $duration = ''; @@ -133,7 +133,7 @@ class NotificationSender return $mailer->Send(); } catch (\Throwable $e) { // Don't let notification failure break the backup flow - error_log('MokoBackup notification error: ' . $e->getMessage()); + error_log('MokoJoomBackup notification error: ' . $e->getMessage()); return false; } diff --git a/source/packages/com_mokobackup/src/Engine/PlaceholderResolver.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/PlaceholderResolver.php similarity index 97% rename from source/packages/com_mokobackup/src/Engine/PlaceholderResolver.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/PlaceholderResolver.php index 712b219..cbac2c9 100644 --- a/source/packages/com_mokobackup/src/Engine/PlaceholderResolver.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/PlaceholderResolver.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -11,7 +11,7 @@ * directory paths and archive filename formats. */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/RemoteUploaderInterface.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RemoteUploaderInterface.php similarity index 89% rename from source/packages/com_mokobackup/src/Engine/RemoteUploaderInterface.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RemoteUploaderInterface.php index 67c1808..d2f1dd7 100644 --- a/source/packages/com_mokobackup/src/Engine/RemoteUploaderInterface.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RemoteUploaderInterface.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/RestoreEngine.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RestoreEngine.php similarity index 96% rename from source/packages/com_mokobackup/src/Engine/RestoreEngine.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RestoreEngine.php index 7099957..64115c9 100644 --- a/source/packages/com_mokobackup/src/Engine/RestoreEngine.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RestoreEngine.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -18,7 +18,7 @@ * 6. Clean up staging directory */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; @@ -57,7 +57,7 @@ class RestoreEngine // Load backup record $query = $db->getQuery(true) ->select('*') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('id') . ' = ' . $recordId); $db->setQuery($query); $record = $db->loadObject(); @@ -77,7 +77,7 @@ class RestoreEngine } // Create staging directory - $this->stagingDir = JPATH_ROOT . '/tmp/mokobackup-restore-' . $record->tag; + $this->stagingDir = JPATH_ROOT . '/tmp/mokojoombackup-restore-' . $record->tag; if (is_dir($this->stagingDir)) { $this->recursiveDelete($this->stagingDir); diff --git a/source/packages/com_mokobackup/src/Engine/S3Uploader.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/S3Uploader.php similarity index 99% rename from source/packages/com_mokobackup/src/Engine/S3Uploader.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/S3Uploader.php index eeae5b4..0c489de 100644 --- a/source/packages/com_mokobackup/src/Engine/S3Uploader.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/S3Uploader.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -12,7 +12,7 @@ * No SDK dependency — pure PHP with cURL. */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedBackupEngine.php similarity index 96% rename from source/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedBackupEngine.php index 7a7559a..be900d5 100644 --- a/source/packages/com_mokobackup/src/Engine/SteppedBackupEngine.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedBackupEngine.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -16,7 +16,7 @@ * where ini_set() and set_time_limit() are disabled. */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; @@ -36,7 +36,7 @@ class SteppedBackupEngine // Load profile $query = $db->getQuery(true) ->select('*') - ->from($db->quoteName('#__mokobackup_profiles')) + ->from($db->quoteName('#__mokojoombackup_profiles')) ->where($db->quoteName('id') . ' = ' . $profileId); $db->setQuery($query); $profile = $db->loadObject(); @@ -55,7 +55,7 @@ class SteppedBackupEngine $session->excludeDirs = $this->parseNewlineList($profile->exclude_dirs ?? ''); $session->excludeFiles = $this->parseNewlineList($profile->exclude_files ?? ''); $session->excludeTables = $this->parseNewlineList($profile->exclude_tables ?? ''); - $session->backupDir = $profile->backup_dir ?: 'administrator/components/com_mokobackup/backups'; + $session->backupDir = $profile->backup_dir ?: 'administrator/components/com_mokojoombackup/backups'; $session->remoteStorage = $profile->remote_storage ?? 'none'; $session->includeMokoRestore = (bool) ($profile->include_mokorestore ?? false); $session->remoteKeepLocal = (bool) ($profile->remote_keep_local ?? true); @@ -99,7 +99,7 @@ class SteppedBackupEngine 'log' => '', ]; - $db->insertObject('#__mokobackup_records', $record, 'id'); + $db->insertObject('#__mokojoombackup_records', $record, 'id'); $session->recordId = $record->id; // Determine what work needs to be done and estimate steps @@ -339,7 +339,7 @@ class SteppedBackupEngine 'filesexist' => 1, ]; - $db->updateObject('#__mokobackup_records', $update, 'id'); + $db->updateObject('#__mokojoombackup_records', $update, 'id'); $session->currentStep++; $session->phase = ($session->remoteStorage !== 'none') ? 'upload' : 'complete'; @@ -361,7 +361,7 @@ class SteppedBackupEngine // Reload profile for remote settings $query = $db->getQuery(true) ->select('*') - ->from($db->quoteName('#__mokobackup_profiles')) + ->from($db->quoteName('#__mokojoombackup_profiles')) ->where($db->quoteName('id') . ' = ' . $session->profileId); $db->setQuery($query); $profile = $db->loadObject(); @@ -396,7 +396,7 @@ class SteppedBackupEngine 'filesexist' => is_file($session->archivePath) ? 1 : 0, ]; - $db->updateObject('#__mokobackup_records', $update, 'id'); + $db->updateObject('#__mokojoombackup_records', $update, 'id'); $session->currentStep++; $session->phase = 'complete'; @@ -423,7 +423,7 @@ class SteppedBackupEngine 'log' => $logContent, ]; - $db->updateObject('#__mokobackup_records', $update, 'id'); + $db->updateObject('#__mokojoombackup_records', $update, 'id'); } /** @@ -439,7 +439,7 @@ class SteppedBackupEngine 'log' => implode("\n", $session->log), ]; - $db->updateObject('#__mokobackup_records', $update, 'id'); + $db->updateObject('#__mokojoombackup_records', $update, 'id'); } /** diff --git a/source/packages/com_mokobackup/src/Engine/SteppedSession.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedSession.php similarity index 96% rename from source/packages/com_mokobackup/src/Engine/SteppedSession.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedSession.php index 76f000d..2e0255b 100644 --- a/source/packages/com_mokobackup/src/Engine/SteppedSession.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedSession.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -17,7 +17,7 @@ * Phases: init → database → files → finalize → upload → complete */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; @@ -62,7 +62,7 @@ class SteppedSession private static function getSessionDir(): string { - $dir = JPATH_ROOT . '/tmp/mokobackup-sessions'; + $dir = JPATH_ROOT . '/tmp/mokojoombackup-sessions'; if (!is_dir($dir)) { mkdir($dir, 0755, true); diff --git a/source/packages/com_mokobackup/src/Engine/TarGzArchiver.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/TarGzArchiver.php similarity index 93% rename from source/packages/com_mokobackup/src/Engine/TarGzArchiver.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/TarGzArchiver.php index fdce0ce..6e8e629 100644 --- a/source/packages/com_mokobackup/src/Engine/TarGzArchiver.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/TarGzArchiver.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/ZipArchiver.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ZipArchiver.php similarity index 90% rename from source/packages/com_mokobackup/src/Engine/ZipArchiver.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ZipArchiver.php index e161035..1b54551 100644 --- a/source/packages/com_mokobackup/src/Engine/ZipArchiver.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ZipArchiver.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Engine; +namespace Joomla\Component\MokoJoomBackup\Administrator\Engine; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Engine/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/index.html similarity index 100% rename from source/packages/com_mokobackup/src/Engine/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/index.html diff --git a/source/packages/com_mokobackup/src/Extension/MokoBackupComponent.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Extension/MokoBackupComponent.php similarity index 68% rename from source/packages/com_mokobackup/src/Extension/MokoBackupComponent.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Extension/MokoBackupComponent.php index a7a6ed9..f5dc0d5 100644 --- a/source/packages/com_mokobackup/src/Extension/MokoBackupComponent.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Extension/MokoBackupComponent.php @@ -2,18 +2,18 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Extension; +namespace Joomla\Component\MokoJoomBackup\Administrator\Extension; defined('_JEXEC') or die; use Joomla\CMS\Extension\MVCComponent; -class MokoBackupComponent extends MVCComponent +class MokoJoomBackupComponent extends MVCComponent { } diff --git a/source/packages/com_mokobackup/src/Extension/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/com_mokobackup/src/Extension/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Extension/index.html diff --git a/source/packages/com_mokobackup/src/Field/DatabaseTablesField.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DatabaseTablesField.php similarity index 90% rename from source/packages/com_mokobackup/src/Field/DatabaseTablesField.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DatabaseTablesField.php index 937202d..4da3578 100644 --- a/source/packages/com_mokobackup/src/Field/DatabaseTablesField.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DatabaseTablesField.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Field; +namespace Joomla\Component\MokoJoomBackup\Administrator\Field; defined('_JEXEC') or die; @@ -55,15 +55,15 @@ class DatabaseTablesField extends FormField $html = '
    '; $html .= ''; - $html .= '
    ' . Text::_('COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES_HELP') . '
    '; + $html .= '
    ' . Text::_('COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_TABLES_HELP') . '
    '; $html .= '
    '; $html .= ''; $html .= ''; $html .= ''; - $html .= ''; + $html .= ''; $html .= ''; - $html .= ''; - $html .= ''; + $html .= ''; + $html .= ''; $html .= ''; foreach ($tables as $table) { diff --git a/source/packages/com_mokobackup/src/Field/DirectoryFilterField.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DirectoryFilterField.php similarity index 95% rename from source/packages/com_mokobackup/src/Field/DirectoryFilterField.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DirectoryFilterField.php index c3cb816..ac9b663 100644 --- a/source/packages/com_mokobackup/src/Field/DirectoryFilterField.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DirectoryFilterField.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -11,7 +11,7 @@ * Loads the directory tree from the server via AJAX (browseDir endpoint). */ -namespace Joomla\Component\MokoBackup\Administrator\Field; +namespace Joomla\Component\MokoJoomBackup\Administrator\Field; defined('_JEXEC') or die; @@ -38,9 +38,9 @@ class DirectoryFilterField extends FormField $itemsJson = json_encode($items); $jRoot = json_encode(JPATH_ROOT); - $labelExclude = Text::_('COM_MOKOBACKUP_FILTER_EXCLUDED'); - $labelInclude = Text::_('COM_MOKOBACKUP_FILTER_INCLUDED'); - $labelManual = Text::_('COM_MOKOBACKUP_FILTER_ADD_MANUAL'); + $labelExclude = Text::_('COM_MOKOJOOMBACKUP_FILTER_EXCLUDED'); + $labelInclude = Text::_('COM_MOKOJOOMBACKUP_FILTER_INCLUDED'); + $labelManual = Text::_('COM_MOKOJOOMBACKUP_FILTER_ADD_MANUAL'); $addLabel = Text::_('JGLOBAL_FIELD_ADD'); $placeholder = htmlspecialchars((string) ($this->element['hint'] ?? 'path/to/directory'), ENT_QUOTES, 'UTF-8'); @@ -163,7 +163,7 @@ class DirectoryFilterField extends FormField const tokenName = Joomla.getOptions('csrf.token') || ''; if (tokenName) form.append(tokenName, '1'); - fetch('index.php?option=com_mokobackup&format=json', { + fetch('index.php?option=com_mokojoombackup&format=json', { method: 'POST', body: form, headers: { 'X-Requested-With': 'XMLHttpRequest' } }) diff --git a/source/packages/com_mokobackup/src/Field/ExcludeListField.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/ExcludeListField.php similarity index 97% rename from source/packages/com_mokobackup/src/Field/ExcludeListField.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/ExcludeListField.php index 483e68c..a170671 100644 --- a/source/packages/com_mokobackup/src/Field/ExcludeListField.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/ExcludeListField.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Field; +namespace Joomla\Component\MokoJoomBackup\Administrator\Field; defined('_JEXEC') or die; diff --git a/source/packages/com_mokobackup/src/Field/FolderPickerField.php b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/FolderPickerField.php similarity index 94% rename from source/packages/com_mokobackup/src/Field/FolderPickerField.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/FolderPickerField.php index 447725d..6e60dff 100644 --- a/source/packages/com_mokobackup/src/Field/FolderPickerField.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/FolderPickerField.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Component\MokoBackup\Administrator\Field; +namespace Joomla\Component\MokoJoomBackup\Administrator\Field; defined('_JEXEC') or die; @@ -39,15 +39,15 @@ class FolderPickerField extends FormField $statusClass = $exists ? 'text-success' : 'text-danger'; $statusIcon = $exists ? 'icon-publish' : 'icon-unpublish'; $statusText = $exists - ? Text::_('COM_MOKOBACKUP_FOLDER_EXISTS') - : Text::_('COM_MOKOBACKUP_FOLDER_NOT_FOUND'); + ? Text::_('COM_MOKOJOOMBACKUP_FOLDER_EXISTS') + : Text::_('COM_MOKOJOOMBACKUP_FOLDER_NOT_FOUND'); $absPathSafe = htmlspecialchars($absPath, ENT_QUOTES, 'UTF-8'); return << + placeholder="/home/user/backups or administrator/components/com_mokojoombackup/backups" />
    ' . Text::_('COM_MOKOBACKUP_FIELD_EXCLUDE_DATA') . '' . Text::_('COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_DATA') . '' . Text::_('COM_MOKOBACKUP_FIELD_EXCLUDE_STRUCTURE') . '' . Text::_('COM_MOKOBACKUP_FIELD_TABLE_NAME') . '' . Text::_('COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_STRUCTURE') . '' . Text::_('COM_MOKOJOOMBACKUP_FIELD_TABLE_NAME') . '
    - + - + - + - + - + - + - + - + - + - + item->checksum)) : ?> - + item->remote_filename)) : ?> - + @@ -95,7 +95,7 @@ $ajaxUrl = Route::_('index.php?option=com_mokobackup&format=json', false);
    item->status) { @@ -39,55 +39,55 @@ $ajaxUrl = Route::_('index.php?option=com_mokobackup&format=json', false);
    escape($this->item->backup_type); ?>
    escape($this->item->origin); ?>
    item->total_size); ?> item->db_size > 0) : ?> - (: item->db_size); ?>) + (: item->db_size); ?>)
    item->backupstart, Text::_('DATE_FORMAT_LC2')); ?>
    item->backupend, Text::_('DATE_FORMAT_LC2')); ?>
    escape($this->item->archivename); ?>
    escape($this->item->absolute_path); ?>
    item->files_count; ?>
    item->tables_count; ?>
    escape($this->item->checksum); ?>
    escape($this->item->remote_filename); ?>
    -

    +

    Loading...
    diff --git a/source/packages/com_mokobackup/tmpl/backup/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backup/index.html similarity index 100% rename from source/packages/com_mokobackup/tmpl/backup/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backup/index.html diff --git a/source/packages/com_mokobackup/tmpl/backups/default.php b/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backups/default.php similarity index 82% rename from source/packages/com_mokobackup/tmpl/backups/default.php rename to source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backups/default.php index cba340d..233852d 100644 --- a/source/packages/com_mokobackup/tmpl/backups/default.php +++ b/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backups/default.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage com_mokobackup + * @subpackage com_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -19,12 +19,12 @@ use Joomla\CMS\Session\Session; HTMLHelper::_('behavior.multiselect'); $ajaxToken = Session::getFormToken(); -$ajaxUrl = Route::_('index.php?option=com_mokobackup&format=json', false); +$ajaxUrl = Route::_('index.php?option=com_mokojoombackup&format=json', false); $listOrder = $this->escape($this->state->get('list.ordering')); $listDirn = $this->escape($this->state->get('list.direction')); ?> -
    +
    @@ -33,7 +33,7 @@ $listDirn = $this->escape($this->state->get('list.direction'));
    -
    @@ -56,36 +56,36 @@ $listDirn = $this->escape($this->state->get('list.direction')); items)) : ?>
    - +
    - + @@ -168,7 +168,7 @@ $listDirn = $this->escape($this->state->get('list.direction')); -
    - + - + - + - + - + - + - + @@ -99,11 +99,11 @@ $listDirn = $this->escape($this->state->get('list.direction')); id); ?> - + escape($item->description); ?> checksum)) : ?> -
    : checksum, 0, 16); ?>... +
    : checksum, 0, 16); ?>...
    @@ -137,14 +137,14 @@ $listDirn = $this->escape($this->state->get('list.direction')); status === 'complete' && $item->filesexist) : ?> - +
    @@ -186,7 +186,7 @@ document.querySelectorAll('.mb-tile').forEach(function(tile) { -
    - +
    - + - + @@ -60,7 +60,7 @@ $listDirn = $this->escape($this->state->get('list.direction')); id); ?> - + escape($item->title); ?> description)) : ?> diff --git a/source/packages/com_mokobackup/tmpl/profiles/index.html b/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profiles/index.html similarity index 100% rename from source/packages/com_mokobackup/tmpl/profiles/index.html rename to source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profiles/index.html diff --git a/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.ini b/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.ini new file mode 100644 index 0000000..743b249 --- /dev/null +++ b/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.ini @@ -0,0 +1,264 @@ +; MokoJoomBackup — Component language file (en-GB) +; @package MokoJoomBackup +; @author Moko Consulting +; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. +; @license GPL-3.0-or-later + +COM_MOKOJOOMBACKUP="MokoJoomBackup" +COM_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla" + +; Submenu +COM_MOKOJOOMBACKUP_SUBMENU_DASHBOARD="Dashboard" +COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS="Backup Records" +COM_MOKOJOOMBACKUP_SUBMENU_PROFILES="Backup Profiles" + +; Dashboard view +COM_MOKOJOOMBACKUP_DASHBOARD_TITLE="MokoJoomBackup Dashboard" +COM_MOKOJOOMBACKUP_DASHBOARD_LAST_BACKUP="Last Backup" +COM_MOKOJOOMBACKUP_DASHBOARD_NO_BACKUPS="No backups yet" +COM_MOKOJOOMBACKUP_DASHBOARD_NEXT_SCHEDULED="Next Scheduled" +COM_MOKOJOOMBACKUP_DASHBOARD_NO_SCHEDULED="No tasks scheduled" +COM_MOKOJOOMBACKUP_DASHBOARD_TOTAL_BACKUPS="Total Backups" +COM_MOKOJOOMBACKUP_DASHBOARD_STORAGE="Storage Used" +COM_MOKOJOOMBACKUP_DASHBOARD_FAILURES_7D="%d failures (7 days)" +COM_MOKOJOOMBACKUP_DASHBOARD_QUICK_ACTIONS="Quick Actions" +COM_MOKOJOOMBACKUP_DASHBOARD_SCHEDULED_TASKS="Scheduled Tasks" +COM_MOKOJOOMBACKUP_DASHBOARD_UPDATE_SITE="Update Site" +COM_MOKOJOOMBACKUP_DASHBOARD_SYSTEM_HEALTH="System Health" + +; Backups view +COM_MOKOJOOMBACKUP_BACKUPS_TITLE="Backup Records" +COM_MOKOJOOMBACKUP_BACKUPS_TABLE_CAPTION="Table of backup records" +COM_MOKOJOOMBACKUP_NO_BACKUPS="No backups found. Click 'Backup Now' to create your first backup." +COM_MOKOJOOMBACKUP_TOOLBAR_BACKUP_NOW="Backup Now" +COM_MOKOJOOMBACKUP_DOWNLOAD="Download" + +; Backup detail view +COM_MOKOJOOMBACKUP_BACKUP_DETAIL="Backup Detail" +COM_MOKOJOOMBACKUP_VIEW_LOG="Backup Log" +COM_MOKOJOOMBACKUP_FIELD_CHECKSUM="SHA-256 Checksum" +COM_MOKOJOOMBACKUP_FIELD_PATH="File Path" +COM_MOKOJOOMBACKUP_FIELD_DB_SIZE="DB Size" +COM_MOKOJOOMBACKUP_FIELD_REMOTE="Remote Path" + +; Profiles view +COM_MOKOJOOMBACKUP_PROFILES_TITLE="Backup Profiles" +COM_MOKOJOOMBACKUP_PROFILES_TABLE_CAPTION="Table of backup profiles" +COM_MOKOJOOMBACKUP_NO_PROFILES="No backup profiles found." +COM_MOKOJOOMBACKUP_PROFILE_NEW="New Profile" +COM_MOKOJOOMBACKUP_PROFILE_EDIT="Edit Profile" + +; Table headings +COM_MOKOJOOMBACKUP_HEADING_DESCRIPTION="Description" +COM_MOKOJOOMBACKUP_HEADING_PROFILE="Profile" +COM_MOKOJOOMBACKUP_HEADING_STATUS="Status" +COM_MOKOJOOMBACKUP_HEADING_TYPE="Type" +COM_MOKOJOOMBACKUP_HEADING_SIZE="Size" +COM_MOKOJOOMBACKUP_HEADING_DATE="Date" +COM_MOKOJOOMBACKUP_HEADING_ACTIONS="Actions" +COM_MOKOJOOMBACKUP_HEADING_TITLE="Title" +COM_MOKOJOOMBACKUP_HEADING_DATE_DESC="Date descending" +COM_MOKOJOOMBACKUP_HEADING_DATE_ASC="Date ascending" +COM_MOKOJOOMBACKUP_HEADING_SIZE_DESC="Size descending" +COM_MOKOJOOMBACKUP_HEADING_SIZE_ASC="Size ascending" +COM_MOKOJOOMBACKUP_HEADING_TITLE_ASC="Title ascending" +COM_MOKOJOOMBACKUP_HEADING_TITLE_DESC="Title descending" + +; General fields +COM_MOKOJOOMBACKUP_FIELD_TITLE="Title" +COM_MOKOJOOMBACKUP_FIELD_TITLE_DESC="Profile name" +COM_MOKOJOOMBACKUP_FIELD_DESCRIPTION="Description" +COM_MOKOJOOMBACKUP_FIELD_DESCRIPTION_DESC="Brief description of this profile" +COM_MOKOJOOMBACKUP_FIELD_BACKUP_TYPE="Backup Type" +COM_MOKOJOOMBACKUP_FIELD_BACKUP_TYPE_DESC="What to include in the backup" +COM_MOKOJOOMBACKUP_FIELD_STATUS="Status" +COM_MOKOJOOMBACKUP_FIELD_ORIGIN="Origin" +COM_MOKOJOOMBACKUP_FIELD_SIZE="Total Size" +COM_MOKOJOOMBACKUP_FIELD_START="Start Time" +COM_MOKOJOOMBACKUP_FIELD_END="End Time" +COM_MOKOJOOMBACKUP_FIELD_ARCHIVE="Archive Name" +COM_MOKOJOOMBACKUP_FIELD_FILES_COUNT="Files Count" +COM_MOKOJOOMBACKUP_FIELD_TABLES_COUNT="Tables Count" + +; Archive settings +COM_MOKOJOOMBACKUP_FIELD_ARCHIVE_FORMAT="Archive Format" +COM_MOKOJOOMBACKUP_FIELD_ARCHIVE_FORMAT_DESC="Format for the backup archive file" +COM_MOKOJOOMBACKUP_FIELD_COMPRESSION="Compression Level" +COM_MOKOJOOMBACKUP_FIELD_COMPRESSION_DESC="Higher compression = smaller file but slower" +COM_MOKOJOOMBACKUP_COMPRESSION_NONE="None (fastest)" +COM_MOKOJOOMBACKUP_COMPRESSION_FASTEST="Low (fast)" +COM_MOKOJOOMBACKUP_COMPRESSION_NORMAL="Normal (balanced)" +COM_MOKOJOOMBACKUP_COMPRESSION_BEST="Maximum (smallest)" +COM_MOKOJOOMBACKUP_FIELD_ENCRYPTION_PASSWORD="Encryption Password" +COM_MOKOJOOMBACKUP_FIELD_ENCRYPTION_PASSWORD_DESC="Set a password to encrypt the backup archive with AES-256. Leave blank for no encryption. Required to restore encrypted backups." +COM_MOKOJOOMBACKUP_FIELD_SPLIT_SIZE="Split Size (MB)" +COM_MOKOJOOMBACKUP_FIELD_SPLIT_SIZE_DESC="Split archive into parts of this size in MB. 0 = no splitting." +COM_MOKOJOOMBACKUP_FIELD_BACKUP_DIR="Backup Directory" +COM_MOKOJOOMBACKUP_FIELD_BACKUP_DIR_DESC="Directory where backup archives are stored. Supports placeholders: [host], [date], [year], [month], [day], [profile_name], [site_name], [type]. Absolute paths (starting with /) are used as-is; relative paths resolve from the Joomla root." +COM_MOKOJOOMBACKUP_FIELD_ARCHIVE_NAME_FORMAT="Archive Name Format" +COM_MOKOJOOMBACKUP_FIELD_ARCHIVE_NAME_FORMAT_DESC="Filename template for backup archives (without extension). Placeholders: [host] hostname, [date] Ymd, [time] His, [datetime] Ymd_His, [year] [month] [day] [hour] [minute] [second], [profile_id], [profile_name], [site_name], [type], [random]." +COM_MOKOJOOMBACKUP_FIELD_INCLUDE_MOKORESTORE="Include Restore Script" +COM_MOKOJOOMBACKUP_FIELD_INCLUDE_MOKORESTORE_DESC="Include MokoRestore (standalone restore.php) inside the backup archive. Creates a self-contained package that can restore the site on a blank server without Joomla installed." + +; Exclusion filter fields +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_DIRS="Exclude Directories" +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_DIRS_DESC="Browse and check directories to exclude from file backup. You can also type paths manually." +COM_MOKOJOOMBACKUP_FILTER_EXCLUDED="Excluded" +COM_MOKOJOOMBACKUP_FILTER_INCLUDED="Included" +COM_MOKOJOOMBACKUP_FILTER_ADD_MANUAL="Add Path" +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_FILES="Exclude Files" +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_FILES_DESC="One filename or pattern per line. Supports wildcards (e.g. *.bak, *.tmp)." +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_TABLES="Exclude Tables" +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_TABLES_DESC="One table name per line (use #__ prefix). These tables will be skipped during database dump." + +; Remote storage fields +COM_MOKOJOOMBACKUP_FIELD_REMOTE_STORAGE="Remote Storage" +COM_MOKOJOOMBACKUP_FIELD_REMOTE_STORAGE_DESC="Optionally upload backup archives to a remote location after creation" +COM_MOKOJOOMBACKUP_REMOTE_NONE="None (local only)" +COM_MOKOJOOMBACKUP_REMOTE_FTP="FTP / FTPS" +COM_MOKOJOOMBACKUP_REMOTE_GDRIVE="Google Drive" +COM_MOKOJOOMBACKUP_FIELD_KEEP_LOCAL="Keep Local Copy" +COM_MOKOJOOMBACKUP_FIELD_KEEP_LOCAL_DESC="Keep the local backup file after uploading to remote storage" + +; FTP fields +COM_MOKOJOOMBACKUP_FIELD_FTP_HOST="FTP Host" +COM_MOKOJOOMBACKUP_FIELD_FTP_HOST_DESC="FTP server hostname or IP address" +COM_MOKOJOOMBACKUP_FIELD_FTP_PORT="FTP Port" +COM_MOKOJOOMBACKUP_FIELD_FTP_PORT_DESC="FTP server port (default: 21)" +COM_MOKOJOOMBACKUP_FIELD_FTP_USERNAME="FTP Username" +COM_MOKOJOOMBACKUP_FIELD_FTP_PASSWORD="FTP Password" +COM_MOKOJOOMBACKUP_FIELD_FTP_PATH="Remote Path" +COM_MOKOJOOMBACKUP_FIELD_FTP_PATH_DESC="Directory on the FTP server to upload backups to" +COM_MOKOJOOMBACKUP_FIELD_FTP_PASSIVE="Passive Mode" +COM_MOKOJOOMBACKUP_FIELD_FTP_PASSIVE_DESC="Use passive mode for FTP connections (recommended)" +COM_MOKOJOOMBACKUP_FIELD_FTP_SSL="Use FTPS (SSL)" +COM_MOKOJOOMBACKUP_FIELD_FTP_SSL_DESC="Connect using FTPS (FTP over SSL/TLS)" + +; Google Drive fields +COM_MOKOJOOMBACKUP_FIELD_GDRIVE_CLIENT_ID="Google Client ID" +COM_MOKOJOOMBACKUP_FIELD_GDRIVE_CLIENT_ID_DESC="OAuth 2.0 Client ID from Google Cloud Console" +COM_MOKOJOOMBACKUP_FIELD_GDRIVE_CLIENT_SECRET="Google Client Secret" +COM_MOKOJOOMBACKUP_FIELD_GDRIVE_REFRESH_TOKEN="Refresh Token" +COM_MOKOJOOMBACKUP_FIELD_GDRIVE_REFRESH_TOKEN_DESC="OAuth 2.0 refresh token for offline access" +COM_MOKOJOOMBACKUP_FIELD_GDRIVE_FOLDER_ID="Drive Folder ID" +COM_MOKOJOOMBACKUP_FIELD_GDRIVE_FOLDER_ID_DESC="Google Drive folder ID where backups will be uploaded. Find this in the folder URL." + +; Backup types +COM_MOKOJOOMBACKUP_TYPE_FULL="Full Site (Database + Files)" +COM_MOKOJOOMBACKUP_TYPE_DATABASE="Database Only" +COM_MOKOJOOMBACKUP_TYPE_FILES="Files Only" +COM_MOKOJOOMBACKUP_TYPE_DIFFERENTIAL="Differential (changed files + full DB)" + +; Status labels +COM_MOKOJOOMBACKUP_STATUS_COMPLETE="Complete" +COM_MOKOJOOMBACKUP_STATUS_RUNNING="Running" +COM_MOKOJOOMBACKUP_STATUS_FAIL="Failed" +COM_MOKOJOOMBACKUP_STATUS_PENDING="Pending" + +; Filters +COM_MOKOJOOMBACKUP_FILTER_SEARCH="Search" +COM_MOKOJOOMBACKUP_FILTER_STATUS="Status" +COM_MOKOJOOMBACKUP_FILTER_STATUS_ALL="- Select Status -" + +; Tabs and fieldsets +COM_MOKOJOOMBACKUP_TAB_GENERAL="General" +COM_MOKOJOOMBACKUP_TAB_ARCHIVE="Archive Settings" +COM_MOKOJOOMBACKUP_TAB_FILTERS="Exclusion Filters" +COM_MOKOJOOMBACKUP_TAB_REMOTE="Remote Storage" +COM_MOKOJOOMBACKUP_FIELDSET_GENERAL="General" +COM_MOKOJOOMBACKUP_FIELDSET_ARCHIVE="Archive Settings" +COM_MOKOJOOMBACKUP_FIELDSET_STATUS="Status" +COM_MOKOJOOMBACKUP_FIELDSET_FILTERS="Exclusion Filters" +COM_MOKOJOOMBACKUP_FIELDSET_REMOTE="Remote Storage" +COM_MOKOJOOMBACKUP_FIELDSET_FTP="FTP Settings" +COM_MOKOJOOMBACKUP_FIELDSET_GDRIVE="Google Drive Settings" + +; Backup profile selector +COM_MOKOJOOMBACKUP_BACKUP_PROFILE="Backup Profile" + +; Restore +COM_MOKOJOOMBACKUP_TOOLBAR_RESTORE="Restore" +COM_MOKOJOOMBACKUP_RESTORE_CONFIRM="WARNING: Restoring will overwrite your current site files and/or database. Are you sure you want to continue?" + +; Notifications +COM_MOKOJOOMBACKUP_TAB_NOTIFICATIONS="Notifications" +COM_MOKOJOOMBACKUP_FIELDSET_NOTIFICATIONS="Email Notifications" +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_EMAIL="Notification Email(s)" +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_EMAIL_DESC="Comma-separated list of email addresses to notify. Leave empty to disable notifications." +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_SUCCESS="Notify on Success" +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_SUCCESS_DESC="Send an email when a backup completes successfully." +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_FAILURE="Notify on Failure" +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_FAILURE_DESC="Send an email when a backup fails. Includes log excerpt for debugging." + +; Integrity verification +COM_MOKOJOOMBACKUP_TOOLBAR_VERIFY="Verify Integrity" +COM_MOKOJOOMBACKUP_VERIFY_OK="Archive integrity verified — SHA-256 checksum matches." +COM_MOKOJOOMBACKUP_VERIFY_FAILED="INTEGRITY CHECK FAILED — archive has been modified or corrupted since backup." +COM_MOKOJOOMBACKUP_VERIFY_NO_CHECKSUM="No checksum stored for this backup. Only backups created after this update can be verified." + +; S3 storage +COM_MOKOJOOMBACKUP_REMOTE_S3="Amazon S3 / S3-Compatible" +COM_MOKOJOOMBACKUP_FIELDSET_S3="S3 Storage Settings" +COM_MOKOJOOMBACKUP_FIELD_S3_ENDPOINT="S3 Endpoint" +COM_MOKOJOOMBACKUP_FIELD_S3_ENDPOINT_DESC="S3 API endpoint URL. Leave blank for AWS S3. For Wasabi, MinIO, Backblaze B2, enter their endpoint URL." +COM_MOKOJOOMBACKUP_FIELD_S3_REGION="Region" +COM_MOKOJOOMBACKUP_FIELD_S3_REGION_DESC="AWS region (e.g. us-east-1, eu-west-1). Required for AWS Signature V4." +COM_MOKOJOOMBACKUP_FIELD_S3_ACCESS_KEY="Access Key" +COM_MOKOJOOMBACKUP_FIELD_S3_SECRET_KEY="Secret Key" +COM_MOKOJOOMBACKUP_FIELD_S3_BUCKET="Bucket Name" +COM_MOKOJOOMBACKUP_FIELD_S3_BUCKET_DESC="S3 bucket name where backups will be stored." +COM_MOKOJOOMBACKUP_FIELD_S3_PATH="Path Prefix" +COM_MOKOJOOMBACKUP_FIELD_S3_PATH_DESC="Optional path prefix inside the bucket (e.g. /backups or /sites/mysite)." + +; Akeeba Import +COM_MOKOJOOMBACKUP_TOOLBAR_IMPORT_AKEEBA="Import from Akeeba" +COM_MOKOJOOMBACKUP_AKEEBA_NOT_FOUND="Akeeba Backup tables not found. Is Akeeba Backup Pro installed?" + +; Update site notice +COM_MOKOJOOMBACKUP_UPDATE_SITE_NOTICE="To receive automatic updates, configure your Update Site with your download key." +COM_MOKOJOOMBACKUP_UPDATE_SITE_MISSING="MokoJoomBackup update site not found. Reinstall the package to register the update server." +COM_MOKOJOOMBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." + +; Component Options (config.xml) +COM_MOKOJOOMBACKUP_CONFIG_GENERAL="General" +COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_BACKUP_DIR="Default Backup Directory" +COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_BACKUP_DIR_DESC="Default directory for backup archives, relative to Joomla root. Can be overridden per profile." +COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_PROFILE="Default Profile" +COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_PROFILE_DESC="Default backup profile used by quick actions and CLI when no profile is specified." +COM_MOKOJOOMBACKUP_CONFIG_SHOW_UPDATE_NOTICE="Show Update Site Notice" +COM_MOKOJOOMBACKUP_CONFIG_SHOW_UPDATE_NOTICE_DESC="Display the update site configuration notice on the Backup Records view." +COM_MOKOJOOMBACKUP_CONFIG_CLEANUP="Cleanup Defaults" +COM_MOKOJOOMBACKUP_CONFIG_MAX_AGE="Max Backup Age (days)" +COM_MOKOJOOMBACKUP_CONFIG_MAX_AGE_DESC="Default maximum age for backup records. Used by the system plugin and CLI cleanup command." +COM_MOKOJOOMBACKUP_CONFIG_MAX_BACKUPS="Max Backup Count" +COM_MOKOJOOMBACKUP_CONFIG_MAX_BACKUPS_DESC="Default maximum number of completed backups to retain." +COM_MOKOJOOMBACKUP_CONFIG_NOTIFICATIONS="Notifications" +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_EMAIL="Global Notification Email(s)" +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_EMAIL_DESC="Comma-separated list of email addresses for global backup notifications. Per-profile settings override this." +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_SUCCESS="Notify on Success" +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_SUCCESS_DESC="Send email when any backup completes successfully (unless overridden by profile)." +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_FAILURE="Notify on Failure" +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_FAILURE_DESC="Send email when any backup fails (unless overridden by profile)." + +; Folder picker +COM_MOKOJOOMBACKUP_FOLDER_EXISTS="Directory exists" +COM_MOKOJOOMBACKUP_FOLDER_NOT_FOUND="Directory not found" +COM_MOKOJOOMBACKUP_BACKUP_DIR_DEFAULT="Default (inside web root)" + +; Exclude fields +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_TABLES_HELP="Check tables to exclude from database backup. Use Data to skip row data (keeps structure), Structure to skip CREATE TABLE, or both to fully exclude." +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_DATA="Data" +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_STRUCTURE="Structure" +COM_MOKOJOOMBACKUP_FIELD_TABLE_NAME="Table Name" + +; User group notifications +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_USER_GROUPS="Notify User Groups" +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_USER_GROUPS_DESC="Select Joomla user groups whose members will receive backup notifications. Combined with email addresses above." + +; Dashboard warnings +COM_MOKOJOOMBACKUP_DASHBOARD_DEFAULT_DIR_WARNING_TITLE="Backup directory is inside the web root" +COM_MOKOJOOMBACKUP_DASHBOARD_DEFAULT_DIR_WARNING="One or more profiles store backups in the default directory inside the web root. This may expose backup archives if .htaccess is not supported. Move backups to a directory outside the web root for better security." + +; Errors +COM_MOKOJOOMBACKUP_ERROR_FILE_NOT_FOUND="Backup archive file not found or has been deleted." +COM_MOKOJOOMBACKUP_ERROR_NO_RECORD_SELECTED="No backup record selected for restore." diff --git a/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.sys.ini b/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.sys.ini new file mode 100644 index 0000000..ec73f5c --- /dev/null +++ b/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.sys.ini @@ -0,0 +1,10 @@ +; MokoJoomBackup — Component system language file (en-GB) +; @package MokoJoomBackup +; @author Moko Consulting +; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. +; @license GPL-3.0-or-later + +COM_MOKOJOOMBACKUP="MokoJoomBackup" +COM_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration." +COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS="Backup Records" +COM_MOKOJOOMBACKUP_SUBMENU_PROFILES="Backup Profiles" diff --git a/source/packages/com_mokojoombackup/language/en-US/com_mokojoombackup.ini b/source/packages/com_mokojoombackup/language/en-US/com_mokojoombackup.ini new file mode 100644 index 0000000..1c47e3a --- /dev/null +++ b/source/packages/com_mokojoombackup/language/en-US/com_mokojoombackup.ini @@ -0,0 +1,69 @@ +; MokoJoomBackup — Component language file (en-US) +; @package MokoJoomBackup +; @author Moko Consulting +; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. +; @license GPL-3.0-or-later + +COM_MOKOJOOMBACKUP="MokoJoomBackup" +COM_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla" +COM_MOKOJOOMBACKUP_SUBMENU_DASHBOARD="Dashboard" +COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS="Backup Records" +COM_MOKOJOOMBACKUP_SUBMENU_PROFILES="Backup Profiles" +COM_MOKOJOOMBACKUP_DASHBOARD_TITLE="MokoJoomBackup Dashboard" +COM_MOKOJOOMBACKUP_DASHBOARD_LAST_BACKUP="Last Backup" +COM_MOKOJOOMBACKUP_DASHBOARD_NO_BACKUPS="No backups yet" +COM_MOKOJOOMBACKUP_DASHBOARD_NEXT_SCHEDULED="Next Scheduled" +COM_MOKOJOOMBACKUP_DASHBOARD_NO_SCHEDULED="No tasks scheduled" +COM_MOKOJOOMBACKUP_DASHBOARD_TOTAL_BACKUPS="Total Backups" +COM_MOKOJOOMBACKUP_DASHBOARD_STORAGE="Storage Used" +COM_MOKOJOOMBACKUP_DASHBOARD_FAILURES_7D="%d failures (7 days)" +COM_MOKOJOOMBACKUP_DASHBOARD_QUICK_ACTIONS="Quick Actions" +COM_MOKOJOOMBACKUP_DASHBOARD_SCHEDULED_TASKS="Scheduled Tasks" +COM_MOKOJOOMBACKUP_DASHBOARD_UPDATE_SITE="Update Site" +COM_MOKOJOOMBACKUP_DASHBOARD_SYSTEM_HEALTH="System Health" +COM_MOKOJOOMBACKUP_BACKUPS_TITLE="Backup Records" +COM_MOKOJOOMBACKUP_PROFILES_TITLE="Backup Profiles" +COM_MOKOJOOMBACKUP_TOOLBAR_BACKUP_NOW="Backup Now" +COM_MOKOJOOMBACKUP_NO_BACKUPS="No backups found. Click 'Backup Now' to create your first backup." +COM_MOKOJOOMBACKUP_NO_PROFILES="No backup profiles found." +COM_MOKOJOOMBACKUP_UPDATE_SITE_NOTICE="To receive automatic updates, configure your Update Site with your download key." +COM_MOKOJOOMBACKUP_UPDATE_SITE_MISSING="MokoJoomBackup update site not found. Reinstall the package to register the update server." +COM_MOKOJOOMBACKUP_POSTINSTALL_UPDATE_SITE="MokoJoomBackup installed successfully. Configure your Update Site to receive automatic updates." +COM_MOKOJOOMBACKUP_CONFIG_GENERAL="General" +COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_BACKUP_DIR="Default Backup Directory" +COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_BACKUP_DIR_DESC="Default directory for backup archives, relative to Joomla root. Can be overridden per profile." +COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_PROFILE="Default Profile" +COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_PROFILE_DESC="Default backup profile used by quick actions and CLI when no profile is specified." +COM_MOKOJOOMBACKUP_CONFIG_SHOW_UPDATE_NOTICE="Show Update Site Notice" +COM_MOKOJOOMBACKUP_CONFIG_SHOW_UPDATE_NOTICE_DESC="Display the update site configuration notice on the Backup Records view." +COM_MOKOJOOMBACKUP_CONFIG_CLEANUP="Cleanup Defaults" +COM_MOKOJOOMBACKUP_CONFIG_MAX_AGE="Max Backup Age (days)" +COM_MOKOJOOMBACKUP_CONFIG_MAX_AGE_DESC="Default maximum age for backup records. Used by the system plugin and CLI cleanup command." +COM_MOKOJOOMBACKUP_CONFIG_MAX_BACKUPS="Max Backup Count" +COM_MOKOJOOMBACKUP_CONFIG_MAX_BACKUPS_DESC="Default maximum number of completed backups to retain." +COM_MOKOJOOMBACKUP_CONFIG_NOTIFICATIONS="Notifications" +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_EMAIL="Global Notification Email(s)" +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_EMAIL_DESC="Comma-separated list of email addresses for global backup notifications. Per-profile settings override this." +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_SUCCESS="Notify on Success" +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_SUCCESS_DESC="Send email when any backup completes successfully (unless overridden by profile)." +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_FAILURE="Notify on Failure" +COM_MOKOJOOMBACKUP_CONFIG_NOTIFY_FAILURE_DESC="Send email when any backup fails (unless overridden by profile)." +COM_MOKOJOOMBACKUP_FOLDER_EXISTS="Directory exists" +COM_MOKOJOOMBACKUP_FOLDER_NOT_FOUND="Directory not found" +COM_MOKOJOOMBACKUP_BACKUP_DIR_DEFAULT="Default (inside web root)" +COM_MOKOJOOMBACKUP_DASHBOARD_DEFAULT_DIR_WARNING_TITLE="Backup directory is inside the web root" +COM_MOKOJOOMBACKUP_DASHBOARD_DEFAULT_DIR_WARNING="One or more profiles store backups in the default directory inside the web root. This may expose backup archives if .htaccess is not supported. Move backups to a directory outside the web root for better security." +COM_MOKOJOOMBACKUP_FOLDER_EXISTS="Directory exists" +COM_MOKOJOOMBACKUP_FOLDER_NOT_FOUND="Directory not found" +COM_MOKOJOOMBACKUP_BACKUP_DIR_DEFAULT="Default (inside web root)" +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_TABLES_HELP="Check tables to exclude from database backup. Use Data to skip row data (keeps structure), Structure to skip CREATE TABLE, or both to fully exclude." +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_DATA="Data" +COM_MOKOJOOMBACKUP_FIELD_EXCLUDE_STRUCTURE="Structure" +COM_MOKOJOOMBACKUP_FIELD_TABLE_NAME="Table Name" +COM_MOKOJOOMBACKUP_VIEW_LOG="Backup Log" +COM_MOKOJOOMBACKUP_FIELD_CHECKSUM="SHA-256 Checksum" +COM_MOKOJOOMBACKUP_FIELD_PATH="File Path" +COM_MOKOJOOMBACKUP_FIELD_DB_SIZE="DB Size" +COM_MOKOJOOMBACKUP_FIELD_REMOTE="Remote Path" +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_USER_GROUPS="Notify User Groups" +COM_MOKOJOOMBACKUP_FIELD_NOTIFY_USER_GROUPS_DESC="Select Joomla user groups whose members will receive backup notifications. Combined with email addresses above." diff --git a/source/packages/com_mokojoombackup/language/en-US/com_mokojoombackup.sys.ini b/source/packages/com_mokojoombackup/language/en-US/com_mokojoombackup.sys.ini new file mode 100644 index 0000000..93b980a --- /dev/null +++ b/source/packages/com_mokojoombackup/language/en-US/com_mokojoombackup.sys.ini @@ -0,0 +1,10 @@ +; MokoJoomBackup — Component system language file (en-US) +; @package MokoJoomBackup +; @author Moko Consulting +; @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. +; @license GPL-3.0-or-later + +COM_MOKOJOOMBACKUP="MokoJoomBackup" +COM_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration." +COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS="Backup Records" +COM_MOKOJOOMBACKUP_SUBMENU_PROFILES="Backup Profiles" diff --git a/source/packages/com_mokobackup/mokobackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml similarity index 63% rename from source/packages/com_mokobackup/mokobackup.xml rename to source/packages/com_mokojoombackup/mokojoombackup.xml index bccba9f..f402a0a 100644 --- a/source/packages/com_mokobackup/mokobackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -1,23 +1,23 @@ - com_mokobackup - 01.01.09-dev + com_mokojoombackup + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - COM_MOKOBACKUP_DESCRIPTION + COM_MOKOJOOMBACKUP_DESCRIPTION - Joomla\Component\MokoBackup + Joomla\Component\MokoJoomBackup @@ -38,11 +38,11 @@ - COM_MOKOBACKUP + COM_MOKOJOOMBACKUP - COM_MOKOBACKUP_SUBMENU_DASHBOARD - COM_MOKOBACKUP_SUBMENU_BACKUPS - COM_MOKOBACKUP_SUBMENU_PROFILES + COM_MOKOJOOMBACKUP_SUBMENU_DASHBOARD + COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS + COM_MOKOJOOMBACKUP_SUBMENU_PROFILES cli @@ -53,8 +53,8 @@ tmpl - en-GB/com_mokobackup.ini - en-GB/com_mokobackup.sys.ini + en-GB/com_mokojoombackup.ini + en-GB/com_mokojoombackup.sys.ini diff --git a/source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini b/source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini deleted file mode 100644 index 6997740..0000000 --- a/source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.ini +++ /dev/null @@ -1,9 +0,0 @@ -; MokoJoomBackup — Actionlog Plugin language file (en-GB) -PLG_ACTIONLOG_MOKOBACKUP="Action Log - MokoJoomBackup" -PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." -PLG_ACTIONLOG_MOKOBACKUP_PROFILE_CREATED="User {username} created backup profile "{title}" (ID: {id})" -PLG_ACTIONLOG_MOKOBACKUP_PROFILE_UPDATED="User {username} updated backup profile "{title}" (ID: {id})" -PLG_ACTIONLOG_MOKOBACKUP_PROFILE_DELETED="User {username} deleted backup profile "{title}" (ID: {id})" -PLG_ACTIONLOG_MOKOBACKUP_RECORD_DELETED="User {username} deleted backup record "{title}" (ID: {id})" -PLG_ACTIONLOG_MOKOBACKUP_BACKUP_COMPLETE="Backup completed: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" -PLG_ACTIONLOG_MOKOBACKUP_BACKUP_FAILED="Backup FAILED: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" diff --git a/source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini b/source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini deleted file mode 100644 index 3e1c655..0000000 --- a/source/packages/plg_actionlog_mokobackup/language/en-GB/plg_actionlog_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Actionlog Plugin system language file (en-GB) -PLG_ACTIONLOG_MOKOBACKUP="Action Log - MokoJoomBackup" -PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." diff --git a/source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini b/source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini deleted file mode 100644 index 27cf1d6..0000000 --- a/source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.ini +++ /dev/null @@ -1,9 +0,0 @@ -; MokoJoomBackup — Actionlog Plugin language file (en-US) -PLG_ACTIONLOG_MOKOBACKUP="Action Log - MokoJoomBackup" -PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." -PLG_ACTIONLOG_MOKOBACKUP_PROFILE_CREATED="User {username} created backup profile "{title}" (ID: {id})" -PLG_ACTIONLOG_MOKOBACKUP_PROFILE_UPDATED="User {username} updated backup profile "{title}" (ID: {id})" -PLG_ACTIONLOG_MOKOBACKUP_PROFILE_DELETED="User {username} deleted backup profile "{title}" (ID: {id})" -PLG_ACTIONLOG_MOKOBACKUP_RECORD_DELETED="User {username} deleted backup record "{title}" (ID: {id})" -PLG_ACTIONLOG_MOKOBACKUP_BACKUP_COMPLETE="Backup completed: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" -PLG_ACTIONLOG_MOKOBACKUP_BACKUP_FAILED="Backup FAILED: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" diff --git a/source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini b/source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini deleted file mode 100644 index 1737124..0000000 --- a/source/packages/plg_actionlog_mokobackup/language/en-US/plg_actionlog_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Actionlog Plugin system language file (en-US) -PLG_ACTIONLOG_MOKOBACKUP="Action Log - MokoJoomBackup" -PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." diff --git a/source/packages/plg_actionlog_mokojoombackup/language/en-GB/plg_actionlog_mokojoombackup.ini b/source/packages/plg_actionlog_mokojoombackup/language/en-GB/plg_actionlog_mokojoombackup.ini new file mode 100644 index 0000000..75b37b6 --- /dev/null +++ b/source/packages/plg_actionlog_mokojoombackup/language/en-GB/plg_actionlog_mokojoombackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — Actionlog Plugin language file (en-GB) +PLG_ACTIONLOG_MOKOJOOMBACKUP="Action Log - MokoJoomBackup" +PLG_ACTIONLOG_MOKOJOOMBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." +PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_CREATED="User {username} created backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_UPDATED="User {username} updated backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_DELETED="User {username} deleted backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_RECORD_DELETED="User {username} deleted backup record "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_BACKUP_COMPLETE="Backup completed: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_BACKUP_FAILED="Backup FAILED: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" diff --git a/source/packages/plg_actionlog_mokojoombackup/language/en-GB/plg_actionlog_mokojoombackup.sys.ini b/source/packages/plg_actionlog_mokojoombackup/language/en-GB/plg_actionlog_mokojoombackup.sys.ini new file mode 100644 index 0000000..61b9070 --- /dev/null +++ b/source/packages/plg_actionlog_mokojoombackup/language/en-GB/plg_actionlog_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Actionlog Plugin system language file (en-GB) +PLG_ACTIONLOG_MOKOJOOMBACKUP="Action Log - MokoJoomBackup" +PLG_ACTIONLOG_MOKOJOOMBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." diff --git a/source/packages/plg_actionlog_mokojoombackup/language/en-US/plg_actionlog_mokojoombackup.ini b/source/packages/plg_actionlog_mokojoombackup/language/en-US/plg_actionlog_mokojoombackup.ini new file mode 100644 index 0000000..9baa0c5 --- /dev/null +++ b/source/packages/plg_actionlog_mokojoombackup/language/en-US/plg_actionlog_mokojoombackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — Actionlog Plugin language file (en-US) +PLG_ACTIONLOG_MOKOJOOMBACKUP="Action Log - MokoJoomBackup" +PLG_ACTIONLOG_MOKOJOOMBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." +PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_CREATED="User {username} created backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_UPDATED="User {username} updated backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_DELETED="User {username} deleted backup profile "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_RECORD_DELETED="User {username} deleted backup record "{title}" (ID: {id})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_BACKUP_COMPLETE="Backup completed: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" +PLG_ACTIONLOG_MOKOJOOMBACKUP_BACKUP_FAILED="Backup FAILED: "{title}" (ID: {id}, profile: {profile_id}, origin: {origin})" diff --git a/source/packages/plg_actionlog_mokojoombackup/language/en-US/plg_actionlog_mokojoombackup.sys.ini b/source/packages/plg_actionlog_mokojoombackup/language/en-US/plg_actionlog_mokojoombackup.sys.ini new file mode 100644 index 0000000..26c0927 --- /dev/null +++ b/source/packages/plg_actionlog_mokojoombackup/language/en-US/plg_actionlog_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Actionlog Plugin system language file (en-US) +PLG_ACTIONLOG_MOKOJOOMBACKUP="Action Log - MokoJoomBackup" +PLG_ACTIONLOG_MOKOJOOMBACKUP_DESCRIPTION="Logs MokoJoomBackup actions (backup, restore, profile changes) to User Action Logs." diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.php b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.php new file mode 100644 index 0000000..13e2b3b --- /dev/null +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.php @@ -0,0 +1,11 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; diff --git a/source/packages/plg_actionlog_mokobackup/mokobackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml similarity index 60% rename from source/packages/plg_actionlog_mokobackup/mokobackup.xml rename to source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index c45e66c..8695fef 100644 --- a/source/packages/plg_actionlog_mokobackup/mokobackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -1,32 +1,32 @@ - plg_actionlog_mokobackup - 01.01.09-dev + plg_actionlog_mokojoombackup + 01.01.07-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_ACTIONLOG_MOKOBACKUP_DESCRIPTION + PLG_ACTIONLOG_MOKOJOOMBACKUP_DESCRIPTION - Joomla\Plugin\Actionlog\MokoBackup + Joomla\Plugin\Actionlog\MokoJoomBackup - mokobackup.php + mokojoombackup.php services src - language/en-GB/plg_actionlog_mokobackup.ini - language/en-GB/plg_actionlog_mokobackup.sys.ini + language/en-GB/plg_actionlog_mokojoombackup.ini + language/en-GB/plg_actionlog_mokojoombackup.sys.ini diff --git a/source/packages/plg_actionlog_mokobackup/services/provider.php b/source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/services/provider.php similarity index 78% rename from source/packages/plg_actionlog_mokobackup/services/provider.php rename to source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/services/provider.php index b13a445..572a2a9 100644 --- a/source/packages/plg_actionlog_mokobackup/services/provider.php +++ b/source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/services/provider.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_actionlog_mokobackup + * @subpackage plg_actionlog_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -16,7 +16,7 @@ use Joomla\CMS\Plugin\PluginHelper; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; -use Joomla\Plugin\Actionlog\MokoBackup\Extension\MokoBackupActionlog; +use Joomla\Plugin\Actionlog\MokoJoomBackup\Extension\MokoJoomBackupActionlog; return new class () implements ServiceProviderInterface { public function register(Container $container): void @@ -24,9 +24,9 @@ return new class () implements ServiceProviderInterface { $container->set( PluginInterface::class, function (Container $container) { - $plugin = new MokoBackupActionlog( + $plugin = new MokoJoomBackupActionlog( $container->get(DispatcherInterface::class), - (array) PluginHelper::getPlugin('actionlog', 'mokobackup') + (array) PluginHelper::getPlugin('actionlog', 'mokojoombackup') ); $plugin->setApplication(Factory::getApplication()); diff --git a/source/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php b/source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/src/Extension/MokoBackupActionlog.php similarity index 77% rename from source/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php rename to source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/src/Extension/MokoBackupActionlog.php index 2cb97e7..ed160cd 100644 --- a/source/packages/plg_actionlog_mokobackup/src/Extension/MokoBackupActionlog.php +++ b/source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/src/Extension/MokoBackupActionlog.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage plg_actionlog_mokobackup + * @subpackage plg_actionlog_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Plugin\Actionlog\MokoBackup\Extension; +namespace Joomla\Plugin\Actionlog\MokoJoomBackup\Extension; defined('_JEXEC') or die; @@ -19,7 +19,7 @@ use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper; use Joomla\Event\Event; use Joomla\Event\SubscriberInterface; -final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface +final class MokoJoomBackupActionlog extends CMSPlugin implements SubscriberInterface { protected $autoloadLanguage = true; @@ -28,7 +28,7 @@ final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface return [ 'onContentAfterSave' => 'onContentAfterSave', 'onContentAfterDelete' => 'onContentAfterDelete', - 'onMokoBackupAfterRun' => 'onMokoBackupAfterRun', + 'onMokoJoomBackupAfterRun' => 'onMokoJoomBackupAfterRun', ]; } @@ -39,13 +39,13 @@ final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface { [$context, $table, $isNew] = array_values($event->getArguments()); - if ($context !== 'com_mokobackup.profile') { + if ($context !== 'com_mokojoombackup.profile') { return; } $messageKey = $isNew - ? 'PLG_ACTIONLOG_MOKOBACKUP_PROFILE_CREATED' - : 'PLG_ACTIONLOG_MOKOBACKUP_PROFILE_UPDATED'; + ? 'PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_CREATED' + : 'PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_UPDATED'; $this->addLog( [ @@ -56,7 +56,7 @@ final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface 'username' => $this->getCurrentUserName(), ], $messageKey, - 'com_mokobackup.profile', + 'com_mokojoombackup.profile', $this->getCurrentUserId() ); } @@ -68,30 +68,30 @@ final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface { [$context, $table] = array_values($event->getArguments()); - if ($context === 'com_mokobackup.profile') { + if ($context === 'com_mokojoombackup.profile') { $this->addLog( [ - 'PLG_ACTIONLOG_MOKOBACKUP_PROFILE_DELETED', + 'PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_DELETED', 'id' => $table->id, 'title' => $table->title ?? '', 'userid' => $this->getCurrentUserId(), 'username' => $this->getCurrentUserName(), ], - 'PLG_ACTIONLOG_MOKOBACKUP_PROFILE_DELETED', - 'com_mokobackup.profile', + 'PLG_ACTIONLOG_MOKOJOOMBACKUP_PROFILE_DELETED', + 'com_mokojoombackup.profile', $this->getCurrentUserId() ); - } elseif ($context === 'com_mokobackup.backup') { + } elseif ($context === 'com_mokojoombackup.backup') { $this->addLog( [ - 'PLG_ACTIONLOG_MOKOBACKUP_RECORD_DELETED', + 'PLG_ACTIONLOG_MOKOJOOMBACKUP_RECORD_DELETED', 'id' => $table->id, 'title' => $table->description ?? 'Backup #' . $table->id, 'userid' => $this->getCurrentUserId(), 'username' => $this->getCurrentUserName(), ], - 'PLG_ACTIONLOG_MOKOBACKUP_RECORD_DELETED', - 'com_mokobackup.backup', + 'PLG_ACTIONLOG_MOKOJOOMBACKUP_RECORD_DELETED', + 'com_mokojoombackup.backup', $this->getCurrentUserId() ); } @@ -101,7 +101,7 @@ final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface * Log when a backup completes or fails. * This event should be dispatched from BackupEngine. */ - public function onMokoBackupAfterRun(Event $event): void + public function onMokoJoomBackupAfterRun(Event $event): void { $args = $event->getArguments(); @@ -112,8 +112,8 @@ final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface $origin = $args['origin'] ?? 'backend'; $messageKey = $success - ? 'PLG_ACTIONLOG_MOKOBACKUP_BACKUP_COMPLETE' - : 'PLG_ACTIONLOG_MOKOBACKUP_BACKUP_FAILED'; + ? 'PLG_ACTIONLOG_MOKOJOOMBACKUP_BACKUP_COMPLETE' + : 'PLG_ACTIONLOG_MOKOJOOMBACKUP_BACKUP_FAILED'; $this->addLog( [ @@ -126,7 +126,7 @@ final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface 'username' => $this->getCurrentUserName(), ], $messageKey, - 'com_mokobackup.backup', + 'com_mokojoombackup.backup', $this->getCurrentUserId() ); } @@ -140,7 +140,7 @@ final class MokoBackupActionlog extends CMSPlugin implements SubscriberInterface 'message_language_key' => $messageLanguageKey, 'message' => json_encode($message), 'date' => date('Y-m-d H:i:s'), - 'extension' => 'com_mokobackup', + 'extension' => 'com_mokojoombackup', 'user_id' => $userId, 'ip_address' => ActionlogsHelper::getIp(), 'item_id' => $message['id'] ?? 0, diff --git a/source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini b/source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini deleted file mode 100644 index 4b87bca..0000000 --- a/source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Console Plugin language file (en-GB) -PLG_CONSOLE_MOKOBACKUP="Console - MokoJoomBackup" -PLG_CONSOLE_MOKOBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini b/source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini deleted file mode 100644 index 02fb8d8..0000000 --- a/source/packages/plg_console_mokobackup/language/en-GB/plg_console_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Console Plugin system language file (en-GB) -PLG_CONSOLE_MOKOBACKUP="Console - MokoJoomBackup" -PLG_CONSOLE_MOKOBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini b/source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini deleted file mode 100644 index 9fa5c15..0000000 --- a/source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Console Plugin language file (en-US) -PLG_CONSOLE_MOKOBACKUP="Console - MokoJoomBackup" -PLG_CONSOLE_MOKOBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini b/source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini deleted file mode 100644 index d22c08c..0000000 --- a/source/packages/plg_console_mokobackup/language/en-US/plg_console_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Console Plugin system language file (en-US) -PLG_CONSOLE_MOKOBACKUP="Console - MokoJoomBackup" -PLG_CONSOLE_MOKOBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/source/packages/plg_console_mokojoombackup/language/en-GB/plg_console_mokojoombackup.ini b/source/packages/plg_console_mokojoombackup/language/en-GB/plg_console_mokojoombackup.ini new file mode 100644 index 0000000..311e8d5 --- /dev/null +++ b/source/packages/plg_console_mokojoombackup/language/en-GB/plg_console_mokojoombackup.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Console Plugin language file (en-GB) +PLG_CONSOLE_MOKOJOOMBACKUP="Console - MokoJoomBackup" +PLG_CONSOLE_MOKOJOOMBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/source/packages/plg_console_mokojoombackup/language/en-GB/plg_console_mokojoombackup.sys.ini b/source/packages/plg_console_mokojoombackup/language/en-GB/plg_console_mokojoombackup.sys.ini new file mode 100644 index 0000000..f56b212 --- /dev/null +++ b/source/packages/plg_console_mokojoombackup/language/en-GB/plg_console_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Console Plugin system language file (en-GB) +PLG_CONSOLE_MOKOJOOMBACKUP="Console - MokoJoomBackup" +PLG_CONSOLE_MOKOJOOMBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/source/packages/plg_console_mokojoombackup/language/en-US/plg_console_mokojoombackup.ini b/source/packages/plg_console_mokojoombackup/language/en-US/plg_console_mokojoombackup.ini new file mode 100644 index 0000000..2de8315 --- /dev/null +++ b/source/packages/plg_console_mokojoombackup/language/en-US/plg_console_mokojoombackup.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Console Plugin language file (en-US) +PLG_CONSOLE_MOKOJOOMBACKUP="Console - MokoJoomBackup" +PLG_CONSOLE_MOKOJOOMBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/source/packages/plg_console_mokojoombackup/language/en-US/plg_console_mokojoombackup.sys.ini b/source/packages/plg_console_mokojoombackup/language/en-US/plg_console_mokojoombackup.sys.ini new file mode 100644 index 0000000..d229b02 --- /dev/null +++ b/source/packages/plg_console_mokojoombackup/language/en-US/plg_console_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Console Plugin system language file (en-US) +PLG_CONSOLE_MOKOJOOMBACKUP="Console - MokoJoomBackup" +PLG_CONSOLE_MOKOJOOMBACKUP_DESCRIPTION="CLI commands for MokoJoomBackup: run, list, profiles, restore, cleanup." diff --git a/source/packages/plg_content_mokobackup/mokobackup.php b/source/packages/plg_console_mokojoombackup/mokojoombackup.php similarity index 86% rename from source/packages/plg_content_mokobackup/mokobackup.php rename to source/packages/plg_console_mokojoombackup/mokojoombackup.php index 2dd15e4..bb0f01c 100644 --- a/source/packages/plg_content_mokobackup/mokobackup.php +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_content_mokobackup + * @subpackage plg_console_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE diff --git a/source/packages/plg_console_mokobackup/mokobackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml similarity index 60% rename from source/packages/plg_console_mokobackup/mokobackup.xml rename to source/packages/plg_console_mokojoombackup/mokojoombackup.xml index b583077..db41eeb 100644 --- a/source/packages/plg_console_mokobackup/mokobackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -1,32 +1,32 @@ - plg_console_mokobackup - 01.01.09-dev + plg_console_mokojoombackup + 01.01.07-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_CONSOLE_MOKOBACKUP_DESCRIPTION + PLG_CONSOLE_MOKOJOOMBACKUP_DESCRIPTION - Joomla\Plugin\Console\MokoBackup + Joomla\Plugin\Console\MokoJoomBackup - mokobackup.php + mokojoombackup.php services src - language/en-GB/plg_console_mokobackup.ini - language/en-GB/plg_console_mokobackup.sys.ini + language/en-GB/plg_console_mokojoombackup.ini + language/en-GB/plg_console_mokojoombackup.sys.ini diff --git a/source/packages/plg_webservices_mokobackup/services/provider.php b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/services/provider.php similarity index 79% rename from source/packages/plg_webservices_mokobackup/services/provider.php rename to source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/services/provider.php index 96e07f1..e80cfa1 100644 --- a/source/packages/plg_webservices_mokobackup/services/provider.php +++ b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/services/provider.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_webservices_mokobackup + * @subpackage plg_console_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -16,7 +16,7 @@ use Joomla\CMS\Plugin\PluginHelper; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; -use Joomla\Plugin\WebServices\MokoBackup\Extension\MokoBackupWebServices; +use Joomla\Plugin\Console\MokoJoomBackup\Extension\MokoJoomBackupConsole; return new class () implements ServiceProviderInterface { public function register(Container $container): void @@ -24,9 +24,9 @@ return new class () implements ServiceProviderInterface { $container->set( PluginInterface::class, function (Container $container) { - $plugin = new MokoBackupWebServices( + $plugin = new MokoJoomBackupConsole( $container->get(DispatcherInterface::class), - (array) PluginHelper::getPlugin('webservices', 'mokobackup') + (array) PluginHelper::getPlugin('console', 'mokojoombackup') ); $plugin->setApplication(Factory::getApplication()); diff --git a/source/packages/plg_console_mokobackup/src/Command/CleanupCommand.php b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/CleanupCommand.php similarity index 88% rename from source/packages/plg_console_mokobackup/src/Command/CleanupCommand.php rename to source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/CleanupCommand.php index 1a8509a..68615b1 100644 --- a/source/packages/plg_console_mokobackup/src/Command/CleanupCommand.php +++ b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/CleanupCommand.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage plg_console_mokobackup + * @subpackage plg_console_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Plugin\Console\MokoBackup\Command; +namespace Joomla\Plugin\Console\MokoJoomBackup\Command; defined('_JEXEC') or die; @@ -21,7 +21,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; class CleanupCommand extends AbstractCommand { - protected static $defaultName = 'mokobackup:cleanup'; + protected static $defaultName = 'mokojoombackup:cleanup'; protected function configure(): void { @@ -51,7 +51,7 @@ class CleanupCommand extends AbstractCommand $cutoff = date('Y-m-d H:i:s', strtotime("-{$maxAge} days")); $query = $db->getQuery(true) ->select('id, absolute_path, description, backupstart') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('backupstart') . ' < ' . $db->quote($cutoff)) ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); $db->setQuery($query); @@ -67,7 +67,7 @@ class CleanupCommand extends AbstractCommand $db->setQuery( $db->getQuery(true) - ->delete($db->quoteName('#__mokobackup_records')) + ->delete($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('id') . ' = ' . (int) $record->id) ); $db->execute(); @@ -79,7 +79,7 @@ class CleanupCommand extends AbstractCommand // Enforce max count $query = $db->getQuery(true) ->select('COUNT(*)') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); $db->setQuery($query); $totalCount = (int) $db->loadResult(); @@ -88,7 +88,7 @@ class CleanupCommand extends AbstractCommand $excess = $totalCount - $maxCount; $query = $db->getQuery(true) ->select('id, absolute_path, description, backupstart') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('status') . ' = ' . $db->quote('complete')) ->order($db->quoteName('backupstart') . ' ASC'); $db->setQuery($query, 0, $excess); @@ -104,7 +104,7 @@ class CleanupCommand extends AbstractCommand $db->setQuery( $db->getQuery(true) - ->delete($db->quoteName('#__mokobackup_records')) + ->delete($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('id') . ' = ' . (int) $record->id) ); $db->execute(); diff --git a/source/packages/plg_console_mokobackup/src/Command/ListCommand.php b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ListCommand.php similarity index 87% rename from source/packages/plg_console_mokobackup/src/Command/ListCommand.php rename to source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ListCommand.php index 9586339..b8aebe7 100644 --- a/source/packages/plg_console_mokobackup/src/Command/ListCommand.php +++ b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ListCommand.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage plg_console_mokobackup + * @subpackage plg_console_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Plugin\Console\MokoBackup\Command; +namespace Joomla\Plugin\Console\MokoJoomBackup\Command; defined('_JEXEC') or die; @@ -21,7 +21,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; class ListCommand extends AbstractCommand { - protected static $defaultName = 'mokobackup:list'; + protected static $defaultName = 'mokojoombackup:list'; protected function configure(): void { @@ -42,8 +42,8 @@ class ListCommand extends AbstractCommand $query = $db->getQuery(true) ->select('r.id, r.description, r.status, r.origin, r.backup_type, r.total_size, r.backupstart, r.backupend') ->select($db->quoteName('p.title', 'profile_title')) - ->from($db->quoteName('#__mokobackup_records', 'r')) - ->join('LEFT', $db->quoteName('#__mokobackup_profiles', 'p') . ' ON p.id = r.profile_id') + ->from($db->quoteName('#__mokojoombackup_records', 'r')) + ->join('LEFT', $db->quoteName('#__mokojoombackup_profiles', 'p') . ' ON p.id = r.profile_id') ->order($db->quoteName('r.backupstart') . ' DESC'); if ($status) { diff --git a/source/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ProfilesCommand.php similarity index 86% rename from source/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php rename to source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ProfilesCommand.php index 8f4b21c..22e8723 100644 --- a/source/packages/plg_console_mokobackup/src/Command/ProfilesCommand.php +++ b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ProfilesCommand.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage plg_console_mokobackup + * @subpackage plg_console_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Plugin\Console\MokoBackup\Command; +namespace Joomla\Plugin\Console\MokoJoomBackup\Command; defined('_JEXEC') or die; @@ -20,7 +20,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; class ProfilesCommand extends AbstractCommand { - protected static $defaultName = 'mokobackup:profiles'; + protected static $defaultName = 'mokojoombackup:profiles'; protected function configure(): void { @@ -36,7 +36,7 @@ class ProfilesCommand extends AbstractCommand $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('id, title, backup_type, published, ordering') - ->from($db->quoteName('#__mokobackup_profiles')) + ->from($db->quoteName('#__mokojoombackup_profiles')) ->order($db->quoteName('ordering') . ' ASC'); $db->setQuery($query); $profiles = $db->loadObjectList(); diff --git a/source/packages/plg_console_mokobackup/src/Command/RestoreCommand.php b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RestoreCommand.php similarity index 86% rename from source/packages/plg_console_mokobackup/src/Command/RestoreCommand.php rename to source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RestoreCommand.php index e5f9082..cccc480 100644 --- a/source/packages/plg_console_mokobackup/src/Command/RestoreCommand.php +++ b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RestoreCommand.php @@ -2,18 +2,18 @@ /** * @package MokoJoomBackup - * @subpackage plg_console_mokobackup + * @subpackage plg_console_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Plugin\Console\MokoBackup\Command; +namespace Joomla\Plugin\Console\MokoJoomBackup\Command; defined('_JEXEC') or die; use Joomla\CMS\Factory; -use Joomla\Component\MokoBackup\Administrator\Engine\RestoreEngine; +use Joomla\Component\MokoJoomBackup\Administrator\Engine\RestoreEngine; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -22,7 +22,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; class RestoreCommand extends AbstractCommand { - protected static $defaultName = 'mokobackup:restore'; + protected static $defaultName = 'mokojoombackup:restore'; protected function configure(): void { @@ -40,7 +40,7 @@ class RestoreCommand extends AbstractCommand $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('id') . ' = ' . $recordId); $db->setQuery($query); $record = $db->loadObject(); @@ -73,7 +73,7 @@ class RestoreCommand extends AbstractCommand return 0; } - $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokobackup/src/Engine/RestoreEngine.php'; + $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokojoombackup/src/Engine/RestoreEngine.php'; if (!file_exists($engineFile)) { $io->error('RestoreEngine not found. Is the component fully installed?'); diff --git a/source/packages/plg_console_mokobackup/src/Command/RunCommand.php b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RunCommand.php similarity index 83% rename from source/packages/plg_console_mokobackup/src/Command/RunCommand.php rename to source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RunCommand.php index d187737..78991af 100644 --- a/source/packages/plg_console_mokobackup/src/Command/RunCommand.php +++ b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RunCommand.php @@ -2,18 +2,18 @@ /** * @package MokoJoomBackup - * @subpackage plg_console_mokobackup + * @subpackage plg_console_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Plugin\Console\MokoBackup\Command; +namespace Joomla\Plugin\Console\MokoJoomBackup\Command; defined('_JEXEC') or die; use Joomla\CMS\Factory; -use Joomla\Component\MokoBackup\Administrator\Engine\BackupEngine; +use Joomla\Component\MokoJoomBackup\Administrator\Engine\BackupEngine; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -22,7 +22,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; class RunCommand extends AbstractCommand { - protected static $defaultName = 'mokobackup:run'; + protected static $defaultName = 'mokojoombackup:run'; protected function configure(): void { @@ -40,7 +40,7 @@ class RunCommand extends AbstractCommand $io->title('MokoJoomBackup — Run Backup'); $io->text('Profile ID: ' . $profileId); - $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokobackup/src/Engine/BackupEngine.php'; + $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokojoombackup/src/Engine/BackupEngine.php'; if (!file_exists($engineFile)) { $io->error('MokoJoomBackup component not installed.'); diff --git a/source/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Extension/MokoBackupConsole.php similarity index 63% rename from source/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php rename to source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Extension/MokoBackupConsole.php index fca96ac..2c04833 100644 --- a/source/packages/plg_console_mokobackup/src/Extension/MokoBackupConsole.php +++ b/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Extension/MokoBackupConsole.php @@ -2,26 +2,26 @@ /** * @package MokoJoomBackup - * @subpackage plg_console_mokobackup + * @subpackage plg_console_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Plugin\Console\MokoBackup\Extension; +namespace Joomla\Plugin\Console\MokoJoomBackup\Extension; defined('_JEXEC') or die; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Event\Event; use Joomla\Event\SubscriberInterface; -use Joomla\Plugin\Console\MokoBackup\Command\CleanupCommand; -use Joomla\Plugin\Console\MokoBackup\Command\ListCommand; -use Joomla\Plugin\Console\MokoBackup\Command\ProfilesCommand; -use Joomla\Plugin\Console\MokoBackup\Command\RestoreCommand; -use Joomla\Plugin\Console\MokoBackup\Command\RunCommand; +use Joomla\Plugin\Console\MokoJoomBackup\Command\CleanupCommand; +use Joomla\Plugin\Console\MokoJoomBackup\Command\ListCommand; +use Joomla\Plugin\Console\MokoJoomBackup\Command\ProfilesCommand; +use Joomla\Plugin\Console\MokoJoomBackup\Command\RestoreCommand; +use Joomla\Plugin\Console\MokoJoomBackup\Command\RunCommand; -final class MokoBackupConsole extends CMSPlugin implements SubscriberInterface +final class MokoJoomBackupConsole extends CMSPlugin implements SubscriberInterface { protected $autoloadLanguage = true; diff --git a/source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini b/source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini deleted file mode 100644 index 5f23262..0000000 --- a/source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.ini +++ /dev/null @@ -1,9 +0,0 @@ -; MokoJoomBackup — Content Plugin language file (en-GB) -PLG_CONTENT_MOKOBACKUP="Content - MokoJoomBackup" -PLG_CONTENT_MOKOBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." -PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_INSTALL="Backup Before Install" -PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_INSTALL_DESC="Run an automatic backup before a new extension is installed." -PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_UPDATE="Backup Before Update" -PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_UPDATE_DESC="Run an automatic backup before an extension is updated." -PLG_CONTENT_MOKOBACKUP_FIELD_PROFILE="Backup Profile" -PLG_CONTENT_MOKOBACKUP_FIELD_PROFILE_DESC="Which backup profile to use for automatic backups." diff --git a/source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini b/source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini deleted file mode 100644 index 3d79871..0000000 --- a/source/packages/plg_content_mokobackup/language/en-GB/plg_content_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Content Plugin system language file (en-GB) -PLG_CONTENT_MOKOBACKUP="Content - MokoJoomBackup" -PLG_CONTENT_MOKOBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." diff --git a/source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini b/source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini deleted file mode 100644 index 1bac9a8..0000000 --- a/source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.ini +++ /dev/null @@ -1,9 +0,0 @@ -; MokoJoomBackup — Content Plugin language file (en-US) -PLG_CONTENT_MOKOBACKUP="Content - MokoJoomBackup" -PLG_CONTENT_MOKOBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." -PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_INSTALL="Backup Before Install" -PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_INSTALL_DESC="Run an automatic backup before a new extension is installed." -PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_UPDATE="Backup Before Update" -PLG_CONTENT_MOKOBACKUP_FIELD_BEFORE_UPDATE_DESC="Run an automatic backup before an extension is updated." -PLG_CONTENT_MOKOBACKUP_FIELD_PROFILE="Backup Profile" -PLG_CONTENT_MOKOBACKUP_FIELD_PROFILE_DESC="Which backup profile to use for automatic backups." diff --git a/source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini b/source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini deleted file mode 100644 index 7a612b3..0000000 --- a/source/packages/plg_content_mokobackup/language/en-US/plg_content_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Content Plugin system language file (en-US) -PLG_CONTENT_MOKOBACKUP="Content - MokoJoomBackup" -PLG_CONTENT_MOKOBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." diff --git a/source/packages/plg_content_mokojoombackup/language/en-GB/plg_content_mokojoombackup.ini b/source/packages/plg_content_mokojoombackup/language/en-GB/plg_content_mokojoombackup.ini new file mode 100644 index 0000000..b7e9375 --- /dev/null +++ b/source/packages/plg_content_mokojoombackup/language/en-GB/plg_content_mokojoombackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — Content Plugin language file (en-GB) +PLG_CONTENT_MOKOJOOMBACKUP="Content - MokoJoomBackup" +PLG_CONTENT_MOKOJOOMBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_BEFORE_INSTALL="Backup Before Install" +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_BEFORE_INSTALL_DESC="Run an automatic backup before a new extension is installed." +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_BEFORE_UPDATE="Backup Before Update" +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_BEFORE_UPDATE_DESC="Run an automatic backup before an extension is updated." +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_PROFILE="Backup Profile" +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_PROFILE_DESC="Which backup profile to use for automatic backups." diff --git a/source/packages/plg_content_mokojoombackup/language/en-GB/plg_content_mokojoombackup.sys.ini b/source/packages/plg_content_mokojoombackup/language/en-GB/plg_content_mokojoombackup.sys.ini new file mode 100644 index 0000000..bb42dfd --- /dev/null +++ b/source/packages/plg_content_mokojoombackup/language/en-GB/plg_content_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Content Plugin system language file (en-GB) +PLG_CONTENT_MOKOJOOMBACKUP="Content - MokoJoomBackup" +PLG_CONTENT_MOKOJOOMBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." diff --git a/source/packages/plg_content_mokojoombackup/language/en-US/plg_content_mokojoombackup.ini b/source/packages/plg_content_mokojoombackup/language/en-US/plg_content_mokojoombackup.ini new file mode 100644 index 0000000..0765605 --- /dev/null +++ b/source/packages/plg_content_mokojoombackup/language/en-US/plg_content_mokojoombackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — Content Plugin language file (en-US) +PLG_CONTENT_MOKOJOOMBACKUP="Content - MokoJoomBackup" +PLG_CONTENT_MOKOJOOMBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_BEFORE_INSTALL="Backup Before Install" +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_BEFORE_INSTALL_DESC="Run an automatic backup before a new extension is installed." +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_BEFORE_UPDATE="Backup Before Update" +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_BEFORE_UPDATE_DESC="Run an automatic backup before an extension is updated." +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_PROFILE="Backup Profile" +PLG_CONTENT_MOKOJOOMBACKUP_FIELD_PROFILE_DESC="Which backup profile to use for automatic backups." diff --git a/source/packages/plg_content_mokojoombackup/language/en-US/plg_content_mokojoombackup.sys.ini b/source/packages/plg_content_mokojoombackup/language/en-US/plg_content_mokojoombackup.sys.ini new file mode 100644 index 0000000..4c443cf --- /dev/null +++ b/source/packages/plg_content_mokojoombackup/language/en-US/plg_content_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Content Plugin system language file (en-US) +PLG_CONTENT_MOKOJOOMBACKUP="Content - MokoJoomBackup" +PLG_CONTENT_MOKOJOOMBACKUP_DESCRIPTION="Automatically triggers a backup before extension installs or updates." diff --git a/source/packages/plg_console_mokobackup/mokobackup.php b/source/packages/plg_content_mokojoombackup/mokojoombackup.php similarity index 86% rename from source/packages/plg_console_mokobackup/mokobackup.php rename to source/packages/plg_content_mokojoombackup/mokojoombackup.php index 724a1bb..c59a8b8 100644 --- a/source/packages/plg_console_mokobackup/mokobackup.php +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_console_mokobackup + * @subpackage plg_content_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE diff --git a/source/packages/plg_content_mokobackup/mokobackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml similarity index 58% rename from source/packages/plg_content_mokobackup/mokobackup.xml rename to source/packages/plg_content_mokojoombackup/mokojoombackup.xml index b254f08..b208cd3 100644 --- a/source/packages/plg_content_mokobackup/mokobackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -1,33 +1,33 @@ - plg_content_mokobackup - 01.01.09-dev + plg_content_mokojoombackup + 01.01.07-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_CONTENT_MOKOBACKUP_DESCRIPTION + PLG_CONTENT_MOKOJOOMBACKUP_DESCRIPTION - Joomla\Plugin\Content\MokoBackup + Joomla\Plugin\Content\MokoJoomBackup - mokobackup.php + mokojoombackup.php services src - language/en-GB/plg_content_mokobackup.ini - language/en-GB/plg_content_mokobackup.sys.ini + language/en-GB/plg_content_mokojoombackup.ini + language/en-GB/plg_content_mokojoombackup.sys.ini @@ -36,8 +36,8 @@ @@ -47,8 +47,8 @@ @@ -58,9 +58,9 @@ diff --git a/source/packages/plg_content_mokobackup/services/provider.php b/source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/services/provider.php similarity index 78% rename from source/packages/plg_content_mokobackup/services/provider.php rename to source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/services/provider.php index 4635162..afcd020 100644 --- a/source/packages/plg_content_mokobackup/services/provider.php +++ b/source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/services/provider.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_content_mokobackup + * @subpackage plg_content_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -16,7 +16,7 @@ use Joomla\CMS\Plugin\PluginHelper; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; -use Joomla\Plugin\Content\MokoBackup\Extension\MokoBackupContent; +use Joomla\Plugin\Content\MokoJoomBackup\Extension\MokoJoomBackupContent; return new class () implements ServiceProviderInterface { public function register(Container $container): void @@ -24,9 +24,9 @@ return new class () implements ServiceProviderInterface { $container->set( PluginInterface::class, function (Container $container) { - $plugin = new MokoBackupContent( + $plugin = new MokoJoomBackupContent( $container->get(DispatcherInterface::class), - (array) PluginHelper::getPlugin('content', 'mokobackup') + (array) PluginHelper::getPlugin('content', 'mokojoombackup') ); $plugin->setApplication(Factory::getApplication()); diff --git a/source/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php b/source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/src/Extension/MokoBackupContent.php similarity index 79% rename from source/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php rename to source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/src/Extension/MokoBackupContent.php index b27d119..ae65490 100644 --- a/source/packages/plg_content_mokobackup/src/Extension/MokoBackupContent.php +++ b/source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/src/Extension/MokoBackupContent.php @@ -2,23 +2,23 @@ /** * @package MokoJoomBackup - * @subpackage plg_content_mokobackup + * @subpackage plg_content_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Plugin\Content\MokoBackup\Extension; +namespace Joomla\Plugin\Content\MokoJoomBackup\Extension; defined('_JEXEC') or die; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Component\MokoBackup\Administrator\Engine\BackupEngine; +use Joomla\Component\MokoJoomBackup\Administrator\Engine\BackupEngine; use Joomla\Event\Event; use Joomla\Event\SubscriberInterface; -final class MokoBackupContent extends CMSPlugin implements SubscriberInterface +final class MokoJoomBackupContent extends CMSPlugin implements SubscriberInterface { protected $autoloadLanguage = true; @@ -63,15 +63,15 @@ final class MokoBackupContent extends CMSPlugin implements SubscriberInterface // Throttle: only one auto-backup per hour via session $session = Factory::getSession(); - $lastRun = $session->get('mokobackup.content_last_autobackup', 0); + $lastRun = $session->get('mokojoombackup.content_last_autobackup', 0); if (time() - $lastRun < 3600) { return; } - $session->set('mokobackup.content_last_autobackup', time()); + $session->set('mokojoombackup.content_last_autobackup', time()); - $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokobackup/src/Engine/BackupEngine.php'; + $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokojoombackup/src/Engine/BackupEngine.php'; if (!file_exists($engineFile)) { return; diff --git a/source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.ini b/source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.ini deleted file mode 100644 index 7dbf949..0000000 --- a/source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.ini +++ /dev/null @@ -1,6 +0,0 @@ -PLG_QUICKICON_MOKOBACKUP="Quick Icon - MokoJoomBackup" -PLG_QUICKICON_MOKOBACKUP_DESCRIPTION="Shows backup status on the administrator dashboard." -PLG_QUICKICON_MOKOBACKUP_OK="Backups: OK" -PLG_QUICKICON_MOKOBACKUP_NO_BACKUPS="Backups: No backups yet!" -PLG_QUICKICON_MOKOBACKUP_FAILURES="Backups: Recent failures!" -PLG_QUICKICON_MOKOBACKUP_STALE="Backups: Last backup > 7 days ago" diff --git a/source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.sys.ini b/source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.sys.ini deleted file mode 100644 index 6d06042..0000000 --- a/source/packages/plg_quickicon_mokobackup/language/en-GB/plg_quickicon_mokobackup.sys.ini +++ /dev/null @@ -1,2 +0,0 @@ -PLG_QUICKICON_MOKOBACKUP="Quick Icon - MokoJoomBackup" -PLG_QUICKICON_MOKOBACKUP_DESCRIPTION="Shows backup status on the administrator dashboard." diff --git a/source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.ini b/source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.ini deleted file mode 100644 index 7dbf949..0000000 --- a/source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.ini +++ /dev/null @@ -1,6 +0,0 @@ -PLG_QUICKICON_MOKOBACKUP="Quick Icon - MokoJoomBackup" -PLG_QUICKICON_MOKOBACKUP_DESCRIPTION="Shows backup status on the administrator dashboard." -PLG_QUICKICON_MOKOBACKUP_OK="Backups: OK" -PLG_QUICKICON_MOKOBACKUP_NO_BACKUPS="Backups: No backups yet!" -PLG_QUICKICON_MOKOBACKUP_FAILURES="Backups: Recent failures!" -PLG_QUICKICON_MOKOBACKUP_STALE="Backups: Last backup > 7 days ago" diff --git a/source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.sys.ini b/source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.sys.ini deleted file mode 100644 index 6d06042..0000000 --- a/source/packages/plg_quickicon_mokobackup/language/en-US/plg_quickicon_mokobackup.sys.ini +++ /dev/null @@ -1,2 +0,0 @@ -PLG_QUICKICON_MOKOBACKUP="Quick Icon - MokoJoomBackup" -PLG_QUICKICON_MOKOBACKUP_DESCRIPTION="Shows backup status on the administrator dashboard." diff --git a/source/packages/plg_quickicon_mokojoombackup/language/en-GB/plg_quickicon_mokojoombackup.ini b/source/packages/plg_quickicon_mokojoombackup/language/en-GB/plg_quickicon_mokojoombackup.ini new file mode 100644 index 0000000..cc0a4e5 --- /dev/null +++ b/source/packages/plg_quickicon_mokojoombackup/language/en-GB/plg_quickicon_mokojoombackup.ini @@ -0,0 +1,6 @@ +PLG_QUICKICON_MOKOJOOMBACKUP="Quick Icon - MokoJoomBackup" +PLG_QUICKICON_MOKOJOOMBACKUP_DESCRIPTION="Shows backup status on the administrator dashboard." +PLG_QUICKICON_MOKOJOOMBACKUP_OK="Backups: OK" +PLG_QUICKICON_MOKOJOOMBACKUP_NO_BACKUPS="Backups: No backups yet!" +PLG_QUICKICON_MOKOJOOMBACKUP_FAILURES="Backups: Recent failures!" +PLG_QUICKICON_MOKOJOOMBACKUP_STALE="Backups: Last backup > 7 days ago" diff --git a/source/packages/plg_quickicon_mokojoombackup/language/en-GB/plg_quickicon_mokojoombackup.sys.ini b/source/packages/plg_quickicon_mokojoombackup/language/en-GB/plg_quickicon_mokojoombackup.sys.ini new file mode 100644 index 0000000..d432f08 --- /dev/null +++ b/source/packages/plg_quickicon_mokojoombackup/language/en-GB/plg_quickicon_mokojoombackup.sys.ini @@ -0,0 +1,2 @@ +PLG_QUICKICON_MOKOJOOMBACKUP="Quick Icon - MokoJoomBackup" +PLG_QUICKICON_MOKOJOOMBACKUP_DESCRIPTION="Shows backup status on the administrator dashboard." diff --git a/source/packages/plg_quickicon_mokojoombackup/language/en-US/plg_quickicon_mokojoombackup.ini b/source/packages/plg_quickicon_mokojoombackup/language/en-US/plg_quickicon_mokojoombackup.ini new file mode 100644 index 0000000..cc0a4e5 --- /dev/null +++ b/source/packages/plg_quickicon_mokojoombackup/language/en-US/plg_quickicon_mokojoombackup.ini @@ -0,0 +1,6 @@ +PLG_QUICKICON_MOKOJOOMBACKUP="Quick Icon - MokoJoomBackup" +PLG_QUICKICON_MOKOJOOMBACKUP_DESCRIPTION="Shows backup status on the administrator dashboard." +PLG_QUICKICON_MOKOJOOMBACKUP_OK="Backups: OK" +PLG_QUICKICON_MOKOJOOMBACKUP_NO_BACKUPS="Backups: No backups yet!" +PLG_QUICKICON_MOKOJOOMBACKUP_FAILURES="Backups: Recent failures!" +PLG_QUICKICON_MOKOJOOMBACKUP_STALE="Backups: Last backup > 7 days ago" diff --git a/source/packages/plg_quickicon_mokojoombackup/language/en-US/plg_quickicon_mokojoombackup.sys.ini b/source/packages/plg_quickicon_mokojoombackup/language/en-US/plg_quickicon_mokojoombackup.sys.ini new file mode 100644 index 0000000..d432f08 --- /dev/null +++ b/source/packages/plg_quickicon_mokojoombackup/language/en-US/plg_quickicon_mokojoombackup.sys.ini @@ -0,0 +1,2 @@ +PLG_QUICKICON_MOKOJOOMBACKUP="Quick Icon - MokoJoomBackup" +PLG_QUICKICON_MOKOJOOMBACKUP_DESCRIPTION="Shows backup status on the administrator dashboard." diff --git a/source/packages/plg_quickicon_mokobackup/mokobackup.php b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.php similarity index 100% rename from source/packages/plg_quickicon_mokobackup/mokobackup.php rename to source/packages/plg_quickicon_mokojoombackup/mokojoombackup.php diff --git a/source/packages/plg_quickicon_mokobackup/mokobackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml similarity index 52% rename from source/packages/plg_quickicon_mokobackup/mokobackup.xml rename to source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index 928feed..2d284a5 100644 --- a/source/packages/plg_quickicon_mokobackup/mokobackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,25 +1,25 @@ - plg_quickicon_mokobackup - 01.01.09-dev + plg_quickicon_mokojoombackup + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_QUICKICON_MOKOBACKUP_DESCRIPTION + PLG_QUICKICON_MOKOJOOMBACKUP_DESCRIPTION - Joomla\Plugin\Quickicon\MokoBackup + Joomla\Plugin\Quickicon\MokoJoomBackup - mokobackup.php + mokojoombackup.php services src - language/en-GB/plg_quickicon_mokobackup.ini - language/en-GB/plg_quickicon_mokobackup.sys.ini + language/en-GB/plg_quickicon_mokojoombackup.ini + language/en-GB/plg_quickicon_mokojoombackup.sys.ini diff --git a/source/packages/plg_quickicon_mokobackup/index.html b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/index.html similarity index 100% rename from source/packages/plg_quickicon_mokobackup/index.html rename to source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/index.html diff --git a/source/packages/plg_quickicon_mokobackup/language/en-GB/index.html b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/en-GB/index.html similarity index 100% rename from source/packages/plg_quickicon_mokobackup/language/en-GB/index.html rename to source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/en-GB/index.html diff --git a/source/packages/plg_quickicon_mokobackup/language/en-US/index.html b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/en-US/index.html similarity index 100% rename from source/packages/plg_quickicon_mokobackup/language/en-US/index.html rename to source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/en-US/index.html diff --git a/source/packages/plg_quickicon_mokobackup/language/index.html b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/index.html similarity index 100% rename from source/packages/plg_quickicon_mokobackup/language/index.html rename to source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/index.html diff --git a/source/packages/plg_quickicon_mokobackup/services/index.html b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/services/index.html similarity index 100% rename from source/packages/plg_quickicon_mokobackup/services/index.html rename to source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/services/index.html diff --git a/source/packages/plg_quickicon_mokobackup/services/provider.php b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/services/provider.php similarity index 75% rename from source/packages/plg_quickicon_mokobackup/services/provider.php rename to source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/services/provider.php index 32262ef..4af1c9f 100644 --- a/source/packages/plg_quickicon_mokobackup/services/provider.php +++ b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/services/provider.php @@ -8,7 +8,7 @@ use Joomla\CMS\Plugin\PluginHelper; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; -use Joomla\Plugin\Quickicon\MokoBackup\Extension\MokoBackupQuickicon; +use Joomla\Plugin\Quickicon\MokoJoomBackup\Extension\MokoJoomBackupQuickicon; return new class () implements ServiceProviderInterface { public function register(Container $container): void @@ -16,9 +16,9 @@ return new class () implements ServiceProviderInterface { $container->set( PluginInterface::class, function (Container $container) { - $plugin = new MokoBackupQuickicon( + $plugin = new MokoJoomBackupQuickicon( $container->get(DispatcherInterface::class), - (array) PluginHelper::getPlugin('quickicon', 'mokobackup') + (array) PluginHelper::getPlugin('quickicon', 'mokojoombackup') ); $plugin->setApplication(Factory::getApplication()); diff --git a/source/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/Extension/MokoBackupQuickicon.php similarity index 81% rename from source/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php rename to source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/Extension/MokoBackupQuickicon.php index c72cda5..b17baee 100644 --- a/source/packages/plg_quickicon_mokobackup/src/Extension/MokoBackupQuickicon.php +++ b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/Extension/MokoBackupQuickicon.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_quickicon_mokobackup + * @subpackage plg_quickicon_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -10,7 +10,7 @@ * Dashboard quickicon widget showing backup status at a glance. */ -namespace Joomla\Plugin\Quickicon\MokoBackup\Extension; +namespace Joomla\Plugin\Quickicon\MokoJoomBackup\Extension; defined('_JEXEC') or die; @@ -20,7 +20,7 @@ use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Event\Event; use Joomla\Event\SubscriberInterface; -final class MokoBackupQuickicon extends CMSPlugin implements SubscriberInterface +final class MokoJoomBackupQuickicon extends CMSPlugin implements SubscriberInterface { protected $autoloadLanguage = true; @@ -44,7 +44,7 @@ final class MokoBackupQuickicon extends CMSPlugin implements SubscriberInterface // Get last completed backup $query = $db->getQuery(true) ->select('*') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('status') . ' = ' . $db->quote('complete')) ->order($db->quoteName('backupstart') . ' DESC'); $db->setQuery($query, 0, 1); @@ -53,7 +53,7 @@ final class MokoBackupQuickicon extends CMSPlugin implements SubscriberInterface // Get total count and storage $query = $db->getQuery(true) ->select('COUNT(*) AS total, COALESCE(SUM(total_size), 0) AS total_size') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); $db->setQuery($query); $stats = $db->loadObject(); @@ -61,7 +61,7 @@ final class MokoBackupQuickicon extends CMSPlugin implements SubscriberInterface // Check for recent failures $query = $db->getQuery(true) ->select('COUNT(*)') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('status') . ' = ' . $db->quote('fail')) ->where($db->quoteName('backupstart') . ' > DATE_SUB(NOW(), INTERVAL 7 DAY)'); $db->setQuery($query); @@ -69,17 +69,17 @@ final class MokoBackupQuickicon extends CMSPlugin implements SubscriberInterface // Determine icon state $warning = false; - $text = 'PLG_QUICKICON_MOKOBACKUP_OK'; + $text = 'PLG_QUICKICON_MOKOJOOMBACKUP_OK'; if (!$lastBackup) { $warning = true; - $text = 'PLG_QUICKICON_MOKOBACKUP_NO_BACKUPS'; + $text = 'PLG_QUICKICON_MOKOJOOMBACKUP_NO_BACKUPS'; } elseif ($recentFailures > 0) { $warning = true; - $text = 'PLG_QUICKICON_MOKOBACKUP_FAILURES'; + $text = 'PLG_QUICKICON_MOKOJOOMBACKUP_FAILURES'; } elseif (strtotime($lastBackup->backupstart) < strtotime('-7 days')) { $warning = true; - $text = 'PLG_QUICKICON_MOKOBACKUP_STALE'; + $text = 'PLG_QUICKICON_MOKOJOOMBACKUP_STALE'; } // Build subtitle @@ -94,12 +94,12 @@ final class MokoBackupQuickicon extends CMSPlugin implements SubscriberInterface $result = $event->getArgument('result', []); $result[] = [ [ - 'link' => 'index.php?option=com_mokobackup&view=backups', + 'link' => 'index.php?option=com_mokojoombackup&view=backups', 'image' => $warning ? 'icon-warning' : 'icon-database', 'icon' => $warning ? 'icon-warning' : 'icon-database', 'text' => Text::_($text), 'linkadd' => $subtitle ? '
    ' . htmlspecialchars($subtitle) . '' : '', - 'id' => 'plg_quickicon_mokobackup', + 'id' => 'plg_quickicon_mokojoombackup', 'group' => 'MOD_QUICKICON_MAINTENANCE', ], ]; diff --git a/source/packages/plg_quickicon_mokobackup/src/Extension/index.html b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/plg_quickicon_mokobackup/src/Extension/index.html rename to source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/Extension/index.html diff --git a/source/packages/plg_quickicon_mokobackup/src/index.html b/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/index.html similarity index 100% rename from source/packages/plg_quickicon_mokobackup/src/index.html rename to source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/index.html diff --git a/source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.ini b/source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.ini deleted file mode 100644 index cdba55f..0000000 --- a/source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.ini +++ /dev/null @@ -1,9 +0,0 @@ -; MokoJoomBackup — System Plugin language file (en-GB) -PLG_SYSTEM_MOKOBACKUP="System - MokoJoomBackup" -PLG_SYSTEM_MOKOBACKUP_DESCRIPTION="Automatic cleanup of expired backup archives and scheduled backup triggers." -PLG_SYSTEM_MOKOBACKUP_FIELD_AUTO_CLEANUP="Auto Cleanup" -PLG_SYSTEM_MOKOBACKUP_FIELD_AUTO_CLEANUP_DESC="Automatically remove old backup archives based on age and count limits." -PLG_SYSTEM_MOKOBACKUP_FIELD_MAX_AGE="Max Backup Age (days)" -PLG_SYSTEM_MOKOBACKUP_FIELD_MAX_AGE_DESC="Delete backup records older than this many days." -PLG_SYSTEM_MOKOBACKUP_FIELD_MAX_BACKUPS="Max Backup Count" -PLG_SYSTEM_MOKOBACKUP_FIELD_MAX_BACKUPS_DESC="Keep at most this many completed backup records." diff --git a/source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.sys.ini b/source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.sys.ini deleted file mode 100644 index af5c9d2..0000000 --- a/source/packages/plg_system_mokobackup/language/en-GB/plg_system_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — System Plugin system language file (en-GB) -PLG_SYSTEM_MOKOBACKUP="System - MokoJoomBackup" -PLG_SYSTEM_MOKOBACKUP_DESCRIPTION="Automatic cleanup of expired backup archives and scheduled backup triggers." diff --git a/source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.ini b/source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.ini deleted file mode 100644 index b9b8d5e..0000000 --- a/source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.ini +++ /dev/null @@ -1,9 +0,0 @@ -; MokoJoomBackup — System Plugin language file (en-US) -PLG_SYSTEM_MOKOBACKUP="System - MokoJoomBackup" -PLG_SYSTEM_MOKOBACKUP_DESCRIPTION="Automatic cleanup of expired backup archives and scheduled backup triggers." -PLG_SYSTEM_MOKOBACKUP_FIELD_AUTO_CLEANUP="Auto Cleanup" -PLG_SYSTEM_MOKOBACKUP_FIELD_AUTO_CLEANUP_DESC="Automatically remove old backup archives based on age and count limits." -PLG_SYSTEM_MOKOBACKUP_FIELD_MAX_AGE="Max Backup Age (days)" -PLG_SYSTEM_MOKOBACKUP_FIELD_MAX_AGE_DESC="Delete backup records older than this many days." -PLG_SYSTEM_MOKOBACKUP_FIELD_MAX_BACKUPS="Max Backup Count" -PLG_SYSTEM_MOKOBACKUP_FIELD_MAX_BACKUPS_DESC="Keep at most this many completed backup records." diff --git a/source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.sys.ini b/source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.sys.ini deleted file mode 100644 index c96a369..0000000 --- a/source/packages/plg_system_mokobackup/language/en-US/plg_system_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — System Plugin system language file (en-US) -PLG_SYSTEM_MOKOBACKUP="System - MokoJoomBackup" -PLG_SYSTEM_MOKOBACKUP_DESCRIPTION="Automatic cleanup of expired backup archives and scheduled backup triggers." diff --git a/source/packages/plg_system_mokojoombackup/language/en-GB/plg_system_mokojoombackup.ini b/source/packages/plg_system_mokojoombackup/language/en-GB/plg_system_mokojoombackup.ini new file mode 100644 index 0000000..246611b --- /dev/null +++ b/source/packages/plg_system_mokojoombackup/language/en-GB/plg_system_mokojoombackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — System Plugin language file (en-GB) +PLG_SYSTEM_MOKOJOOMBACKUP="System - MokoJoomBackup" +PLG_SYSTEM_MOKOJOOMBACKUP_DESCRIPTION="Automatic cleanup of expired backup archives and scheduled backup triggers." +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_AUTO_CLEANUP="Auto Cleanup" +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_AUTO_CLEANUP_DESC="Automatically remove old backup archives based on age and count limits." +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_MAX_AGE="Max Backup Age (days)" +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_MAX_AGE_DESC="Delete backup records older than this many days." +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_MAX_BACKUPS="Max Backup Count" +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_MAX_BACKUPS_DESC="Keep at most this many completed backup records." diff --git a/source/packages/plg_system_mokojoombackup/language/en-GB/plg_system_mokojoombackup.sys.ini b/source/packages/plg_system_mokojoombackup/language/en-GB/plg_system_mokojoombackup.sys.ini new file mode 100644 index 0000000..1fce9d3 --- /dev/null +++ b/source/packages/plg_system_mokojoombackup/language/en-GB/plg_system_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — System Plugin system language file (en-GB) +PLG_SYSTEM_MOKOJOOMBACKUP="System - MokoJoomBackup" +PLG_SYSTEM_MOKOJOOMBACKUP_DESCRIPTION="Automatic cleanup of expired backup archives and scheduled backup triggers." diff --git a/source/packages/plg_system_mokojoombackup/language/en-US/plg_system_mokojoombackup.ini b/source/packages/plg_system_mokojoombackup/language/en-US/plg_system_mokojoombackup.ini new file mode 100644 index 0000000..8d8e47f --- /dev/null +++ b/source/packages/plg_system_mokojoombackup/language/en-US/plg_system_mokojoombackup.ini @@ -0,0 +1,9 @@ +; MokoJoomBackup — System Plugin language file (en-US) +PLG_SYSTEM_MOKOJOOMBACKUP="System - MokoJoomBackup" +PLG_SYSTEM_MOKOJOOMBACKUP_DESCRIPTION="Automatic cleanup of expired backup archives and scheduled backup triggers." +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_AUTO_CLEANUP="Auto Cleanup" +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_AUTO_CLEANUP_DESC="Automatically remove old backup archives based on age and count limits." +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_MAX_AGE="Max Backup Age (days)" +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_MAX_AGE_DESC="Delete backup records older than this many days." +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_MAX_BACKUPS="Max Backup Count" +PLG_SYSTEM_MOKOJOOMBACKUP_FIELD_MAX_BACKUPS_DESC="Keep at most this many completed backup records." diff --git a/source/packages/plg_system_mokojoombackup/language/en-US/plg_system_mokojoombackup.sys.ini b/source/packages/plg_system_mokojoombackup/language/en-US/plg_system_mokojoombackup.sys.ini new file mode 100644 index 0000000..9c4fd2e --- /dev/null +++ b/source/packages/plg_system_mokojoombackup/language/en-US/plg_system_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — System Plugin system language file (en-US) +PLG_SYSTEM_MOKOJOOMBACKUP="System - MokoJoomBackup" +PLG_SYSTEM_MOKOJOOMBACKUP_DESCRIPTION="Automatic cleanup of expired backup archives and scheduled backup triggers." diff --git a/source/packages/plg_actionlog_mokobackup/mokobackup.php b/source/packages/plg_system_mokojoombackup/mokojoombackup.php similarity index 86% rename from source/packages/plg_actionlog_mokobackup/mokobackup.php rename to source/packages/plg_system_mokojoombackup/mokojoombackup.php index 2a4226a..b53cca7 100644 --- a/source/packages/plg_actionlog_mokobackup/mokobackup.php +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_actionlog_mokobackup + * @subpackage plg_system_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE diff --git a/source/packages/plg_system_mokobackup/mokobackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml similarity index 60% rename from source/packages/plg_system_mokobackup/mokobackup.xml rename to source/packages/plg_system_mokojoombackup/mokojoombackup.xml index 8d422e9..45c64e8 100644 --- a/source/packages/plg_system_mokobackup/mokobackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -1,33 +1,33 @@ - plg_system_mokobackup - 01.01.09-dev + plg_system_mokojoombackup + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_SYSTEM_MOKOBACKUP_DESCRIPTION + PLG_SYSTEM_MOKOJOOMBACKUP_DESCRIPTION - Joomla\Plugin\System\MokoBackup + Joomla\Plugin\System\MokoJoomBackup - mokobackup.php + mokojoombackup.php services src - language/en-GB/plg_system_mokobackup.ini - language/en-GB/plg_system_mokobackup.sys.ini + language/en-GB/plg_system_mokojoombackup.ini + language/en-GB/plg_system_mokojoombackup.sys.ini @@ -36,8 +36,8 @@ @@ -47,8 +47,8 @@ * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -16,7 +16,7 @@ use Joomla\CMS\Plugin\PluginHelper; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; -use Joomla\Plugin\Console\MokoBackup\Extension\MokoBackupConsole; +use Joomla\Plugin\System\MokoJoomBackup\Extension\MokoJoomBackup; return new class () implements ServiceProviderInterface { public function register(Container $container): void @@ -24,9 +24,9 @@ return new class () implements ServiceProviderInterface { $container->set( PluginInterface::class, function (Container $container) { - $plugin = new MokoBackupConsole( + $plugin = new MokoJoomBackup( $container->get(DispatcherInterface::class), - (array) PluginHelper::getPlugin('console', 'mokobackup') + (array) PluginHelper::getPlugin('system', 'mokojoombackup') ); $plugin->setApplication(Factory::getApplication()); diff --git a/source/packages/plg_system_mokobackup/src/Extension/MokoBackup.php b/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/Extension/MokoBackup.php similarity index 83% rename from source/packages/plg_system_mokobackup/src/Extension/MokoBackup.php rename to source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/Extension/MokoBackup.php index c166862..f7d342f 100644 --- a/source/packages/plg_system_mokobackup/src/Extension/MokoBackup.php +++ b/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/Extension/MokoBackup.php @@ -2,13 +2,13 @@ /** * @package MokoJoomBackup - * @subpackage plg_system_mokobackup + * @subpackage plg_system_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ -namespace Joomla\Plugin\System\MokoBackup\Extension; +namespace Joomla\Plugin\System\MokoJoomBackup\Extension; defined('_JEXEC') or die; @@ -17,7 +17,7 @@ use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Event\Event; use Joomla\Event\SubscriberInterface; -final class MokoBackup extends CMSPlugin implements SubscriberInterface +final class MokoJoomBackup extends CMSPlugin implements SubscriberInterface { protected $autoloadLanguage = true; @@ -46,13 +46,13 @@ final class MokoBackup extends CMSPlugin implements SubscriberInterface // Throttle: only check once per hour via session flag $session = Factory::getSession(); - $lastCheck = $session->get('mokobackup.last_cleanup', 0); + $lastCheck = $session->get('mokojoombackup.last_cleanup', 0); if (time() - $lastCheck < 3600) { return; } - $session->set('mokobackup.last_cleanup', time()); + $session->set('mokojoombackup.last_cleanup', time()); $this->cleanupOldBackups(); } @@ -70,7 +70,7 @@ final class MokoBackup extends CMSPlugin implements SubscriberInterface $cutoff = date('Y-m-d H:i:s', strtotime("-{$maxAge} days")); $query = $db->getQuery(true) ->select('id, absolute_path') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('backupstart') . ' < ' . $db->quote($cutoff)) ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); $db->setQuery($query); @@ -83,7 +83,7 @@ final class MokoBackup extends CMSPlugin implements SubscriberInterface $db->setQuery( $db->getQuery(true) - ->delete($db->quoteName('#__mokobackup_records')) + ->delete($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('id') . ' = ' . (int) $record->id) ); $db->execute(); @@ -92,7 +92,7 @@ final class MokoBackup extends CMSPlugin implements SubscriberInterface // Enforce max backups count (keep newest) $query = $db->getQuery(true) ->select('COUNT(*)') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); $db->setQuery($query); $totalCount = (int) $db->loadResult(); @@ -101,7 +101,7 @@ final class MokoBackup extends CMSPlugin implements SubscriberInterface $excess = $totalCount - $maxBackups; $query = $db->getQuery(true) ->select('id, absolute_path') - ->from($db->quoteName('#__mokobackup_records')) + ->from($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('status') . ' = ' . $db->quote('complete')) ->order($db->quoteName('backupstart') . ' ASC'); $db->setQuery($query, 0, $excess); @@ -114,7 +114,7 @@ final class MokoBackup extends CMSPlugin implements SubscriberInterface $db->setQuery( $db->getQuery(true) - ->delete($db->quoteName('#__mokobackup_records')) + ->delete($db->quoteName('#__mokojoombackup_records')) ->where($db->quoteName('id') . ' = ' . (int) $record->id) ); $db->execute(); diff --git a/source/packages/plg_system_mokobackup/src/Extension/index.html b/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/plg_system_mokobackup/src/Extension/index.html rename to source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/Extension/index.html diff --git a/source/packages/plg_system_mokobackup/src/index.html b/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/index.html similarity index 100% rename from source/packages/plg_system_mokobackup/src/index.html rename to source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/index.html diff --git a/source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini b/source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini deleted file mode 100644 index 0b8dfb0..0000000 --- a/source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini +++ /dev/null @@ -1,12 +0,0 @@ -; MokoJoomBackup — Task Plugin language file (en-GB) -PLG_TASK_MOKOBACKUP="Task - MokoJoomBackup" -PLG_TASK_MOKOBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Allows running backup profiles on a schedule via Joomla's Scheduled Tasks system." - -; Task type -PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE_TITLE="MokoJoomBackup: Run Backup Profile" -PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE_DESC="Run a MokoJoomBackup backup using the selected profile. Create multiple tasks with different profiles for different backup schedules (e.g. daily full backup, hourly database-only backup)." - -; Task form fields -PLG_TASK_MOKOBACKUP_FIELD_PROFILE="Backup Profile" -PLG_TASK_MOKOBACKUP_FIELD_PROFILE_DESC="Select which backup profile to run. Each profile defines backup type (full/database/files), exclusion filters, and storage settings." -PLG_TASK_MOKOBACKUP_SELECT_PROFILE="- Select Profile -" diff --git a/source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini b/source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini deleted file mode 100644 index f75bb7e..0000000 --- a/source/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Task Plugin system language file (en-GB) -PLG_TASK_MOKOBACKUP="Task - MokoJoomBackup" -PLG_TASK_MOKOBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Run backup profiles on a schedule via Joomla's Scheduled Tasks." diff --git a/source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini b/source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini deleted file mode 100644 index 02d0939..0000000 --- a/source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini +++ /dev/null @@ -1,8 +0,0 @@ -; MokoJoomBackup — Task Plugin language file (en-US) -PLG_TASK_MOKOBACKUP="Task - MokoJoomBackup" -PLG_TASK_MOKOBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Allows running backup profiles on a schedule via Joomla's Scheduled Tasks system." -PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE_TITLE="MokoJoomBackup: Run Backup Profile" -PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE_DESC="Run a MokoJoomBackup backup using the selected profile. Create multiple tasks with different profiles for different backup schedules." -PLG_TASK_MOKOBACKUP_FIELD_PROFILE="Backup Profile" -PLG_TASK_MOKOBACKUP_FIELD_PROFILE_DESC="Select which backup profile to run." -PLG_TASK_MOKOBACKUP_SELECT_PROFILE="- Select Profile -" diff --git a/source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini b/source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini deleted file mode 100644 index c2ba0a8..0000000 --- a/source/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — Task Plugin system language file (en-US) -PLG_TASK_MOKOBACKUP="Task - MokoJoomBackup" -PLG_TASK_MOKOBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Run backup profiles on a schedule via Joomla's Scheduled Tasks." diff --git a/source/packages/plg_task_mokobackup/mokobackup.php b/source/packages/plg_task_mokobackup/mokobackup.php deleted file mode 100644 index 30fa3ac..0000000 --- a/source/packages/plg_task_mokobackup/mokobackup.php +++ /dev/null @@ -1,11 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - */ - -defined('_JEXEC') or die; diff --git a/source/packages/plg_task_mokobackup/services/provider.php b/source/packages/plg_task_mokobackup/services/provider.php deleted file mode 100644 index 00b1ac0..0000000 --- a/source/packages/plg_task_mokobackup/services/provider.php +++ /dev/null @@ -1,37 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - */ - -defined('_JEXEC') or die; - -use Joomla\CMS\Extension\PluginInterface; -use Joomla\CMS\Factory; -use Joomla\CMS\Plugin\PluginHelper; -use Joomla\DI\Container; -use Joomla\DI\ServiceProviderInterface; -use Joomla\Event\DispatcherInterface; -use Joomla\Plugin\Task\MokoBackup\Extension\MokoBackupTask; - -return new class () implements ServiceProviderInterface { - public function register(Container $container): void - { - $container->set( - PluginInterface::class, - function (Container $container) { - $plugin = new MokoBackupTask( - $container->get(DispatcherInterface::class), - (array) PluginHelper::getPlugin('task', 'mokobackup') - ); - $plugin->setApplication(Factory::getApplication()); - - return $plugin; - } - ); - } -}; diff --git a/source/packages/plg_task_mokojoombackup/language/en-GB/plg_task_mokojoombackup.ini b/source/packages/plg_task_mokojoombackup/language/en-GB/plg_task_mokojoombackup.ini new file mode 100644 index 0000000..ef0115d --- /dev/null +++ b/source/packages/plg_task_mokojoombackup/language/en-GB/plg_task_mokojoombackup.ini @@ -0,0 +1,12 @@ +; MokoJoomBackup — Task Plugin language file (en-GB) +PLG_TASK_MOKOJOOMBACKUP="Task - MokoJoomBackup" +PLG_TASK_MOKOJOOMBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Allows running backup profiles on a schedule via Joomla's Scheduled Tasks system." + +; Task type +PLG_TASK_MOKOJOOMBACKUP_TASK_RUN_PROFILE_TITLE="MokoJoomBackup: Run Backup Profile" +PLG_TASK_MOKOJOOMBACKUP_TASK_RUN_PROFILE_DESC="Run a MokoJoomBackup backup using the selected profile. Create multiple tasks with different profiles for different backup schedules (e.g. daily full backup, hourly database-only backup)." + +; Task form fields +PLG_TASK_MOKOJOOMBACKUP_FIELD_PROFILE="Backup Profile" +PLG_TASK_MOKOJOOMBACKUP_FIELD_PROFILE_DESC="Select which backup profile to run. Each profile defines backup type (full/database/files), exclusion filters, and storage settings." +PLG_TASK_MOKOJOOMBACKUP_SELECT_PROFILE="- Select Profile -" diff --git a/source/packages/plg_task_mokojoombackup/language/en-GB/plg_task_mokojoombackup.sys.ini b/source/packages/plg_task_mokojoombackup/language/en-GB/plg_task_mokojoombackup.sys.ini new file mode 100644 index 0000000..cc26e23 --- /dev/null +++ b/source/packages/plg_task_mokojoombackup/language/en-GB/plg_task_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Task Plugin system language file (en-GB) +PLG_TASK_MOKOJOOMBACKUP="Task - MokoJoomBackup" +PLG_TASK_MOKOJOOMBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Run backup profiles on a schedule via Joomla's Scheduled Tasks." diff --git a/source/packages/plg_task_mokojoombackup/language/en-US/plg_task_mokojoombackup.ini b/source/packages/plg_task_mokojoombackup/language/en-US/plg_task_mokojoombackup.ini new file mode 100644 index 0000000..7deadad --- /dev/null +++ b/source/packages/plg_task_mokojoombackup/language/en-US/plg_task_mokojoombackup.ini @@ -0,0 +1,8 @@ +; MokoJoomBackup — Task Plugin language file (en-US) +PLG_TASK_MOKOJOOMBACKUP="Task - MokoJoomBackup" +PLG_TASK_MOKOJOOMBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Allows running backup profiles on a schedule via Joomla's Scheduled Tasks system." +PLG_TASK_MOKOJOOMBACKUP_TASK_RUN_PROFILE_TITLE="MokoJoomBackup: Run Backup Profile" +PLG_TASK_MOKOJOOMBACKUP_TASK_RUN_PROFILE_DESC="Run a MokoJoomBackup backup using the selected profile. Create multiple tasks with different profiles for different backup schedules." +PLG_TASK_MOKOJOOMBACKUP_FIELD_PROFILE="Backup Profile" +PLG_TASK_MOKOJOOMBACKUP_FIELD_PROFILE_DESC="Select which backup profile to run." +PLG_TASK_MOKOJOOMBACKUP_SELECT_PROFILE="- Select Profile -" diff --git a/source/packages/plg_task_mokojoombackup/language/en-US/plg_task_mokojoombackup.sys.ini b/source/packages/plg_task_mokojoombackup/language/en-US/plg_task_mokojoombackup.sys.ini new file mode 100644 index 0000000..4850f90 --- /dev/null +++ b/source/packages/plg_task_mokojoombackup/language/en-US/plg_task_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Task Plugin system language file (en-US) +PLG_TASK_MOKOJOOMBACKUP="Task - MokoJoomBackup" +PLG_TASK_MOKOJOOMBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Run backup profiles on a schedule via Joomla's Scheduled Tasks." diff --git a/source/packages/plg_system_mokobackup/mokobackup.php b/source/packages/plg_task_mokojoombackup/mokojoombackup.php similarity index 87% rename from source/packages/plg_system_mokobackup/mokobackup.php rename to source/packages/plg_task_mokojoombackup/mokojoombackup.php index 9cabb97..4cc8120 100644 --- a/source/packages/plg_system_mokobackup/mokobackup.php +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_system_mokobackup + * @subpackage plg_task_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE diff --git a/source/packages/plg_task_mokobackup/mokobackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml similarity index 63% rename from source/packages/plg_task_mokobackup/mokobackup.xml rename to source/packages/plg_task_mokojoombackup/mokojoombackup.xml index 523cefd..b65b02d 100644 --- a/source/packages/plg_task_mokobackup/mokobackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -1,33 +1,33 @@ - plg_task_mokobackup - 01.01.09-dev + plg_task_mokojoombackup + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_TASK_MOKOBACKUP_DESCRIPTION + PLG_TASK_MOKOJOOMBACKUP_DESCRIPTION - Joomla\Plugin\Task\MokoBackup + Joomla\Plugin\Task\MokoJoomBackup - mokobackup.php + mokojoombackup.php services src forms - language/en-GB/plg_task_mokobackup.ini - language/en-GB/plg_task_mokobackup.sys.ini + language/en-GB/plg_task_mokojoombackup.ini + language/en-GB/plg_task_mokojoombackup.sys.ini diff --git a/source/packages/plg_task_mokobackup/forms/index.html b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/forms/index.html similarity index 100% rename from source/packages/plg_task_mokobackup/forms/index.html rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/forms/index.html diff --git a/source/packages/plg_task_mokobackup/forms/run_profile.xml b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/forms/run_profile.xml similarity index 55% rename from source/packages/plg_task_mokobackup/forms/run_profile.xml rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/forms/run_profile.xml index ad07f85..57ee2f4 100644 --- a/source/packages/plg_task_mokobackup/forms/run_profile.xml +++ b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/forms/run_profile.xml @@ -9,13 +9,13 @@ - + diff --git a/source/packages/plg_task_mokobackup/index.html b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/index.html similarity index 100% rename from source/packages/plg_task_mokobackup/index.html rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/index.html diff --git a/source/packages/plg_task_mokobackup/language/en-GB/index.html b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/en-GB/index.html similarity index 100% rename from source/packages/plg_task_mokobackup/language/en-GB/index.html rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/en-GB/index.html diff --git a/source/packages/plg_task_mokobackup/language/en-US/index.html b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/en-US/index.html similarity index 100% rename from source/packages/plg_task_mokobackup/language/en-US/index.html rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/en-US/index.html diff --git a/source/packages/plg_task_mokobackup/language/index.html b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/index.html similarity index 100% rename from source/packages/plg_task_mokobackup/language/index.html rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/index.html diff --git a/source/packages/plg_task_mokobackup/services/index.html b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/services/index.html similarity index 100% rename from source/packages/plg_task_mokobackup/services/index.html rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/services/index.html diff --git a/source/packages/plg_system_mokobackup/services/provider.php b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/services/provider.php similarity index 80% rename from source/packages/plg_system_mokobackup/services/provider.php rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/services/provider.php index f0bf9f9..f48dbc4 100644 --- a/source/packages/plg_system_mokobackup/services/provider.php +++ b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/services/provider.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_system_mokobackup + * @subpackage plg_task_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -16,7 +16,7 @@ use Joomla\CMS\Plugin\PluginHelper; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; -use Joomla\Plugin\System\MokoBackup\Extension\MokoBackup; +use Joomla\Plugin\Task\MokoJoomBackup\Extension\MokoJoomBackupTask; return new class () implements ServiceProviderInterface { public function register(Container $container): void @@ -24,9 +24,9 @@ return new class () implements ServiceProviderInterface { $container->set( PluginInterface::class, function (Container $container) { - $plugin = new MokoBackup( + $plugin = new MokoJoomBackupTask( $container->get(DispatcherInterface::class), - (array) PluginHelper::getPlugin('system', 'mokobackup') + (array) PluginHelper::getPlugin('task', 'mokojoombackup') ); $plugin->setApplication(Factory::getApplication()); diff --git a/source/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/Extension/MokoBackupTask.php similarity index 81% rename from source/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/Extension/MokoBackupTask.php index 723642b..4023778 100644 --- a/source/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php +++ b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/Extension/MokoBackupTask.php @@ -2,7 +2,7 @@ /** * @package MokoJoomBackup - * @subpackage plg_task_mokobackup + * @subpackage plg_task_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -14,7 +14,7 @@ * each pointing to a different backup profile — just like Akeeba Backup Pro. */ -namespace Joomla\Plugin\Task\MokoBackup\Extension; +namespace Joomla\Plugin\Task\MokoJoomBackup\Extension; defined('_JEXEC') or die; @@ -25,7 +25,7 @@ use Joomla\Component\Scheduler\Administrator\Traits\TaskPluginTrait; use Joomla\Event\Event; use Joomla\Event\SubscriberInterface; -final class MokoBackupTask extends CMSPlugin implements SubscriberInterface +final class MokoJoomBackupTask extends CMSPlugin implements SubscriberInterface { use TaskPluginTrait; @@ -38,8 +38,8 @@ final class MokoBackupTask extends CMSPlugin implements SubscriberInterface * so different backup profiles run on different schedules. */ protected const TASKS_MAP = [ - 'mokobackup.run_profile' => [ - 'langConstPrefix' => 'PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE', + 'mokojoombackup.run_profile' => [ + 'langConstPrefix' => 'PLG_TASK_MOKOJOOMBACKUP_TASK_RUN_PROFILE', 'method' => 'runBackupProfile', 'form' => 'run_profile', ], @@ -67,7 +67,7 @@ final class MokoBackupTask extends CMSPlugin implements SubscriberInterface $profileId = (int) ($params->profile_id ?? 1); // Load the backup engine from the component - $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokobackup/src/Engine/BackupEngine.php'; + $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokojoombackup/src/Engine/BackupEngine.php'; if (!file_exists($engineFile)) { $this->logTask('MokoJoomBackup component not installed — cannot run backup.'); @@ -76,11 +76,11 @@ final class MokoBackupTask extends CMSPlugin implements SubscriberInterface } // The autoloader should handle this via namespace, but ensure class is available - if (!class_exists('\\Joomla\\Component\\MokoBackup\\Administrator\\Engine\\BackupEngine')) { + if (!class_exists('\\Joomla\\Component\\MokoJoomBackup\\Administrator\\Engine\\BackupEngine')) { require_once $engineFile; } - $engine = new \Joomla\Component\MokoBackup\Administrator\Engine\BackupEngine(); + $engine = new \Joomla\Component\MokoJoomBackup\Administrator\Engine\BackupEngine(); $result = $engine->run($profileId, 'Scheduled task backup', 'scheduled'); if ($result['success']) { diff --git a/source/packages/plg_task_mokobackup/src/Extension/index.html b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/plg_task_mokobackup/src/Extension/index.html rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/Extension/index.html diff --git a/source/packages/plg_task_mokobackup/src/index.html b/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/index.html similarity index 100% rename from source/packages/plg_task_mokobackup/src/index.html rename to source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/index.html diff --git a/source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.ini b/source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.ini deleted file mode 100644 index 7038eca..0000000 --- a/source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — WebServices Plugin language file (en-GB) -PLG_WEBSERVICES_MOKOBACKUP="Web Services - MokoJoomBackup" -PLG_WEBSERVICES_MOKOBACKUP_DESCRIPTION="REST API for remote backup management. Provides endpoints to start, list, download, and delete backups." diff --git a/source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.sys.ini b/source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.sys.ini deleted file mode 100644 index 3b2da03..0000000 --- a/source/packages/plg_webservices_mokobackup/language/en-GB/plg_webservices_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — WebServices Plugin system language file (en-GB) -PLG_WEBSERVICES_MOKOBACKUP="Web Services - MokoJoomBackup" -PLG_WEBSERVICES_MOKOBACKUP_DESCRIPTION="REST API for remote backup management." diff --git a/source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.ini b/source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.ini deleted file mode 100644 index e015f44..0000000 --- a/source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — WebServices Plugin language file (en-US) -PLG_WEBSERVICES_MOKOBACKUP="Web Services - MokoJoomBackup" -PLG_WEBSERVICES_MOKOBACKUP_DESCRIPTION="REST API for remote backup management. Provides endpoints to start, list, download, and delete backups." diff --git a/source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.sys.ini b/source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.sys.ini deleted file mode 100644 index c728408..0000000 --- a/source/packages/plg_webservices_mokobackup/language/en-US/plg_webservices_mokobackup.sys.ini +++ /dev/null @@ -1,3 +0,0 @@ -; MokoJoomBackup — WebServices Plugin system language file (en-US) -PLG_WEBSERVICES_MOKOBACKUP="Web Services - MokoJoomBackup" -PLG_WEBSERVICES_MOKOBACKUP_DESCRIPTION="REST API for remote backup management." diff --git a/source/packages/plg_webservices_mokobackup/mokobackup.php b/source/packages/plg_webservices_mokobackup/mokobackup.php deleted file mode 100644 index 5a84f43..0000000 --- a/source/packages/plg_webservices_mokobackup/mokobackup.php +++ /dev/null @@ -1,11 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - */ - -defined('_JEXEC') or die; diff --git a/source/packages/plg_webservices_mokojoombackup/language/en-GB/plg_webservices_mokojoombackup.ini b/source/packages/plg_webservices_mokojoombackup/language/en-GB/plg_webservices_mokojoombackup.ini new file mode 100644 index 0000000..b712a7f --- /dev/null +++ b/source/packages/plg_webservices_mokojoombackup/language/en-GB/plg_webservices_mokojoombackup.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — WebServices Plugin language file (en-GB) +PLG_WEBSERVICES_MOKOJOOMBACKUP="Web Services - MokoJoomBackup" +PLG_WEBSERVICES_MOKOJOOMBACKUP_DESCRIPTION="REST API for remote backup management. Provides endpoints to start, list, download, and delete backups." diff --git a/source/packages/plg_webservices_mokojoombackup/language/en-GB/plg_webservices_mokojoombackup.sys.ini b/source/packages/plg_webservices_mokojoombackup/language/en-GB/plg_webservices_mokojoombackup.sys.ini new file mode 100644 index 0000000..575a5a7 --- /dev/null +++ b/source/packages/plg_webservices_mokojoombackup/language/en-GB/plg_webservices_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — WebServices Plugin system language file (en-GB) +PLG_WEBSERVICES_MOKOJOOMBACKUP="Web Services - MokoJoomBackup" +PLG_WEBSERVICES_MOKOJOOMBACKUP_DESCRIPTION="REST API for remote backup management." diff --git a/source/packages/plg_webservices_mokojoombackup/language/en-US/plg_webservices_mokojoombackup.ini b/source/packages/plg_webservices_mokojoombackup/language/en-US/plg_webservices_mokojoombackup.ini new file mode 100644 index 0000000..6fbd792 --- /dev/null +++ b/source/packages/plg_webservices_mokojoombackup/language/en-US/plg_webservices_mokojoombackup.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — WebServices Plugin language file (en-US) +PLG_WEBSERVICES_MOKOJOOMBACKUP="Web Services - MokoJoomBackup" +PLG_WEBSERVICES_MOKOJOOMBACKUP_DESCRIPTION="REST API for remote backup management. Provides endpoints to start, list, download, and delete backups." diff --git a/source/packages/plg_webservices_mokojoombackup/language/en-US/plg_webservices_mokojoombackup.sys.ini b/source/packages/plg_webservices_mokojoombackup/language/en-US/plg_webservices_mokojoombackup.sys.ini new file mode 100644 index 0000000..0d83f11 --- /dev/null +++ b/source/packages/plg_webservices_mokojoombackup/language/en-US/plg_webservices_mokojoombackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — WebServices Plugin system language file (en-US) +PLG_WEBSERVICES_MOKOJOOMBACKUP="Web Services - MokoJoomBackup" +PLG_WEBSERVICES_MOKOJOOMBACKUP_DESCRIPTION="REST API for remote backup management." diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.php b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.php new file mode 100644 index 0000000..d3162d7 --- /dev/null +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.php @@ -0,0 +1,11 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; diff --git a/source/packages/plg_webservices_mokobackup/mokobackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml similarity index 59% rename from source/packages/plg_webservices_mokobackup/mokobackup.xml rename to source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index 0b7e1e0..f63f135 100644 --- a/source/packages/plg_webservices_mokobackup/mokobackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -1,32 +1,32 @@ - plg_webservices_mokobackup - 01.01.09-dev + plg_webservices_mokojoombackup + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_WEBSERVICES_MOKOBACKUP_DESCRIPTION + PLG_WEBSERVICES_MOKOJOOMBACKUP_DESCRIPTION - Joomla\Plugin\WebServices\MokoBackup + Joomla\Plugin\WebServices\MokoJoomBackup - mokobackup.php + mokojoombackup.php services src - language/en-GB/plg_webservices_mokobackup.ini - language/en-GB/plg_webservices_mokobackup.sys.ini + language/en-GB/plg_webservices_mokojoombackup.ini + language/en-GB/plg_webservices_mokojoombackup.sys.ini diff --git a/source/packages/plg_webservices_mokobackup/index.html b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/index.html similarity index 100% rename from source/packages/plg_webservices_mokobackup/index.html rename to source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/index.html diff --git a/source/packages/plg_webservices_mokobackup/language/en-GB/index.html b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/en-GB/index.html similarity index 100% rename from source/packages/plg_webservices_mokobackup/language/en-GB/index.html rename to source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/en-GB/index.html diff --git a/source/packages/plg_webservices_mokobackup/language/en-US/index.html b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/en-US/index.html similarity index 100% rename from source/packages/plg_webservices_mokobackup/language/en-US/index.html rename to source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/en-US/index.html diff --git a/source/packages/plg_webservices_mokobackup/language/index.html b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/index.html similarity index 100% rename from source/packages/plg_webservices_mokobackup/language/index.html rename to source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/index.html diff --git a/source/packages/plg_webservices_mokobackup/services/index.html b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/index.html similarity index 100% rename from source/packages/plg_webservices_mokobackup/services/index.html rename to source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/index.html diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/provider.php b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/provider.php new file mode 100644 index 0000000..b96697f --- /dev/null +++ b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/provider.php @@ -0,0 +1,37 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\WebServices\MokoJoomBackup\Extension\MokoJoomBackupWebServices; + +return new class () implements ServiceProviderInterface { + public function register(Container $container): void + { + $container->set( + PluginInterface::class, + function (Container $container) { + $plugin = new MokoJoomBackupWebServices( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('webservices', 'mokojoombackup') + ); + $plugin->setApplication(Factory::getApplication()); + + return $plugin; + } + ); + } +}; diff --git a/source/packages/plg_webservices_mokobackup/src/Extension/MokoBackupWebServices.php b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/Extension/MokoBackupWebServices.php similarity index 60% rename from source/packages/plg_webservices_mokobackup/src/Extension/MokoBackupWebServices.php rename to source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/Extension/MokoBackupWebServices.php index b6ad328..8a67e20 100644 --- a/source/packages/plg_webservices_mokobackup/src/Extension/MokoBackupWebServices.php +++ b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/Extension/MokoBackupWebServices.php @@ -2,22 +2,22 @@ /** * @package MokoJoomBackup - * @subpackage plg_webservices_mokobackup + * @subpackage plg_webservices_mokojoombackup * @author Moko Consulting * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE * - * REST API endpoints — wire-compatible with the mcp_mokobackup MCP server. + * REST API endpoints — wire-compatible with the mcp_mokojoombackup MCP server. * * Akeeba-compatible routes: - * POST /api/index.php/v1/mokobackup/backup — Start backup - * GET /api/index.php/v1/mokobackup/backups — List records - * DELETE /api/index.php/v1/mokobackup/backup/:id — Delete record - * GET /api/index.php/v1/mokobackup/backup/:id/download — Download archive - * GET /api/index.php/v1/mokobackup/profiles — List profiles + * POST /api/index.php/v1/mokojoombackup/backup — Start backup + * GET /api/index.php/v1/mokojoombackup/backups — List records + * DELETE /api/index.php/v1/mokojoombackup/backup/:id — Delete record + * GET /api/index.php/v1/mokojoombackup/backup/:id/download — Download archive + * GET /api/index.php/v1/mokojoombackup/profiles — List profiles */ -namespace Joomla\Plugin\WebServices\MokoBackup\Extension; +namespace Joomla\Plugin\WebServices\MokoJoomBackup\Extension; defined('_JEXEC') or die; @@ -27,7 +27,7 @@ use Joomla\Event\Event; use Joomla\Event\SubscriberInterface; use Joomla\Router\Route; -final class MokoBackupWebServices extends CMSPlugin implements SubscriberInterface +final class MokoJoomBackupWebServices extends CMSPlugin implements SubscriberInterface { protected $autoloadLanguage = true; @@ -44,18 +44,18 @@ final class MokoBackupWebServices extends CMSPlugin implements SubscriberInterfa [$router] = array_values($event->getArguments()); $defaults = [ - 'component' => 'com_mokobackup', + 'component' => 'com_mokojoombackup', 'public' => false, ]; // Standard CRUD for backup records - $router->createCRUDRoutes('v1/mokobackup/backups', 'backups', $defaults); + $router->createCRUDRoutes('v1/mokojoombackup/backups', 'backups', $defaults); // Start a backup (POST) $router->addRoute( new Route( ['POST'], - 'v1/mokobackup/backup', + 'v1/mokojoombackup/backup', 'backups.backup', [], $defaults @@ -66,7 +66,7 @@ final class MokoBackupWebServices extends CMSPlugin implements SubscriberInterfa $router->addRoute( new Route( ['DELETE'], - 'v1/mokobackup/backup/:id', + 'v1/mokojoombackup/backup/:id', 'backups.delete', ['id' => '(\d+)'], $defaults @@ -77,7 +77,7 @@ final class MokoBackupWebServices extends CMSPlugin implements SubscriberInterfa $router->addRoute( new Route( ['GET'], - 'v1/mokobackup/backup/:id/download', + 'v1/mokojoombackup/backup/:id/download', 'backups.download', ['id' => '(\d+)'], $defaults @@ -88,7 +88,7 @@ final class MokoBackupWebServices extends CMSPlugin implements SubscriberInterfa $router->addRoute( new Route( ['GET'], - 'v1/mokobackup/profiles', + 'v1/mokojoombackup/profiles', 'backups.profiles', [], $defaults diff --git a/source/packages/plg_webservices_mokobackup/src/Extension/index.html b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/plg_webservices_mokobackup/src/Extension/index.html rename to source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/Extension/index.html diff --git a/source/packages/plg_webservices_mokobackup/src/index.html b/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/index.html similarity index 100% rename from source/packages/plg_webservices_mokobackup/src/index.html rename to source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/index.html diff --git a/source/pkg_mokobackup.xml b/source/pkg_mokojoombackup.xml similarity index 53% rename from source/pkg_mokobackup.xml rename to source/pkg_mokojoombackup.xml index ce73908..b0e12be 100644 --- a/source/pkg_mokobackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -7,31 +7,31 @@ --> Package - MokoJoomBackup - mokobackup - 01.01.09-dev + mokojoombackup + 01.01.07-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PKG_MOKOBACKUP_DESCRIPTION + PKG_MOKOJOOMBACKUP_DESCRIPTION script.php - com_mokobackup.zip - plg_system_mokobackup.zip - plg_task_mokobackup.zip - plg_quickicon_mokobackup.zip - plg_webservices_mokobackup.zip - plg_console_mokobackup.zip - plg_content_mokobackup.zip - plg_actionlog_mokobackup.zip + com_mokojoombackup.zip + plg_system_mokojoombackup.zip + plg_task_mokojoombackup.zip + plg_quickicon_mokojoombackup.zip + plg_webservices_mokojoombackup.zip + plg_console_mokojoombackup.zip + plg_content_mokojoombackup.zip + plg_actionlog_mokojoombackup.zip - language/en-GB/pkg_mokobackup.sys.ini + language/en-GB/pkg_mokojoombackup.sys.ini diff --git a/source/script.php b/source/script.php index d970bcd..017b868 100644 --- a/source/script.php +++ b/source/script.php @@ -14,7 +14,7 @@ use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; -class Pkg_MokoBackupInstallerScript +class Pkg_MokoJoomBackupInstallerScript { /** * Minimum Joomla version required @@ -42,7 +42,7 @@ class Pkg_MokoBackupInstallerScript { if (version_compare(PHP_VERSION, $this->minimumPhp, '<')) { Factory::getApplication()->enqueueMessage( - Text::sprintf('PKG_MOKOBACKUP_PHP_VERSION_ERROR', $this->minimumPhp), + Text::sprintf('PKG_MOKOJOOMBACKUP_PHP_VERSION_ERROR', $this->minimumPhp), 'error' ); @@ -70,7 +70,7 @@ class Pkg_MokoBackupInstallerScript ->set($db->quoteName('enabled') . ' = 1') ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('system')) - ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + ->where($db->quoteName('element') . ' = ' . $db->quote('mokojoombackup')); $db->setQuery($query); $db->execute(); @@ -81,7 +81,7 @@ class Pkg_MokoBackupInstallerScript ->set($db->quoteName('enabled') . ' = 1') ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('quickicon')) - ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + ->where($db->quoteName('element') . ' = ' . $db->quote('mokojoombackup')); $db->setQuery($query); $db->execute(); @@ -92,7 +92,7 @@ class Pkg_MokoBackupInstallerScript ->set($db->quoteName('enabled') . ' = 1') ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('task')) - ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + ->where($db->quoteName('element') . ' = ' . $db->quote('mokojoombackup')); $db->setQuery($query); $db->execute(); @@ -103,7 +103,7 @@ class Pkg_MokoBackupInstallerScript ->set($db->quoteName('enabled') . ' = 1') ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('webservices')) - ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + ->where($db->quoteName('element') . ' = ' . $db->quote('mokojoombackup')); $db->setQuery($query); $db->execute(); @@ -114,7 +114,7 @@ class Pkg_MokoBackupInstallerScript ->set($db->quoteName('enabled') . ' = 1') ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('console')) - ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + ->where($db->quoteName('element') . ' = ' . $db->quote('mokojoombackup')); $db->setQuery($query); $db->execute(); @@ -125,7 +125,7 @@ class Pkg_MokoBackupInstallerScript ->set($db->quoteName('enabled') . ' = 1') ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('content')) - ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + ->where($db->quoteName('element') . ' = ' . $db->quote('mokojoombackup')); $db->setQuery($query); $db->execute(); @@ -136,13 +136,13 @@ class Pkg_MokoBackupInstallerScript ->set($db->quoteName('enabled') . ' = 1') ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('actionlog')) - ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + ->where($db->quoteName('element') . ' = ' . $db->quote('mokojoombackup')); $db->setQuery($query); $db->execute(); // Create default backup directory - $backupDir = JPATH_ADMINISTRATOR . '/components/com_mokobackup/backups'; + $backupDir = JPATH_ADMINISTRATOR . '/components/com_mokojoombackup/backups'; if (!is_dir($backupDir)) { mkdir($backupDir, 0755, true); @@ -181,7 +181,7 @@ class Pkg_MokoBackupInstallerScript $db->quoteName('#__extensions', 'e') . ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id') ) - ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokobackup')) + ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokojoombackup')) ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) ->setLimit(1); @@ -190,11 +190,11 @@ class Pkg_MokoBackupInstallerScript if ($updateSiteId > 0) { $editUrl = Route::_( - 'index.php?option=com_installer&view=updatesites&filter[search]=mokobackup' + 'index.php?option=com_installer&view=updatesites&filter[search]=mokojoombackup' ); Factory::getApplication()->enqueueMessage( - Text::sprintf('PKG_MOKOBACKUP_POSTINSTALL_UPDATE_SITE', $editUrl), + Text::sprintf('PKG_MOKOJOOMBACKUP_POSTINSTALL_UPDATE_SITE', $editUrl), 'info' ); } -- 2.52.0 From a10827294aaff24cf9b66208dc796c43476ef4e6 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 20:02:10 +0000 Subject: [PATCH 59/81] chore(version): pre-release bump to 01.01.10-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/auto-release.yml | 648 +++++++++--------- .mokogitea/workflows/issue-branch.yml | 2 +- .mokogitea/workflows/pre-release.yml | 486 ++++++------- README.md | 2 +- .../com_mokojoombackup/mokojoombackup.xml | 2 +- .../mokojoombackup.xml | 2 +- .../mokojoombackup.xml | 2 +- .../mokojoombackup.xml | 2 +- .../mokojoombackup.xml | 2 +- .../mokojoombackup.xml | 2 +- .../mokojoombackup.xml | 2 +- .../mokojoombackup.xml | 2 +- source/pkg_mokojoombackup.xml | 2 +- 14 files changed, 579 insertions(+), 579 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 1022eb7..a419469 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.09-dev + 01.01.10-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index ca40435..b657b98 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -1,324 +1,324 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform -# PATH: /templates/workflows/universal/auto-release.yml.template -# VERSION: 05.00.00 -# BRIEF: Universal build & release � detects platform from manifest.xml -# -# +========================================================================+ -# | UNIVERSAL BUILD & RELEASE PIPELINE | -# +========================================================================+ -# | | -# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | -# | | -# | Platform-specific: | -# | joomla: XML manifest, type-prefixed packages | -# | dolibarr: mod*.class.php, update.txt, dev version reset | -# | generic: README-only, no update stream | -# | | -# +========================================================================+ - -name: "Universal: Build & Release" - -on: - pull_request: - types: [opened, closed] - branches: - - main - workflow_dispatch: - inputs: - action: - description: 'Action to perform' - required: false - type: choice - default: release - options: - - release - - promote-rc - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -permissions: - contents: write - -jobs: - # ── PR Opened → Rename branch to RC and build RC release ───────────────────── - promote-rc: - name: Promote to RC - runs-on: release - if: >- - (github.event.action == 'opened' && github.event.pull_request.merged != true) || - (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 1 - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - run: | - if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then - echo Using pre-installed /opt/moko-platform - echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV - else - echo Falling back to fresh clone - if ! command -v composer > /dev/null 2>&1; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 - fi - rm -rf /tmp/moko-platform-api - CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git - git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV - fi - - - name: Rename branch to rc - run: | - php ${MOKO_CLI}/branch_rename.php \ - --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ - --pr "${{ github.event.pull_request.number }}" - - - name: Checkout rc and configure git - run: | - git fetch origin rc - git checkout rc - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - - name: Publish RC release - run: | - php ${MOKO_CLI}/release_publish.php \ - --path . --stability rc --bump minor --branch rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" - - - name: Summary - if: always() - run: | - echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY - - # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── - release: - name: Build & Release Pipeline - runs-on: release - if: >- - github.event.pull_request.merged == true || - (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 0 - - - name: Configure git for bot pushes - run: | - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - - name: Check for merge conflict markers - run: | - CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) - if [ -n "$CONFLICTS" ]; then - echo "::error::Merge conflict markers found — aborting release" - echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - exit 1 - fi - echo "No conflict markers found" - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' - run: | - if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then - echo Using pre-installed /opt/moko-platform - echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV - else - echo Falling back to fresh clone - if ! command -v composer > /dev/null 2>&1; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 - fi - rm -rf /tmp/moko-platform-api - CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git - git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV - fi - - - name: "Publish stable release" - run: | - php ${MOKO_CLI}/release_publish.php \ - --path . --stability stable --bump minor --branch main \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" - - - name: Update release notes from CHANGELOG.md - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - # Extract [Unreleased] section from changelog - if [ -f "CHANGELOG.md" ]; then - NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) - [ -z "$NOTES" ] && NOTES="Stable release" - else - NOTES="Stable release" - fi - - # Update release body via API - RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) - - if [ -n "$RELEASE_ID" ]; then - python3 -c " - import json, urllib.request - body = open('/dev/stdin').read() - payload = json.dumps({'body': body}).encode() - req = urllib.request.Request( - '${API_BASE}/releases/${RELEASE_ID}', - data=payload, method='PATCH', - headers={ - 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', - 'Content-Type': 'application/json' - }) - urllib.request.urlopen(req) - " <<< "$NOTES" - echo "Release notes updated from CHANGELOG.md" - fi - - # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - - name: "Step 9: Mirror release to GitHub" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_mirror.php \ - --version "$VERSION" --tag "$RELEASE_TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ - --branch main 2>&1 || true - echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY - - # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- - - name: "Step 10: Push main to GitHub mirror" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) - GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) - git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ - git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" - git fetch origin main --depth=1 - git push github origin/main:refs/heads/main --force 2>/dev/null \ - && echo "main branch pushed to GitHub mirror" \ - || echo "WARNING: GitHub mirror push failed" - - - name: "Step 11: Delete rc branch and recreate dev from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - # Delete rc branch (ephemeral — created by promote-rc) - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/rc" 2>/dev/null \ - && echo "Deleted rc branch" || echo "rc branch not found" - - # Delete dev branch - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" - - # Recreate dev from main (now includes version bump + changelog promotion) - curl -sf -X POST -H "Authorization: token ${TOKEN}" \ - -H "Content-Type: application/json" \ - "${API_BASE}/branches" \ - -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" - - echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY - - - name: "Step 12: Create version branch from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - BRANCH_NAME="version/${VERSION}" - MAIN_SHA=$(git rev-parse HEAD) - - # Delete old version branch if it exists (same version re-release) - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" - - # Create version/XX.YY.ZZ from main - curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" - - echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY - - - - # -- Dolibarr post-release: Reset dev version ----------------------------- - - name: "Post-release: Reset dev version" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/version_reset_dev.php \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ - --branch dev --path . 2>&1 || true - - # -- Summary -------------------------------------------------------------- - - name: Pipeline Summary - if: always() - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - PLATFORM="${{ steps.platform.outputs.platform }}" - if [ "${{ steps.version.outputs.skip }}" = "true" ]; then - echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY - echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY - elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then - echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY - echo "|------|--------|" >> $GITHUB_STEP_SUMMARY - echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY - fi +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform +# PATH: /templates/workflows/universal/auto-release.yml.template +# VERSION: 05.00.00 +# BRIEF: Universal build & release � detects platform from manifest.xml +# +# +========================================================================+ +# | UNIVERSAL BUILD & RELEASE PIPELINE | +# +========================================================================+ +# | | +# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | +# | | +# | Platform-specific: | +# | joomla: XML manifest, type-prefixed packages | +# | dolibarr: mod*.class.php, update.txt, dev version reset | +# | generic: README-only, no update stream | +# | | +# +========================================================================+ + +name: "Universal: Build & Release" + +on: + pull_request: + types: [opened, closed] + branches: + - main + workflow_dispatch: + inputs: + action: + description: 'Action to perform' + required: false + type: choice + default: release + options: + - release + - promote-rc + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} + GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} + +permissions: + contents: write + +jobs: + # ── PR Opened → Rename branch to RC and build RC release ───────────────────── + promote-rc: + name: Promote to RC + runs-on: release + if: >- + (github.event.action == 'opened' && github.event.pull_request.merged != true) || + (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 1 + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + fi + + - name: Rename branch to rc + run: | + php ${MOKO_CLI}/branch_rename.php \ + --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ + --pr "${{ github.event.pull_request.number }}" + + - name: Checkout rc and configure git + run: | + git fetch origin rc + git checkout rc + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + + - name: Publish RC release + run: | + php ${MOKO_CLI}/release_publish.php \ + --path . --stability rc --bump minor --branch rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" + + - name: Summary + if: always() + run: | + echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY + echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY + + # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── + release: + name: Build & Release Pipeline + runs-on: release + if: >- + github.event.pull_request.merged == true || + (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 0 + + - name: Configure git for bot pushes + run: | + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + + - name: Check for merge conflict markers + run: | + CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) + if [ -n "$CONFLICTS" ]; then + echo "::error::Merge conflict markers found — aborting release" + echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "No conflict markers found" + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' + run: | + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + fi + + - name: "Publish stable release" + run: | + php ${MOKO_CLI}/release_publish.php \ + --path . --stability stable --bump minor --branch main \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" + + - name: Update release notes from CHANGELOG.md + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Extract [Unreleased] section from changelog + if [ -f "CHANGELOG.md" ]; then + NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) + [ -z "$NOTES" ] && NOTES="Stable release" + else + NOTES="Stable release" + fi + + # Update release body via API + RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + + if [ -n "$RELEASE_ID" ]; then + python3 -c " + import json, urllib.request + body = open('/dev/stdin').read() + payload = json.dumps({'body': body}).encode() + req = urllib.request.Request( + '${API_BASE}/releases/${RELEASE_ID}', + data=payload, method='PATCH', + headers={ + 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', + 'Content-Type': 'application/json' + }) + urllib.request.urlopen(req) + " <<< "$NOTES" + echo "Release notes updated from CHANGELOG.md" + fi + + # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- + - name: "Step 9: Mirror release to GitHub" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + RELEASE_TAG="${{ steps.version.outputs.release_tag }}" + GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_mirror.php \ + --version "$VERSION" --tag "$RELEASE_TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ + --branch main 2>&1 || true + echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY + + # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- + - name: "Step 10: Push main to GitHub mirror" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" + GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) + GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) + git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ + git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" + git fetch origin main --depth=1 + git push github origin/main:refs/heads/main --force 2>/dev/null \ + && echo "main branch pushed to GitHub mirror" \ + || echo "WARNING: GitHub mirror push failed" + + - name: "Step 11: Delete rc branch and recreate dev from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + # Delete rc branch (ephemeral — created by promote-rc) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/rc" 2>/dev/null \ + && echo "Deleted rc branch" || echo "rc branch not found" + + # Delete dev branch + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" + + # Recreate dev from main (now includes version bump + changelog promotion) + curl -sf -X POST -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${API_BASE}/branches" \ + -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" + + echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY + + - name: "Step 12: Create version branch from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + BRANCH_NAME="version/${VERSION}" + MAIN_SHA=$(git rev-parse HEAD) + + # Delete old version branch if it exists (same version re-release) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" + + # Create version/XX.YY.ZZ from main + curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" + + echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY + + + + # -- Dolibarr post-release: Reset dev version ----------------------------- + - name: "Post-release: Reset dev version" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/version_reset_dev.php \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ + --branch dev --path . 2>&1 || true + + # -- Summary -------------------------------------------------------------- + - name: Pipeline Summary + if: always() + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + PLATFORM="${{ steps.platform.outputs.platform }}" + if [ "${{ steps.version.outputs.skip }}" = "true" ]; then + echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY + echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY + elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then + echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY + echo "|------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY + fi diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index dd54aff..5081974 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.09 +# VERSION: 01.01.10 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 9615a4e..86908c2 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -1,243 +1,243 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform -# PATH: /templates/workflows/universal/pre-release.yml.template -# VERSION: 05.01.00 -# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch - -name: "Universal: Pre-Release" - -on: - pull_request: - types: [closed] - branches: - - dev - pull_request_target: - types: [synchronize, opened, reopened] - branches: - - main - workflow_dispatch: - inputs: - stability: - description: 'Pre-release channel' - required: true - type: choice - options: - - development - - alpha - - beta - - release-candidate - -permissions: - contents: write - -env: - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -jobs: - build: - name: "Build Pre-Release (${{ inputs.stability || 'development' }})" - runs-on: release - if: >- - github.event_name == 'workflow_dispatch' || - (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') || - (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main') - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.MOKOGITEA_TOKEN }} - ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }} - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - run: | - # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) - if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then - echo Using pre-installed /opt/moko-platform - echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV - else - echo Falling back to fresh clone - if ! command -v composer > /dev/null 2>&1; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 - fi - rm -rf /tmp/moko-platform-api - CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git - git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api - cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet - echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV - fi - - - name: Detect platform - id: platform - run: | - php ${MOKO_CLI}/manifest_read.php --path . --github-output - - - name: Resolve metadata and bump version - id: meta - run: | - # Auto-detect stability: RC for PRs targeting main, else use input or default to development - if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then - STABILITY="release-candidate" - else - STABILITY="${{ inputs.stability || 'development' }}" - fi - - case "$STABILITY" in - development) SUFFIX="-dev"; TAG="development" ;; - alpha) SUFFIX="-alpha"; TAG="alpha" ;; - beta) SUFFIX="-beta"; TAG="beta" ;; - release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; - esac - - # Bump version via CLI: patch for dev/alpha/beta, minor for RC - case "$STABILITY" in - release-candidate) BUMP="minor" ;; - *) BUMP="patch" ;; - esac - - php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true - - # Set stability suffix and verify consistency - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") - VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') - - php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true - php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - - # Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml - php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true - - # Append suffix for output - if [ -n "$SUFFIX" ]; then - VERSION="${VERSION}${SUFFIX}" - fi - - # Commit version bump - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - git add -A - git diff --cached --quiet || { - git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" - git push origin HEAD 2>&1 - } - - # Auto-detect element via manifest_element.php - php ${MOKO_CLI}/manifest_element.php \ - --path . --version "$VERSION" --stability "$STABILITY" \ - --repo "${GITEA_REPO}" --github-output - - # Read back element outputs - EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) - ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) - [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" - - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" - echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" - echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" - - echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" - - - name: Create release - id: release - run: | - TAG="${{ steps.meta.outputs.tag }}" - VERSION="${{ steps.meta.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_create.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch dev --prerelease - - - name: Update release notes from CHANGELOG.md - run: | - TAG="${{ steps.meta.outputs.tag }}" - VERSION="${{ steps.meta.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) - if [ -f "CHANGELOG.md" ]; then - NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) - [ -z "$NOTES" ] && NOTES="Release ${VERSION}" - else - NOTES="Release ${VERSION}" - fi - - # Update release body via API - RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) - - if [ -n "$RELEASE_ID" ]; then - python3 -c " - import json, urllib.request - body = open('/dev/stdin').read() - payload = json.dumps({'body': body}).encode() - req = urllib.request.Request( - '${API_BASE}/releases/${RELEASE_ID}', - data=payload, method='PATCH', - headers={ - 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', - 'Content-Type': 'application/json' - }) - urllib.request.urlopen(req) - " <<< "$NOTES" - echo "Release notes updated from CHANGELOG.md" - fi - - - name: Build package and upload - id: package - run: | - VERSION="${{ steps.meta.outputs.version }}" - TAG="${{ steps.meta.outputs.tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_package.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp || true - - # updates.xml is generated dynamically by MokoGitea license server - # No need to build, commit, or sync updates.xml from workflows - - - name: "Delete lesser pre-release channels (cascade)" - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - php ${MOKO_CLI}/release_cascade.php \ - --stability "${{ steps.meta.outputs.stability }}" \ - --token "${TOKEN}" \ - --api-base "${API_BASE}" - - - name: Summary - if: always() - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - ZIP_NAME="${{ steps.meta.outputs.zip_name }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY - echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY - echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /templates/workflows/universal/pre-release.yml.template +# VERSION: 05.01.00 +# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch + +name: "Universal: Pre-Release" + +on: + pull_request: + types: [closed] + branches: + - dev + pull_request_target: + types: [synchronize, opened, reopened] + branches: + - main + workflow_dispatch: + inputs: + stability: + description: 'Pre-release channel' + required: true + type: choice + options: + - development + - alpha + - beta + - release-candidate + +permissions: + contents: write + +env: + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} + GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} + +jobs: + build: + name: "Build Pre-Release (${{ inputs.stability || 'development' }})" + runs-on: release + if: >- + github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') || + (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main') + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.MOKOGITEA_TOKEN }} + ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }} + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + fi + + - name: Detect platform + id: platform + run: | + php ${MOKO_CLI}/manifest_read.php --path . --github-output + + - name: Resolve metadata and bump version + id: meta + run: | + # Auto-detect stability: RC for PRs targeting main, else use input or default to development + if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then + STABILITY="release-candidate" + else + STABILITY="${{ inputs.stability || 'development' }}" + fi + + case "$STABILITY" in + development) SUFFIX="-dev"; TAG="development" ;; + alpha) SUFFIX="-alpha"; TAG="alpha" ;; + beta) SUFFIX="-beta"; TAG="beta" ;; + release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; + esac + + # Bump version via CLI: patch for dev/alpha/beta, minor for RC + case "$STABILITY" in + release-candidate) BUMP="minor" ;; + *) BUMP="patch" ;; + esac + + php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true + + # Set stability suffix and verify consistency + VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") + VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') + + php ${MOKO_CLI}/version_set_platform.php \ + --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true + php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true + + # Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml + php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true + + # Append suffix for output + if [ -n "$SUFFIX" ]; then + VERSION="${VERSION}${SUFFIX}" + fi + + # Commit version bump + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + git add -A + git diff --cached --quiet || { + git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" + git push origin HEAD 2>&1 + } + + # Auto-detect element via manifest_element.php + php ${MOKO_CLI}/manifest_element.php \ + --path . --version "$VERSION" --stability "$STABILITY" \ + --repo "${GITEA_REPO}" --github-output + + # Read back element outputs + EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') + [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" + + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" + echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" + echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" + + echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" + + - name: Create release + id: release + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_create.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --branch dev --prerelease + + - name: Update release notes from CHANGELOG.md + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) + if [ -f "CHANGELOG.md" ]; then + NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) + [ -z "$NOTES" ] && NOTES="Release ${VERSION}" + else + NOTES="Release ${VERSION}" + fi + + # Update release body via API + RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + + if [ -n "$RELEASE_ID" ]; then + python3 -c " + import json, urllib.request + body = open('/dev/stdin').read() + payload = json.dumps({'body': body}).encode() + req = urllib.request.Request( + '${API_BASE}/releases/${RELEASE_ID}', + data=payload, method='PATCH', + headers={ + 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', + 'Content-Type': 'application/json' + }) + urllib.request.urlopen(req) + " <<< "$NOTES" + echo "Release notes updated from CHANGELOG.md" + fi + + - name: Build package and upload + id: package + run: | + VERSION="${{ steps.meta.outputs.version }}" + TAG="${{ steps.meta.outputs.tag }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_package.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --output /tmp || true + + # updates.xml is generated dynamically by MokoGitea license server + # No need to build, commit, or sync updates.xml from workflows + + - name: "Delete lesser pre-release channels (cascade)" + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + php ${MOKO_CLI}/release_cascade.php \ + --stability "${{ steps.meta.outputs.stability }}" \ + --token "${TOKEN}" \ + --api-base "${API_BASE}" + + - name: Summary + if: always() + run: | + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + ZIP_NAME="${{ steps.meta.outputs.zip_name }}" + SHA256="${{ steps.package.outputs.sha256_zip }}" + echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY + echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY + echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY diff --git a/README.md b/README.md index b91a717..263bb7a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokojoombackup/mokojoombackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml index f402a0a..590e349 100644 --- a/source/packages/com_mokojoombackup/mokojoombackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> com_mokojoombackup - 01.01.07-dev + 01.01.10-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index 8695fef..62f2c0b 100644 --- a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokojoombackup - 01.01.07-dev + 01.01.10-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml index db41eeb..73ae765 100644 --- a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokojoombackup - 01.01.07-dev + 01.01.10-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml index b208cd3..af5ed97 100644 --- a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokojoombackup - 01.01.07-dev + 01.01.10-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index 2d284a5..b353b34 100644 --- a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokojoombackup - 01.01.07-dev + 01.01.10-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml index 45c64e8..cdbf066 100644 --- a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokojoombackup - 01.01.07-dev + 01.01.10-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml index b65b02d..6322cf2 100644 --- a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokojoombackup - 01.01.07-dev + 01.01.10-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index f63f135..b7e97b7 100644 --- a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokojoombackup - 01.01.07-dev + 01.01.10-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokojoombackup.xml b/source/pkg_mokojoombackup.xml index b0e12be..b5980ca 100644 --- a/source/pkg_mokojoombackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokojoombackup - 01.01.07-dev + 01.01.10-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From a0c6332372856c0e71b26a3fc764a88feb9449c4 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 15:10:04 -0500 Subject: [PATCH 60/81] fix: flatten nested package directories from rename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mokobackup→mokojoombackup rename created double-nested directories (e.g. com_mokojoombackup/com_mokojoombackup/). Joomla installer could not find files at the expected paths. Flattened all packages. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../com_mokojoombackup/{com_mokojoombackup => }/api/index.html | 0 .../api/src/Controller/BackupsController.php | 0 .../{com_mokojoombackup => }/api/src/Controller/index.html | 0 .../{com_mokojoombackup => }/api/src/View/Backups/JsonapiView.php | 0 .../{com_mokojoombackup => }/api/src/View/Backups/index.html | 0 .../{com_mokojoombackup => }/api/src/View/index.html | 0 .../{com_mokojoombackup => }/api/src/index.html | 0 .../com_mokojoombackup/{com_mokojoombackup => }/cli/index.html | 0 .../com_mokojoombackup/{com_mokojoombackup => }/config.xml | 0 .../com_mokojoombackup/{com_mokojoombackup => }/forms/backup.xml | 0 .../{com_mokojoombackup => }/forms/filter_backups.xml | 0 .../{com_mokojoombackup => }/forms/filter_profiles.xml | 0 .../com_mokojoombackup/{com_mokojoombackup => }/forms/index.html | 0 .../com_mokojoombackup/{com_mokojoombackup => }/forms/profile.xml | 0 .../com_mokojoombackup/{com_mokojoombackup => }/index.html | 0 .../{com_mokojoombackup => }/language/en-GB/index.html | 0 .../{com_mokojoombackup => }/language/en-US/index.html | 0 .../{com_mokojoombackup => }/language/index.html | 0 .../{com_mokojoombackup => }/services/index.html | 0 .../{com_mokojoombackup => }/services/provider.php | 0 .../com_mokojoombackup/{com_mokojoombackup => }/sql/index.html | 0 .../{com_mokojoombackup => }/sql/install.mysql.sql | 0 .../{com_mokojoombackup => }/sql/mysql/index.html | 0 .../{com_mokojoombackup => }/sql/uninstall.mysql.sql | 0 .../{com_mokojoombackup => }/sql/updates/index.html | 0 .../{com_mokojoombackup => }/sql/updates/mysql/01.00.00.sql | 0 .../{com_mokojoombackup => }/sql/updates/mysql/01.01.01.sql | 0 .../{com_mokojoombackup => }/sql/updates/mysql/01.01.02.sql | 0 .../{com_mokojoombackup => }/sql/updates/mysql/index.html | 0 .../{com_mokojoombackup => }/src/Controller/AjaxController.php | 0 .../{com_mokojoombackup => }/src/Controller/BackupController.php | 0 .../{com_mokojoombackup => }/src/Controller/BackupsController.php | 0 .../{com_mokojoombackup => }/src/Controller/DisplayController.php | 0 .../{com_mokojoombackup => }/src/Controller/ProfileController.php | 0 .../src/Controller/ProfilesController.php | 0 .../{com_mokojoombackup => }/src/Controller/index.html | 0 .../{com_mokojoombackup => }/src/Engine/AkeebaImporter.php | 0 .../{com_mokojoombackup => }/src/Engine/ArchiverInterface.php | 0 .../{com_mokojoombackup => }/src/Engine/BackupEngine.php | 0 .../{com_mokojoombackup => }/src/Engine/DatabaseDumper.php | 0 .../{com_mokojoombackup => }/src/Engine/DatabaseImporter.php | 0 .../{com_mokojoombackup => }/src/Engine/DifferentialScanner.php | 0 .../{com_mokojoombackup => }/src/Engine/FileRestorer.php | 0 .../{com_mokojoombackup => }/src/Engine/FileScanner.php | 0 .../{com_mokojoombackup => }/src/Engine/FtpUploader.php | 0 .../{com_mokojoombackup => }/src/Engine/GoogleDriveUploader.php | 0 .../{com_mokojoombackup => }/src/Engine/JpaUnarchiver.php | 0 .../{com_mokojoombackup => }/src/Engine/MokoRestore.php | 0 .../{com_mokojoombackup => }/src/Engine/NotificationSender.php | 0 .../{com_mokojoombackup => }/src/Engine/PlaceholderResolver.php | 0 .../src/Engine/RemoteUploaderInterface.php | 0 .../{com_mokojoombackup => }/src/Engine/RestoreEngine.php | 0 .../{com_mokojoombackup => }/src/Engine/S3Uploader.php | 0 .../{com_mokojoombackup => }/src/Engine/SteppedBackupEngine.php | 0 .../{com_mokojoombackup => }/src/Engine/SteppedSession.php | 0 .../{com_mokojoombackup => }/src/Engine/TarGzArchiver.php | 0 .../{com_mokojoombackup => }/src/Engine/ZipArchiver.php | 0 .../{com_mokojoombackup => }/src/Engine/index.html | 0 .../src/Extension/MokoBackupComponent.php | 0 .../{com_mokojoombackup => }/src/Extension/index.html | 0 .../{com_mokojoombackup => }/src/Field/DatabaseTablesField.php | 0 .../{com_mokojoombackup => }/src/Field/DirectoryFilterField.php | 0 .../{com_mokojoombackup => }/src/Field/ExcludeListField.php | 0 .../{com_mokojoombackup => }/src/Field/FolderPickerField.php | 0 .../{com_mokojoombackup => }/src/Model/BackupModel.php | 0 .../{com_mokojoombackup => }/src/Model/BackupsModel.php | 0 .../{com_mokojoombackup => }/src/Model/DashboardModel.php | 0 .../{com_mokojoombackup => }/src/Model/ProfileModel.php | 0 .../{com_mokojoombackup => }/src/Model/ProfilesModel.php | 0 .../{com_mokojoombackup => }/src/Model/index.html | 0 .../{com_mokojoombackup => }/src/Table/BackupTable.php | 0 .../{com_mokojoombackup => }/src/Table/ProfileTable.php | 0 .../{com_mokojoombackup => }/src/Table/index.html | 0 .../{com_mokojoombackup => }/src/View/Backup/HtmlView.php | 0 .../{com_mokojoombackup => }/src/View/Backup/index.html | 0 .../{com_mokojoombackup => }/src/View/Backups/HtmlView.php | 0 .../{com_mokojoombackup => }/src/View/Backups/index.html | 0 .../{com_mokojoombackup => }/src/View/Dashboard/HtmlView.php | 0 .../{com_mokojoombackup => }/src/View/Profile/HtmlView.php | 0 .../{com_mokojoombackup => }/src/View/Profile/index.html | 0 .../{com_mokojoombackup => }/src/View/Profiles/HtmlView.php | 0 .../{com_mokojoombackup => }/src/View/Profiles/index.html | 0 .../{com_mokojoombackup => }/src/View/index.html | 0 .../com_mokojoombackup/{com_mokojoombackup => }/src/index.html | 0 .../{com_mokojoombackup => }/tmpl/backup/default.php | 0 .../{com_mokojoombackup => }/tmpl/backup/index.html | 0 .../{com_mokojoombackup => }/tmpl/backups/default.php | 0 .../{com_mokojoombackup => }/tmpl/backups/index.html | 0 .../{com_mokojoombackup => }/tmpl/dashboard/default.php | 0 .../com_mokojoombackup/{com_mokojoombackup => }/tmpl/index.html | 0 .../{com_mokojoombackup => }/tmpl/profile/edit.php | 0 .../{com_mokojoombackup => }/tmpl/profile/index.html | 0 .../{com_mokojoombackup => }/tmpl/profiles/default.php | 0 .../{com_mokojoombackup => }/tmpl/profiles/index.html | 0 .../{plg_actionlog_mokojoombackup => }/services/provider.php | 0 .../src/Extension/MokoBackupActionlog.php | 0 .../{plg_console_mokojoombackup => }/services/provider.php | 0 .../src/Command/CleanupCommand.php | 0 .../{plg_console_mokojoombackup => }/src/Command/ListCommand.php | 0 .../src/Command/ProfilesCommand.php | 0 .../src/Command/RestoreCommand.php | 0 .../{plg_console_mokojoombackup => }/src/Command/RunCommand.php | 0 .../src/Extension/MokoBackupConsole.php | 0 .../{plg_content_mokojoombackup => }/services/provider.php | 0 .../src/Extension/MokoBackupContent.php | 0 .../{plg_quickicon_mokojoombackup => }/index.html | 0 .../{plg_quickicon_mokojoombackup => }/language/en-GB/index.html | 0 .../{plg_quickicon_mokojoombackup => }/language/en-US/index.html | 0 .../{plg_quickicon_mokojoombackup => }/language/index.html | 0 .../{plg_quickicon_mokojoombackup => }/services/index.html | 0 .../{plg_quickicon_mokojoombackup => }/services/provider.php | 0 .../src/Extension/MokoBackupQuickicon.php | 0 .../{plg_quickicon_mokojoombackup => }/src/Extension/index.html | 0 .../{plg_quickicon_mokojoombackup => }/src/index.html | 0 .../{plg_system_mokojoombackup => }/index.html | 0 .../{plg_system_mokojoombackup => }/language/en-GB/index.html | 0 .../{plg_system_mokojoombackup => }/language/en-US/index.html | 0 .../{plg_system_mokojoombackup => }/language/index.html | 0 .../{plg_system_mokojoombackup => }/services/index.html | 0 .../{plg_system_mokojoombackup => }/services/provider.php | 0 .../{plg_system_mokojoombackup => }/src/Extension/MokoBackup.php | 0 .../{plg_system_mokojoombackup => }/src/Extension/index.html | 0 .../{plg_system_mokojoombackup => }/src/index.html | 0 .../{plg_task_mokojoombackup => }/forms/index.html | 0 .../{plg_task_mokojoombackup => }/forms/run_profile.xml | 0 .../{plg_task_mokojoombackup => }/index.html | 0 .../{plg_task_mokojoombackup => }/language/en-GB/index.html | 0 .../{plg_task_mokojoombackup => }/language/en-US/index.html | 0 .../{plg_task_mokojoombackup => }/language/index.html | 0 .../{plg_task_mokojoombackup => }/services/index.html | 0 .../{plg_task_mokojoombackup => }/services/provider.php | 0 .../src/Extension/MokoBackupTask.php | 0 .../{plg_task_mokojoombackup => }/src/Extension/index.html | 0 .../{plg_task_mokojoombackup => }/src/index.html | 0 .../{plg_webservices_mokojoombackup => }/index.html | 0 .../language/en-GB/index.html | 0 .../language/en-US/index.html | 0 .../{plg_webservices_mokojoombackup => }/language/index.html | 0 .../{plg_webservices_mokojoombackup => }/services/index.html | 0 .../{plg_webservices_mokojoombackup => }/services/provider.php | 0 .../src/Extension/MokoBackupWebServices.php | 0 .../{plg_webservices_mokojoombackup => }/src/Extension/index.html | 0 .../{plg_webservices_mokojoombackup => }/src/index.html | 0 143 files changed, 0 insertions(+), 0 deletions(-) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/api/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/api/src/Controller/BackupsController.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/api/src/Controller/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/api/src/View/Backups/JsonapiView.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/api/src/View/Backups/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/api/src/View/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/api/src/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/cli/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/config.xml (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/forms/backup.xml (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/forms/filter_backups.xml (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/forms/filter_profiles.xml (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/forms/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/forms/profile.xml (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/language/en-GB/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/language/en-US/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/language/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/services/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/services/provider.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/sql/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/sql/install.mysql.sql (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/sql/mysql/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/sql/uninstall.mysql.sql (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/sql/updates/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/sql/updates/mysql/01.00.00.sql (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/sql/updates/mysql/01.01.01.sql (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/sql/updates/mysql/01.01.02.sql (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/sql/updates/mysql/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Controller/AjaxController.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Controller/BackupController.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Controller/BackupsController.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Controller/DisplayController.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Controller/ProfileController.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Controller/ProfilesController.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Controller/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/AkeebaImporter.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/ArchiverInterface.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/BackupEngine.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/DatabaseDumper.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/DatabaseImporter.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/DifferentialScanner.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/FileRestorer.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/FileScanner.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/FtpUploader.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/GoogleDriveUploader.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/JpaUnarchiver.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/MokoRestore.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/NotificationSender.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/PlaceholderResolver.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/RemoteUploaderInterface.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/RestoreEngine.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/S3Uploader.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/SteppedBackupEngine.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/SteppedSession.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/TarGzArchiver.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/ZipArchiver.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Engine/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Extension/MokoBackupComponent.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Extension/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Field/DatabaseTablesField.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Field/DirectoryFilterField.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Field/ExcludeListField.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Field/FolderPickerField.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Model/BackupModel.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Model/BackupsModel.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Model/DashboardModel.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Model/ProfileModel.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Model/ProfilesModel.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Model/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Table/BackupTable.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Table/ProfileTable.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/Table/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/Backup/HtmlView.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/Backup/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/Backups/HtmlView.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/Backups/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/Dashboard/HtmlView.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/Profile/HtmlView.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/Profile/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/Profiles/HtmlView.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/Profiles/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/View/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/src/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/backup/default.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/backup/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/backups/default.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/backups/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/dashboard/default.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/profile/edit.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/profile/index.html (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/profiles/default.php (100%) rename source/packages/com_mokojoombackup/{com_mokojoombackup => }/tmpl/profiles/index.html (100%) rename source/packages/plg_actionlog_mokojoombackup/{plg_actionlog_mokojoombackup => }/services/provider.php (100%) rename source/packages/plg_actionlog_mokojoombackup/{plg_actionlog_mokojoombackup => }/src/Extension/MokoBackupActionlog.php (100%) rename source/packages/plg_console_mokojoombackup/{plg_console_mokojoombackup => }/services/provider.php (100%) rename source/packages/plg_console_mokojoombackup/{plg_console_mokojoombackup => }/src/Command/CleanupCommand.php (100%) rename source/packages/plg_console_mokojoombackup/{plg_console_mokojoombackup => }/src/Command/ListCommand.php (100%) rename source/packages/plg_console_mokojoombackup/{plg_console_mokojoombackup => }/src/Command/ProfilesCommand.php (100%) rename source/packages/plg_console_mokojoombackup/{plg_console_mokojoombackup => }/src/Command/RestoreCommand.php (100%) rename source/packages/plg_console_mokojoombackup/{plg_console_mokojoombackup => }/src/Command/RunCommand.php (100%) rename source/packages/plg_console_mokojoombackup/{plg_console_mokojoombackup => }/src/Extension/MokoBackupConsole.php (100%) rename source/packages/plg_content_mokojoombackup/{plg_content_mokojoombackup => }/services/provider.php (100%) rename source/packages/plg_content_mokojoombackup/{plg_content_mokojoombackup => }/src/Extension/MokoBackupContent.php (100%) rename source/packages/plg_quickicon_mokojoombackup/{plg_quickicon_mokojoombackup => }/index.html (100%) rename source/packages/plg_quickicon_mokojoombackup/{plg_quickicon_mokojoombackup => }/language/en-GB/index.html (100%) rename source/packages/plg_quickicon_mokojoombackup/{plg_quickicon_mokojoombackup => }/language/en-US/index.html (100%) rename source/packages/plg_quickicon_mokojoombackup/{plg_quickicon_mokojoombackup => }/language/index.html (100%) rename source/packages/plg_quickicon_mokojoombackup/{plg_quickicon_mokojoombackup => }/services/index.html (100%) rename source/packages/plg_quickicon_mokojoombackup/{plg_quickicon_mokojoombackup => }/services/provider.php (100%) rename source/packages/plg_quickicon_mokojoombackup/{plg_quickicon_mokojoombackup => }/src/Extension/MokoBackupQuickicon.php (100%) rename source/packages/plg_quickicon_mokojoombackup/{plg_quickicon_mokojoombackup => }/src/Extension/index.html (100%) rename source/packages/plg_quickicon_mokojoombackup/{plg_quickicon_mokojoombackup => }/src/index.html (100%) rename source/packages/plg_system_mokojoombackup/{plg_system_mokojoombackup => }/index.html (100%) rename source/packages/plg_system_mokojoombackup/{plg_system_mokojoombackup => }/language/en-GB/index.html (100%) rename source/packages/plg_system_mokojoombackup/{plg_system_mokojoombackup => }/language/en-US/index.html (100%) rename source/packages/plg_system_mokojoombackup/{plg_system_mokojoombackup => }/language/index.html (100%) rename source/packages/plg_system_mokojoombackup/{plg_system_mokojoombackup => }/services/index.html (100%) rename source/packages/plg_system_mokojoombackup/{plg_system_mokojoombackup => }/services/provider.php (100%) rename source/packages/plg_system_mokojoombackup/{plg_system_mokojoombackup => }/src/Extension/MokoBackup.php (100%) rename source/packages/plg_system_mokojoombackup/{plg_system_mokojoombackup => }/src/Extension/index.html (100%) rename source/packages/plg_system_mokojoombackup/{plg_system_mokojoombackup => }/src/index.html (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/forms/index.html (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/forms/run_profile.xml (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/index.html (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/language/en-GB/index.html (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/language/en-US/index.html (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/language/index.html (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/services/index.html (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/services/provider.php (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/src/Extension/MokoBackupTask.php (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/src/Extension/index.html (100%) rename source/packages/plg_task_mokojoombackup/{plg_task_mokojoombackup => }/src/index.html (100%) rename source/packages/plg_webservices_mokojoombackup/{plg_webservices_mokojoombackup => }/index.html (100%) rename source/packages/plg_webservices_mokojoombackup/{plg_webservices_mokojoombackup => }/language/en-GB/index.html (100%) rename source/packages/plg_webservices_mokojoombackup/{plg_webservices_mokojoombackup => }/language/en-US/index.html (100%) rename source/packages/plg_webservices_mokojoombackup/{plg_webservices_mokojoombackup => }/language/index.html (100%) rename source/packages/plg_webservices_mokojoombackup/{plg_webservices_mokojoombackup => }/services/index.html (100%) rename source/packages/plg_webservices_mokojoombackup/{plg_webservices_mokojoombackup => }/services/provider.php (100%) rename source/packages/plg_webservices_mokojoombackup/{plg_webservices_mokojoombackup => }/src/Extension/MokoBackupWebServices.php (100%) rename source/packages/plg_webservices_mokojoombackup/{plg_webservices_mokojoombackup => }/src/Extension/index.html (100%) rename source/packages/plg_webservices_mokojoombackup/{plg_webservices_mokojoombackup => }/src/index.html (100%) diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/api/index.html b/source/packages/com_mokojoombackup/api/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/api/index.html rename to source/packages/com_mokojoombackup/api/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/Controller/BackupsController.php b/source/packages/com_mokojoombackup/api/src/Controller/BackupsController.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/api/src/Controller/BackupsController.php rename to source/packages/com_mokojoombackup/api/src/Controller/BackupsController.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/Controller/index.html b/source/packages/com_mokojoombackup/api/src/Controller/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/api/src/Controller/index.html rename to source/packages/com_mokojoombackup/api/src/Controller/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/Backups/JsonapiView.php b/source/packages/com_mokojoombackup/api/src/View/Backups/JsonapiView.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/Backups/JsonapiView.php rename to source/packages/com_mokojoombackup/api/src/View/Backups/JsonapiView.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/Backups/index.html b/source/packages/com_mokojoombackup/api/src/View/Backups/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/Backups/index.html rename to source/packages/com_mokojoombackup/api/src/View/Backups/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/index.html b/source/packages/com_mokojoombackup/api/src/View/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/api/src/View/index.html rename to source/packages/com_mokojoombackup/api/src/View/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/api/src/index.html b/source/packages/com_mokojoombackup/api/src/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/api/src/index.html rename to source/packages/com_mokojoombackup/api/src/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/cli/index.html b/source/packages/com_mokojoombackup/cli/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/cli/index.html rename to source/packages/com_mokojoombackup/cli/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/config.xml b/source/packages/com_mokojoombackup/config.xml similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/config.xml rename to source/packages/com_mokojoombackup/config.xml diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/forms/backup.xml b/source/packages/com_mokojoombackup/forms/backup.xml similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/forms/backup.xml rename to source/packages/com_mokojoombackup/forms/backup.xml diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_backups.xml b/source/packages/com_mokojoombackup/forms/filter_backups.xml similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_backups.xml rename to source/packages/com_mokojoombackup/forms/filter_backups.xml diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_profiles.xml b/source/packages/com_mokojoombackup/forms/filter_profiles.xml similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/forms/filter_profiles.xml rename to source/packages/com_mokojoombackup/forms/filter_profiles.xml diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/forms/index.html b/source/packages/com_mokojoombackup/forms/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/forms/index.html rename to source/packages/com_mokojoombackup/forms/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/forms/profile.xml b/source/packages/com_mokojoombackup/forms/profile.xml similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/forms/profile.xml rename to source/packages/com_mokojoombackup/forms/profile.xml diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/index.html b/source/packages/com_mokojoombackup/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/index.html rename to source/packages/com_mokojoombackup/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/language/en-GB/index.html b/source/packages/com_mokojoombackup/language/en-GB/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/language/en-GB/index.html rename to source/packages/com_mokojoombackup/language/en-GB/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/language/en-US/index.html b/source/packages/com_mokojoombackup/language/en-US/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/language/en-US/index.html rename to source/packages/com_mokojoombackup/language/en-US/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/language/index.html b/source/packages/com_mokojoombackup/language/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/language/index.html rename to source/packages/com_mokojoombackup/language/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/services/index.html b/source/packages/com_mokojoombackup/services/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/services/index.html rename to source/packages/com_mokojoombackup/services/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/services/provider.php b/source/packages/com_mokojoombackup/services/provider.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/services/provider.php rename to source/packages/com_mokojoombackup/services/provider.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/index.html b/source/packages/com_mokojoombackup/sql/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/sql/index.html rename to source/packages/com_mokojoombackup/sql/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/install.mysql.sql b/source/packages/com_mokojoombackup/sql/install.mysql.sql similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/sql/install.mysql.sql rename to source/packages/com_mokojoombackup/sql/install.mysql.sql diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/mysql/index.html b/source/packages/com_mokojoombackup/sql/mysql/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/sql/mysql/index.html rename to source/packages/com_mokojoombackup/sql/mysql/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/uninstall.mysql.sql b/source/packages/com_mokojoombackup/sql/uninstall.mysql.sql similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/sql/uninstall.mysql.sql rename to source/packages/com_mokojoombackup/sql/uninstall.mysql.sql diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/index.html b/source/packages/com_mokojoombackup/sql/updates/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/index.html rename to source/packages/com_mokojoombackup/sql/updates/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.00.00.sql b/source/packages/com_mokojoombackup/sql/updates/mysql/01.00.00.sql similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.00.00.sql rename to source/packages/com_mokojoombackup/sql/updates/mysql/01.00.00.sql diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.01.sql b/source/packages/com_mokojoombackup/sql/updates/mysql/01.01.01.sql similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.01.sql rename to source/packages/com_mokojoombackup/sql/updates/mysql/01.01.01.sql diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.02.sql b/source/packages/com_mokojoombackup/sql/updates/mysql/01.01.02.sql similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/01.01.02.sql rename to source/packages/com_mokojoombackup/sql/updates/mysql/01.01.02.sql diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/index.html b/source/packages/com_mokojoombackup/sql/updates/mysql/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/sql/updates/mysql/index.html rename to source/packages/com_mokojoombackup/sql/updates/mysql/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/AjaxController.php b/source/packages/com_mokojoombackup/src/Controller/AjaxController.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/AjaxController.php rename to source/packages/com_mokojoombackup/src/Controller/AjaxController.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupController.php b/source/packages/com_mokojoombackup/src/Controller/BackupController.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupController.php rename to source/packages/com_mokojoombackup/src/Controller/BackupController.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupsController.php b/source/packages/com_mokojoombackup/src/Controller/BackupsController.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/BackupsController.php rename to source/packages/com_mokojoombackup/src/Controller/BackupsController.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/DisplayController.php b/source/packages/com_mokojoombackup/src/Controller/DisplayController.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/DisplayController.php rename to source/packages/com_mokojoombackup/src/Controller/DisplayController.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfileController.php b/source/packages/com_mokojoombackup/src/Controller/ProfileController.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfileController.php rename to source/packages/com_mokojoombackup/src/Controller/ProfileController.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfilesController.php b/source/packages/com_mokojoombackup/src/Controller/ProfilesController.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/ProfilesController.php rename to source/packages/com_mokojoombackup/src/Controller/ProfilesController.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/index.html b/source/packages/com_mokojoombackup/src/Controller/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Controller/index.html rename to source/packages/com_mokojoombackup/src/Controller/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/AkeebaImporter.php b/source/packages/com_mokojoombackup/src/Engine/AkeebaImporter.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/AkeebaImporter.php rename to source/packages/com_mokojoombackup/src/Engine/AkeebaImporter.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ArchiverInterface.php b/source/packages/com_mokojoombackup/src/Engine/ArchiverInterface.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ArchiverInterface.php rename to source/packages/com_mokojoombackup/src/Engine/ArchiverInterface.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/BackupEngine.php b/source/packages/com_mokojoombackup/src/Engine/BackupEngine.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/BackupEngine.php rename to source/packages/com_mokojoombackup/src/Engine/BackupEngine.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseDumper.php b/source/packages/com_mokojoombackup/src/Engine/DatabaseDumper.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseDumper.php rename to source/packages/com_mokojoombackup/src/Engine/DatabaseDumper.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseImporter.php b/source/packages/com_mokojoombackup/src/Engine/DatabaseImporter.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DatabaseImporter.php rename to source/packages/com_mokojoombackup/src/Engine/DatabaseImporter.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DifferentialScanner.php b/source/packages/com_mokojoombackup/src/Engine/DifferentialScanner.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/DifferentialScanner.php rename to source/packages/com_mokojoombackup/src/Engine/DifferentialScanner.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileRestorer.php b/source/packages/com_mokojoombackup/src/Engine/FileRestorer.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileRestorer.php rename to source/packages/com_mokojoombackup/src/Engine/FileRestorer.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileScanner.php b/source/packages/com_mokojoombackup/src/Engine/FileScanner.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FileScanner.php rename to source/packages/com_mokojoombackup/src/Engine/FileScanner.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FtpUploader.php b/source/packages/com_mokojoombackup/src/Engine/FtpUploader.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/FtpUploader.php rename to source/packages/com_mokojoombackup/src/Engine/FtpUploader.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/GoogleDriveUploader.php b/source/packages/com_mokojoombackup/src/Engine/GoogleDriveUploader.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/GoogleDriveUploader.php rename to source/packages/com_mokojoombackup/src/Engine/GoogleDriveUploader.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/JpaUnarchiver.php b/source/packages/com_mokojoombackup/src/Engine/JpaUnarchiver.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/JpaUnarchiver.php rename to source/packages/com_mokojoombackup/src/Engine/JpaUnarchiver.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/MokoRestore.php b/source/packages/com_mokojoombackup/src/Engine/MokoRestore.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/MokoRestore.php rename to source/packages/com_mokojoombackup/src/Engine/MokoRestore.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/NotificationSender.php b/source/packages/com_mokojoombackup/src/Engine/NotificationSender.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/NotificationSender.php rename to source/packages/com_mokojoombackup/src/Engine/NotificationSender.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/PlaceholderResolver.php b/source/packages/com_mokojoombackup/src/Engine/PlaceholderResolver.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/PlaceholderResolver.php rename to source/packages/com_mokojoombackup/src/Engine/PlaceholderResolver.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RemoteUploaderInterface.php b/source/packages/com_mokojoombackup/src/Engine/RemoteUploaderInterface.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RemoteUploaderInterface.php rename to source/packages/com_mokojoombackup/src/Engine/RemoteUploaderInterface.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RestoreEngine.php b/source/packages/com_mokojoombackup/src/Engine/RestoreEngine.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/RestoreEngine.php rename to source/packages/com_mokojoombackup/src/Engine/RestoreEngine.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/S3Uploader.php b/source/packages/com_mokojoombackup/src/Engine/S3Uploader.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/S3Uploader.php rename to source/packages/com_mokojoombackup/src/Engine/S3Uploader.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedBackupEngine.php b/source/packages/com_mokojoombackup/src/Engine/SteppedBackupEngine.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedBackupEngine.php rename to source/packages/com_mokojoombackup/src/Engine/SteppedBackupEngine.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedSession.php b/source/packages/com_mokojoombackup/src/Engine/SteppedSession.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/SteppedSession.php rename to source/packages/com_mokojoombackup/src/Engine/SteppedSession.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/TarGzArchiver.php b/source/packages/com_mokojoombackup/src/Engine/TarGzArchiver.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/TarGzArchiver.php rename to source/packages/com_mokojoombackup/src/Engine/TarGzArchiver.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ZipArchiver.php b/source/packages/com_mokojoombackup/src/Engine/ZipArchiver.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/ZipArchiver.php rename to source/packages/com_mokojoombackup/src/Engine/ZipArchiver.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/index.html b/source/packages/com_mokojoombackup/src/Engine/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Engine/index.html rename to source/packages/com_mokojoombackup/src/Engine/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Extension/MokoBackupComponent.php b/source/packages/com_mokojoombackup/src/Extension/MokoBackupComponent.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Extension/MokoBackupComponent.php rename to source/packages/com_mokojoombackup/src/Extension/MokoBackupComponent.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Extension/index.html b/source/packages/com_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Extension/index.html rename to source/packages/com_mokojoombackup/src/Extension/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DatabaseTablesField.php b/source/packages/com_mokojoombackup/src/Field/DatabaseTablesField.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DatabaseTablesField.php rename to source/packages/com_mokojoombackup/src/Field/DatabaseTablesField.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DirectoryFilterField.php b/source/packages/com_mokojoombackup/src/Field/DirectoryFilterField.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/DirectoryFilterField.php rename to source/packages/com_mokojoombackup/src/Field/DirectoryFilterField.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/ExcludeListField.php b/source/packages/com_mokojoombackup/src/Field/ExcludeListField.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/ExcludeListField.php rename to source/packages/com_mokojoombackup/src/Field/ExcludeListField.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/FolderPickerField.php b/source/packages/com_mokojoombackup/src/Field/FolderPickerField.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Field/FolderPickerField.php rename to source/packages/com_mokojoombackup/src/Field/FolderPickerField.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/BackupModel.php b/source/packages/com_mokojoombackup/src/Model/BackupModel.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/BackupModel.php rename to source/packages/com_mokojoombackup/src/Model/BackupModel.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/BackupsModel.php b/source/packages/com_mokojoombackup/src/Model/BackupsModel.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/BackupsModel.php rename to source/packages/com_mokojoombackup/src/Model/BackupsModel.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/DashboardModel.php b/source/packages/com_mokojoombackup/src/Model/DashboardModel.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/DashboardModel.php rename to source/packages/com_mokojoombackup/src/Model/DashboardModel.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/ProfileModel.php b/source/packages/com_mokojoombackup/src/Model/ProfileModel.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/ProfileModel.php rename to source/packages/com_mokojoombackup/src/Model/ProfileModel.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/ProfilesModel.php b/source/packages/com_mokojoombackup/src/Model/ProfilesModel.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/ProfilesModel.php rename to source/packages/com_mokojoombackup/src/Model/ProfilesModel.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/index.html b/source/packages/com_mokojoombackup/src/Model/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Model/index.html rename to source/packages/com_mokojoombackup/src/Model/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Table/BackupTable.php b/source/packages/com_mokojoombackup/src/Table/BackupTable.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Table/BackupTable.php rename to source/packages/com_mokojoombackup/src/Table/BackupTable.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Table/ProfileTable.php b/source/packages/com_mokojoombackup/src/Table/ProfileTable.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Table/ProfileTable.php rename to source/packages/com_mokojoombackup/src/Table/ProfileTable.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/Table/index.html b/source/packages/com_mokojoombackup/src/Table/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/Table/index.html rename to source/packages/com_mokojoombackup/src/Table/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Backup/HtmlView.php b/source/packages/com_mokojoombackup/src/View/Backup/HtmlView.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Backup/HtmlView.php rename to source/packages/com_mokojoombackup/src/View/Backup/HtmlView.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Backup/index.html b/source/packages/com_mokojoombackup/src/View/Backup/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Backup/index.html rename to source/packages/com_mokojoombackup/src/View/Backup/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Backups/HtmlView.php b/source/packages/com_mokojoombackup/src/View/Backups/HtmlView.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Backups/HtmlView.php rename to source/packages/com_mokojoombackup/src/View/Backups/HtmlView.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Backups/index.html b/source/packages/com_mokojoombackup/src/View/Backups/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Backups/index.html rename to source/packages/com_mokojoombackup/src/View/Backups/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Dashboard/HtmlView.php b/source/packages/com_mokojoombackup/src/View/Dashboard/HtmlView.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Dashboard/HtmlView.php rename to source/packages/com_mokojoombackup/src/View/Dashboard/HtmlView.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Profile/HtmlView.php b/source/packages/com_mokojoombackup/src/View/Profile/HtmlView.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Profile/HtmlView.php rename to source/packages/com_mokojoombackup/src/View/Profile/HtmlView.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Profile/index.html b/source/packages/com_mokojoombackup/src/View/Profile/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Profile/index.html rename to source/packages/com_mokojoombackup/src/View/Profile/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Profiles/HtmlView.php b/source/packages/com_mokojoombackup/src/View/Profiles/HtmlView.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Profiles/HtmlView.php rename to source/packages/com_mokojoombackup/src/View/Profiles/HtmlView.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Profiles/index.html b/source/packages/com_mokojoombackup/src/View/Profiles/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/Profiles/index.html rename to source/packages/com_mokojoombackup/src/View/Profiles/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/View/index.html b/source/packages/com_mokojoombackup/src/View/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/View/index.html rename to source/packages/com_mokojoombackup/src/View/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/src/index.html b/source/packages/com_mokojoombackup/src/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/src/index.html rename to source/packages/com_mokojoombackup/src/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backup/default.php b/source/packages/com_mokojoombackup/tmpl/backup/default.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backup/default.php rename to source/packages/com_mokojoombackup/tmpl/backup/default.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backup/index.html b/source/packages/com_mokojoombackup/tmpl/backup/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backup/index.html rename to source/packages/com_mokojoombackup/tmpl/backup/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backups/default.php b/source/packages/com_mokojoombackup/tmpl/backups/default.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backups/default.php rename to source/packages/com_mokojoombackup/tmpl/backups/default.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backups/index.html b/source/packages/com_mokojoombackup/tmpl/backups/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/backups/index.html rename to source/packages/com_mokojoombackup/tmpl/backups/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/dashboard/default.php b/source/packages/com_mokojoombackup/tmpl/dashboard/default.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/dashboard/default.php rename to source/packages/com_mokojoombackup/tmpl/dashboard/default.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/index.html b/source/packages/com_mokojoombackup/tmpl/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/index.html rename to source/packages/com_mokojoombackup/tmpl/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profile/edit.php b/source/packages/com_mokojoombackup/tmpl/profile/edit.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profile/edit.php rename to source/packages/com_mokojoombackup/tmpl/profile/edit.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profile/index.html b/source/packages/com_mokojoombackup/tmpl/profile/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profile/index.html rename to source/packages/com_mokojoombackup/tmpl/profile/index.html diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profiles/default.php b/source/packages/com_mokojoombackup/tmpl/profiles/default.php similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profiles/default.php rename to source/packages/com_mokojoombackup/tmpl/profiles/default.php diff --git a/source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profiles/index.html b/source/packages/com_mokojoombackup/tmpl/profiles/index.html similarity index 100% rename from source/packages/com_mokojoombackup/com_mokojoombackup/tmpl/profiles/index.html rename to source/packages/com_mokojoombackup/tmpl/profiles/index.html diff --git a/source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/services/provider.php b/source/packages/plg_actionlog_mokojoombackup/services/provider.php similarity index 100% rename from source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/services/provider.php rename to source/packages/plg_actionlog_mokojoombackup/services/provider.php diff --git a/source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/src/Extension/MokoBackupActionlog.php b/source/packages/plg_actionlog_mokojoombackup/src/Extension/MokoBackupActionlog.php similarity index 100% rename from source/packages/plg_actionlog_mokojoombackup/plg_actionlog_mokojoombackup/src/Extension/MokoBackupActionlog.php rename to source/packages/plg_actionlog_mokojoombackup/src/Extension/MokoBackupActionlog.php diff --git a/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/services/provider.php b/source/packages/plg_console_mokojoombackup/services/provider.php similarity index 100% rename from source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/services/provider.php rename to source/packages/plg_console_mokojoombackup/services/provider.php diff --git a/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/CleanupCommand.php b/source/packages/plg_console_mokojoombackup/src/Command/CleanupCommand.php similarity index 100% rename from source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/CleanupCommand.php rename to source/packages/plg_console_mokojoombackup/src/Command/CleanupCommand.php diff --git a/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ListCommand.php b/source/packages/plg_console_mokojoombackup/src/Command/ListCommand.php similarity index 100% rename from source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ListCommand.php rename to source/packages/plg_console_mokojoombackup/src/Command/ListCommand.php diff --git a/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ProfilesCommand.php b/source/packages/plg_console_mokojoombackup/src/Command/ProfilesCommand.php similarity index 100% rename from source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/ProfilesCommand.php rename to source/packages/plg_console_mokojoombackup/src/Command/ProfilesCommand.php diff --git a/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RestoreCommand.php b/source/packages/plg_console_mokojoombackup/src/Command/RestoreCommand.php similarity index 100% rename from source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RestoreCommand.php rename to source/packages/plg_console_mokojoombackup/src/Command/RestoreCommand.php diff --git a/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RunCommand.php b/source/packages/plg_console_mokojoombackup/src/Command/RunCommand.php similarity index 100% rename from source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Command/RunCommand.php rename to source/packages/plg_console_mokojoombackup/src/Command/RunCommand.php diff --git a/source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Extension/MokoBackupConsole.php b/source/packages/plg_console_mokojoombackup/src/Extension/MokoBackupConsole.php similarity index 100% rename from source/packages/plg_console_mokojoombackup/plg_console_mokojoombackup/src/Extension/MokoBackupConsole.php rename to source/packages/plg_console_mokojoombackup/src/Extension/MokoBackupConsole.php diff --git a/source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/services/provider.php b/source/packages/plg_content_mokojoombackup/services/provider.php similarity index 100% rename from source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/services/provider.php rename to source/packages/plg_content_mokojoombackup/services/provider.php diff --git a/source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/src/Extension/MokoBackupContent.php b/source/packages/plg_content_mokojoombackup/src/Extension/MokoBackupContent.php similarity index 100% rename from source/packages/plg_content_mokojoombackup/plg_content_mokojoombackup/src/Extension/MokoBackupContent.php rename to source/packages/plg_content_mokojoombackup/src/Extension/MokoBackupContent.php diff --git a/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/index.html b/source/packages/plg_quickicon_mokojoombackup/index.html similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/index.html rename to source/packages/plg_quickicon_mokojoombackup/index.html diff --git a/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/en-GB/index.html b/source/packages/plg_quickicon_mokojoombackup/language/en-GB/index.html similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/en-GB/index.html rename to source/packages/plg_quickicon_mokojoombackup/language/en-GB/index.html diff --git a/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/en-US/index.html b/source/packages/plg_quickicon_mokojoombackup/language/en-US/index.html similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/en-US/index.html rename to source/packages/plg_quickicon_mokojoombackup/language/en-US/index.html diff --git a/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/index.html b/source/packages/plg_quickicon_mokojoombackup/language/index.html similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/language/index.html rename to source/packages/plg_quickicon_mokojoombackup/language/index.html diff --git a/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/services/index.html b/source/packages/plg_quickicon_mokojoombackup/services/index.html similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/services/index.html rename to source/packages/plg_quickicon_mokojoombackup/services/index.html diff --git a/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/services/provider.php b/source/packages/plg_quickicon_mokojoombackup/services/provider.php similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/services/provider.php rename to source/packages/plg_quickicon_mokojoombackup/services/provider.php diff --git a/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/Extension/MokoBackupQuickicon.php b/source/packages/plg_quickicon_mokojoombackup/src/Extension/MokoBackupQuickicon.php similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/Extension/MokoBackupQuickicon.php rename to source/packages/plg_quickicon_mokojoombackup/src/Extension/MokoBackupQuickicon.php diff --git a/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/Extension/index.html b/source/packages/plg_quickicon_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/Extension/index.html rename to source/packages/plg_quickicon_mokojoombackup/src/Extension/index.html diff --git a/source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/index.html b/source/packages/plg_quickicon_mokojoombackup/src/index.html similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/plg_quickicon_mokojoombackup/src/index.html rename to source/packages/plg_quickicon_mokojoombackup/src/index.html diff --git a/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/index.html b/source/packages/plg_system_mokojoombackup/index.html similarity index 100% rename from source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/index.html rename to source/packages/plg_system_mokojoombackup/index.html diff --git a/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/language/en-GB/index.html b/source/packages/plg_system_mokojoombackup/language/en-GB/index.html similarity index 100% rename from source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/language/en-GB/index.html rename to source/packages/plg_system_mokojoombackup/language/en-GB/index.html diff --git a/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/language/en-US/index.html b/source/packages/plg_system_mokojoombackup/language/en-US/index.html similarity index 100% rename from source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/language/en-US/index.html rename to source/packages/plg_system_mokojoombackup/language/en-US/index.html diff --git a/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/language/index.html b/source/packages/plg_system_mokojoombackup/language/index.html similarity index 100% rename from source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/language/index.html rename to source/packages/plg_system_mokojoombackup/language/index.html diff --git a/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/services/index.html b/source/packages/plg_system_mokojoombackup/services/index.html similarity index 100% rename from source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/services/index.html rename to source/packages/plg_system_mokojoombackup/services/index.html diff --git a/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/services/provider.php b/source/packages/plg_system_mokojoombackup/services/provider.php similarity index 100% rename from source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/services/provider.php rename to source/packages/plg_system_mokojoombackup/services/provider.php diff --git a/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/Extension/MokoBackup.php b/source/packages/plg_system_mokojoombackup/src/Extension/MokoBackup.php similarity index 100% rename from source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/Extension/MokoBackup.php rename to source/packages/plg_system_mokojoombackup/src/Extension/MokoBackup.php diff --git a/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/Extension/index.html b/source/packages/plg_system_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/Extension/index.html rename to source/packages/plg_system_mokojoombackup/src/Extension/index.html diff --git a/source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/index.html b/source/packages/plg_system_mokojoombackup/src/index.html similarity index 100% rename from source/packages/plg_system_mokojoombackup/plg_system_mokojoombackup/src/index.html rename to source/packages/plg_system_mokojoombackup/src/index.html diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/forms/index.html b/source/packages/plg_task_mokojoombackup/forms/index.html similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/forms/index.html rename to source/packages/plg_task_mokojoombackup/forms/index.html diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/forms/run_profile.xml b/source/packages/plg_task_mokojoombackup/forms/run_profile.xml similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/forms/run_profile.xml rename to source/packages/plg_task_mokojoombackup/forms/run_profile.xml diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/index.html b/source/packages/plg_task_mokojoombackup/index.html similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/index.html rename to source/packages/plg_task_mokojoombackup/index.html diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/en-GB/index.html b/source/packages/plg_task_mokojoombackup/language/en-GB/index.html similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/en-GB/index.html rename to source/packages/plg_task_mokojoombackup/language/en-GB/index.html diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/en-US/index.html b/source/packages/plg_task_mokojoombackup/language/en-US/index.html similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/en-US/index.html rename to source/packages/plg_task_mokojoombackup/language/en-US/index.html diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/index.html b/source/packages/plg_task_mokojoombackup/language/index.html similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/language/index.html rename to source/packages/plg_task_mokojoombackup/language/index.html diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/services/index.html b/source/packages/plg_task_mokojoombackup/services/index.html similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/services/index.html rename to source/packages/plg_task_mokojoombackup/services/index.html diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/services/provider.php b/source/packages/plg_task_mokojoombackup/services/provider.php similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/services/provider.php rename to source/packages/plg_task_mokojoombackup/services/provider.php diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/Extension/MokoBackupTask.php b/source/packages/plg_task_mokojoombackup/src/Extension/MokoBackupTask.php similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/Extension/MokoBackupTask.php rename to source/packages/plg_task_mokojoombackup/src/Extension/MokoBackupTask.php diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/Extension/index.html b/source/packages/plg_task_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/Extension/index.html rename to source/packages/plg_task_mokojoombackup/src/Extension/index.html diff --git a/source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/index.html b/source/packages/plg_task_mokojoombackup/src/index.html similarity index 100% rename from source/packages/plg_task_mokojoombackup/plg_task_mokojoombackup/src/index.html rename to source/packages/plg_task_mokojoombackup/src/index.html diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/index.html b/source/packages/plg_webservices_mokojoombackup/index.html similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/index.html rename to source/packages/plg_webservices_mokojoombackup/index.html diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/en-GB/index.html b/source/packages/plg_webservices_mokojoombackup/language/en-GB/index.html similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/en-GB/index.html rename to source/packages/plg_webservices_mokojoombackup/language/en-GB/index.html diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/en-US/index.html b/source/packages/plg_webservices_mokojoombackup/language/en-US/index.html similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/en-US/index.html rename to source/packages/plg_webservices_mokojoombackup/language/en-US/index.html diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/index.html b/source/packages/plg_webservices_mokojoombackup/language/index.html similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/language/index.html rename to source/packages/plg_webservices_mokojoombackup/language/index.html diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/index.html b/source/packages/plg_webservices_mokojoombackup/services/index.html similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/index.html rename to source/packages/plg_webservices_mokojoombackup/services/index.html diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/provider.php b/source/packages/plg_webservices_mokojoombackup/services/provider.php similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/services/provider.php rename to source/packages/plg_webservices_mokojoombackup/services/provider.php diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/Extension/MokoBackupWebServices.php b/source/packages/plg_webservices_mokojoombackup/src/Extension/MokoBackupWebServices.php similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/Extension/MokoBackupWebServices.php rename to source/packages/plg_webservices_mokojoombackup/src/Extension/MokoBackupWebServices.php diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/Extension/index.html b/source/packages/plg_webservices_mokojoombackup/src/Extension/index.html similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/Extension/index.html rename to source/packages/plg_webservices_mokojoombackup/src/Extension/index.html diff --git a/source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/index.html b/source/packages/plg_webservices_mokojoombackup/src/index.html similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/plg_webservices_mokojoombackup/src/index.html rename to source/packages/plg_webservices_mokojoombackup/src/index.html -- 2.52.0 From f03bb54d4a212f171c2100e154c6b407327c52a1 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 20:11:23 +0000 Subject: [PATCH 61/81] chore(version): pre-release bump to 01.01.11-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_console_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_content_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_system_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_task_mokojoombackup/mokojoombackup.xml | 2 +- .../packages/plg_webservices_mokojoombackup/mokojoombackup.xml | 2 +- source/pkg_mokojoombackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index a419469..37b60af 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.10-dev + 01.01.11-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 5081974..e45db7f 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.10 +# VERSION: 01.01.11 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index 263bb7a..d27af5b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokojoombackup/mokojoombackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml index 590e349..8a42d59 100644 --- a/source/packages/com_mokojoombackup/mokojoombackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> com_mokojoombackup - 01.01.10-dev + 01.01.11-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index 62f2c0b..39e72f2 100644 --- a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokojoombackup - 01.01.10-dev + 01.01.11-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml index 73ae765..696b96f 100644 --- a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokojoombackup - 01.01.10-dev + 01.01.11-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml index af5ed97..c1ae867 100644 --- a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokojoombackup - 01.01.10-dev + 01.01.11-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index b353b34..69b926a 100644 --- a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokojoombackup - 01.01.10-dev + 01.01.11-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml index cdbf066..5e7f81b 100644 --- a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokojoombackup - 01.01.10-dev + 01.01.11-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml index 6322cf2..383b1bc 100644 --- a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokojoombackup - 01.01.10-dev + 01.01.11-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index b7e97b7..cda4f15 100644 --- a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokojoombackup - 01.01.10-dev + 01.01.11-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokojoombackup.xml b/source/pkg_mokojoombackup.xml index b5980ca..62151a6 100644 --- a/source/pkg_mokojoombackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokojoombackup - 01.01.10-dev + 01.01.11-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From fae2ea5567418af3fd17700d4cfc25785111a53b Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 20:15:02 +0000 Subject: [PATCH 62/81] chore(version): pre-release bump to 01.01.12-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_console_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_content_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_system_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_task_mokojoombackup/mokojoombackup.xml | 2 +- .../packages/plg_webservices_mokojoombackup/mokojoombackup.xml | 2 +- source/pkg_mokojoombackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 37b60af..8257d09 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.11-dev + 01.01.12-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index e45db7f..6e47733 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.11 +# VERSION: 01.01.12 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index d27af5b..8d5cd42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokojoombackup/mokojoombackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml index 8a42d59..8a58561 100644 --- a/source/packages/com_mokojoombackup/mokojoombackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> com_mokojoombackup - 01.01.11-dev + 01.01.12-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index 39e72f2..296645a 100644 --- a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokojoombackup - 01.01.11-dev + 01.01.12-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml index 696b96f..d47b941 100644 --- a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokojoombackup - 01.01.11-dev + 01.01.12-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml index c1ae867..6cd4696 100644 --- a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokojoombackup - 01.01.11-dev + 01.01.12-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index 69b926a..3c926c5 100644 --- a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokojoombackup - 01.01.11-dev + 01.01.12-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml index 5e7f81b..799f342 100644 --- a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokojoombackup - 01.01.11-dev + 01.01.12-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml index 383b1bc..dac1262 100644 --- a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokojoombackup - 01.01.11-dev + 01.01.12-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index cda4f15..537c6c0 100644 --- a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokojoombackup - 01.01.11-dev + 01.01.12-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokojoombackup.xml b/source/pkg_mokojoombackup.xml index 62151a6..58b68bc 100644 --- a/source/pkg_mokojoombackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokojoombackup - 01.01.11-dev + 01.01.12-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 9975d92ca21a9549406230e7fc8b6c9f2d96f1b9 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 15:36:43 -0500 Subject: [PATCH 63/81] feat: web cron trigger for shared hosting without crontab Add URL-based backup trigger for external cron services: index.php?mokojoombackup_cron=SECRET&profile_id=1 - System plugin intercepts onAfterInitialise (before auth) - Secret word, enable toggle, and IP whitelist in component config - Timing-safe secret comparison with hash_equals - Returns JSON response with success/failure and timestamp Also fix: INSERT IGNORE for default profile to prevent duplicate key error when updating an existing installation. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- source/packages/com_mokojoombackup/config.xml | 32 ++++++ .../language/en-GB/com_mokojoombackup.ini | 9 ++ .../com_mokojoombackup/sql/install.mysql.sql | 4 +- .../src/Extension/MokoBackup.php | 97 ++++++++++++++++++- updates.xml | 9 ++ 5 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 updates.xml diff --git a/source/packages/com_mokojoombackup/config.xml b/source/packages/com_mokojoombackup/config.xml index 089c63b..98c5a0d 100644 --- a/source/packages/com_mokojoombackup/config.xml +++ b/source/packages/com_mokojoombackup/config.xml @@ -39,6 +39,38 @@ +
    + + + + + + +
    +
    'onAfterRoute', + 'onAfterInitialise' => 'onAfterInitialise', + 'onAfterRoute' => 'onAfterRoute', ]; } + /** + * Web cron trigger — runs before routing so no authentication is needed. + * + * URL: index.php?mokojoombackup_cron=SECRET&profile_id=1 + * + * External cron services (cron-job.org, UptimeRobot, etc.) can call this + * URL on a schedule to trigger backups on shared hosting without crontab. + */ + public function onAfterInitialise(Event $event): void + { + $app = $this->getApplication(); + $secret = $app->input->getString('mokojoombackup_cron', ''); + + if ($secret === '') { + return; + } + + // Load component params + $params = ComponentHelper::getParams('com_mokojoombackup'); + $enabled = (int) $params->get('webcron_enabled', 0); + $configSecret = trim($params->get('webcron_secret', '')); + $ipWhitelist = trim($params->get('webcron_ip_whitelist', '')); + + // Reject if disabled or no secret configured + if (!$enabled || $configSecret === '') { + $this->sendJsonResponse(false, 'Web cron is not enabled', 403); + } + + // Validate secret (timing-safe comparison) + if (!hash_equals($configSecret, $secret)) { + $this->sendJsonResponse(false, 'Invalid secret', 403); + } + + // IP whitelist check (if configured) + if ($ipWhitelist !== '') { + $allowedIps = array_map('trim', explode(',', $ipWhitelist)); + $clientIp = $app->input->server->getString('REMOTE_ADDR', ''); + + if (!in_array($clientIp, $allowedIps, true)) { + $this->sendJsonResponse(false, 'IP not allowed', 403); + } + } + + // Determine profile + $profileId = $app->input->getInt('profile_id', (int) $params->get('default_profile', 1)); + + // Override PHP limits + @set_time_limit(0); + @ini_set('max_execution_time', '0'); + @ini_set('memory_limit', '512M'); + + try { + $engine = new BackupEngine(); + $result = $engine->run($profileId, 'Web cron backup', 'webcron'); + + $this->sendJsonResponse( + $result['success'], + $result['message'], + $result['success'] ? 200 : 500 + ); + } catch (\Throwable $e) { + $this->sendJsonResponse(false, 'Backup failed: ' . $e->getMessage(), 500); + } + } + /** * Cleanup expired backups on admin page loads (lightweight check). */ @@ -35,6 +103,11 @@ final class MokoJoomBackup extends CMSPlugin implements SubscriberInterface { $app = $this->getApplication(); + // Skip if this is a web cron request (already handled) + if ($app->input->getString('mokojoombackup_cron', '') !== '') { + return; + } + // Only run in admin, and only on component page loads (not AJAX) if (!$app->isClient('administrator') || $app->input->getCmd('format', 'html') !== 'html') { return; @@ -121,4 +194,26 @@ final class MokoJoomBackup extends CMSPlugin implements SubscriberInterface } } } + + /** + * Send a JSON response and terminate — used by web cron handler. + */ + private function sendJsonResponse(bool $success, string $message, int $httpCode = 200): void + { + while (@ob_end_clean()) { + // flush buffers + } + + http_response_code($httpCode); + header('Content-Type: application/json; charset=utf-8'); + header('Cache-Control: no-cache, no-store, must-revalidate'); + + echo json_encode([ + 'success' => $success, + 'message' => $message, + 'timestamp' => date('Y-m-d H:i:s'), + ]); + + $this->getApplication()->close(); + } } diff --git a/updates.xml b/updates.xml new file mode 100644 index 0000000..77c090b --- /dev/null +++ b/updates.xml @@ -0,0 +1,9 @@ + + + + + -- 2.52.0 From 4d64459b9672c0ffc2d80017a1ebfddee5ece870 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 20:38:36 +0000 Subject: [PATCH 64/81] chore(version): pre-release bump to 01.01.13-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_console_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_content_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_system_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_task_mokojoombackup/mokojoombackup.xml | 2 +- .../packages/plg_webservices_mokojoombackup/mokojoombackup.xml | 2 +- source/pkg_mokojoombackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 8257d09..1e4b717 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.12-dev + 01.01.13-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 6e47733..87045d1 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.12 +# VERSION: 01.01.13 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index 8d5cd42..75f2f27 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokojoombackup/mokojoombackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml index 8a58561..ab9c5d6 100644 --- a/source/packages/com_mokojoombackup/mokojoombackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> com_mokojoombackup - 01.01.12-dev + 01.01.13-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index 296645a..f7c5221 100644 --- a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokojoombackup - 01.01.12-dev + 01.01.13-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml index d47b941..9beb0d9 100644 --- a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokojoombackup - 01.01.12-dev + 01.01.13-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml index 6cd4696..6fc22d2 100644 --- a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokojoombackup - 01.01.12-dev + 01.01.13-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index 3c926c5..9aa2033 100644 --- a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokojoombackup - 01.01.12-dev + 01.01.13-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml index 799f342..d763711 100644 --- a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokojoombackup - 01.01.12-dev + 01.01.13-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml index dac1262..7d1cdef 100644 --- a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokojoombackup - 01.01.12-dev + 01.01.13-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index 537c6c0..d4f1583 100644 --- a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokojoombackup - 01.01.12-dev + 01.01.13-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokojoombackup.xml b/source/pkg_mokojoombackup.xml index 58b68bc..75ed447 100644 --- a/source/pkg_mokojoombackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokojoombackup - 01.01.12-dev + 01.01.13-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 95cf065264275d5be6466a2f6dc922052752cb25 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 15:42:58 -0500 Subject: [PATCH 65/81] fix: rename class files to match PSR-4 class names MokoBackup*.php files renamed to MokoJoomBackup*.php to match the class names inside. Joomla's PSR-4 autoloader requires filename=classname. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../{MokoBackupComponent.php => MokoJoomBackupComponent.php} | 0 .../{MokoBackupActionlog.php => MokoJoomBackupActionlog.php} | 0 .../{MokoBackupConsole.php => MokoJoomBackupConsole.php} | 0 .../{MokoBackupContent.php => MokoJoomBackupContent.php} | 0 .../{MokoBackupQuickicon.php => MokoJoomBackupQuickicon.php} | 0 .../src/Extension/{MokoBackup.php => MokoJoomBackup.php} | 0 .../src/Extension/{MokoBackupTask.php => MokoJoomBackupTask.php} | 0 .../{MokoBackupWebServices.php => MokoJoomBackupWebServices.php} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename source/packages/com_mokojoombackup/src/Extension/{MokoBackupComponent.php => MokoJoomBackupComponent.php} (100%) rename source/packages/plg_actionlog_mokojoombackup/src/Extension/{MokoBackupActionlog.php => MokoJoomBackupActionlog.php} (100%) rename source/packages/plg_console_mokojoombackup/src/Extension/{MokoBackupConsole.php => MokoJoomBackupConsole.php} (100%) rename source/packages/plg_content_mokojoombackup/src/Extension/{MokoBackupContent.php => MokoJoomBackupContent.php} (100%) rename source/packages/plg_quickicon_mokojoombackup/src/Extension/{MokoBackupQuickicon.php => MokoJoomBackupQuickicon.php} (100%) rename source/packages/plg_system_mokojoombackup/src/Extension/{MokoBackup.php => MokoJoomBackup.php} (100%) rename source/packages/plg_task_mokojoombackup/src/Extension/{MokoBackupTask.php => MokoJoomBackupTask.php} (100%) rename source/packages/plg_webservices_mokojoombackup/src/Extension/{MokoBackupWebServices.php => MokoJoomBackupWebServices.php} (100%) diff --git a/source/packages/com_mokojoombackup/src/Extension/MokoBackupComponent.php b/source/packages/com_mokojoombackup/src/Extension/MokoJoomBackupComponent.php similarity index 100% rename from source/packages/com_mokojoombackup/src/Extension/MokoBackupComponent.php rename to source/packages/com_mokojoombackup/src/Extension/MokoJoomBackupComponent.php diff --git a/source/packages/plg_actionlog_mokojoombackup/src/Extension/MokoBackupActionlog.php b/source/packages/plg_actionlog_mokojoombackup/src/Extension/MokoJoomBackupActionlog.php similarity index 100% rename from source/packages/plg_actionlog_mokojoombackup/src/Extension/MokoBackupActionlog.php rename to source/packages/plg_actionlog_mokojoombackup/src/Extension/MokoJoomBackupActionlog.php diff --git a/source/packages/plg_console_mokojoombackup/src/Extension/MokoBackupConsole.php b/source/packages/plg_console_mokojoombackup/src/Extension/MokoJoomBackupConsole.php similarity index 100% rename from source/packages/plg_console_mokojoombackup/src/Extension/MokoBackupConsole.php rename to source/packages/plg_console_mokojoombackup/src/Extension/MokoJoomBackupConsole.php diff --git a/source/packages/plg_content_mokojoombackup/src/Extension/MokoBackupContent.php b/source/packages/plg_content_mokojoombackup/src/Extension/MokoJoomBackupContent.php similarity index 100% rename from source/packages/plg_content_mokojoombackup/src/Extension/MokoBackupContent.php rename to source/packages/plg_content_mokojoombackup/src/Extension/MokoJoomBackupContent.php diff --git a/source/packages/plg_quickicon_mokojoombackup/src/Extension/MokoBackupQuickicon.php b/source/packages/plg_quickicon_mokojoombackup/src/Extension/MokoJoomBackupQuickicon.php similarity index 100% rename from source/packages/plg_quickicon_mokojoombackup/src/Extension/MokoBackupQuickicon.php rename to source/packages/plg_quickicon_mokojoombackup/src/Extension/MokoJoomBackupQuickicon.php diff --git a/source/packages/plg_system_mokojoombackup/src/Extension/MokoBackup.php b/source/packages/plg_system_mokojoombackup/src/Extension/MokoJoomBackup.php similarity index 100% rename from source/packages/plg_system_mokojoombackup/src/Extension/MokoBackup.php rename to source/packages/plg_system_mokojoombackup/src/Extension/MokoJoomBackup.php diff --git a/source/packages/plg_task_mokojoombackup/src/Extension/MokoBackupTask.php b/source/packages/plg_task_mokojoombackup/src/Extension/MokoJoomBackupTask.php similarity index 100% rename from source/packages/plg_task_mokojoombackup/src/Extension/MokoBackupTask.php rename to source/packages/plg_task_mokojoombackup/src/Extension/MokoJoomBackupTask.php diff --git a/source/packages/plg_webservices_mokojoombackup/src/Extension/MokoBackupWebServices.php b/source/packages/plg_webservices_mokojoombackup/src/Extension/MokoJoomBackupWebServices.php similarity index 100% rename from source/packages/plg_webservices_mokojoombackup/src/Extension/MokoBackupWebServices.php rename to source/packages/plg_webservices_mokojoombackup/src/Extension/MokoJoomBackupWebServices.php -- 2.52.0 From 0225baa53de3e7a02a0a9675a80a55217518932e Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 20:44:26 +0000 Subject: [PATCH 66/81] chore(version): pre-release bump to 01.01.14-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_console_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_content_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_system_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_task_mokojoombackup/mokojoombackup.xml | 2 +- .../packages/plg_webservices_mokojoombackup/mokojoombackup.xml | 2 +- source/pkg_mokojoombackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 1e4b717..7dca158 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.13-dev + 01.01.14-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 87045d1..8bfab41 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.13 +# VERSION: 01.01.14 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index 75f2f27..30d7ae1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokojoombackup/mokojoombackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml index ab9c5d6..506f1f5 100644 --- a/source/packages/com_mokojoombackup/mokojoombackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> com_mokojoombackup - 01.01.13-dev + 01.01.14-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index f7c5221..c5a2250 100644 --- a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokojoombackup - 01.01.13-dev + 01.01.14-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml index 9beb0d9..559d297 100644 --- a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokojoombackup - 01.01.13-dev + 01.01.14-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml index 6fc22d2..6b7ca1f 100644 --- a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokojoombackup - 01.01.13-dev + 01.01.14-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index 9aa2033..b951b3b 100644 --- a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokojoombackup - 01.01.13-dev + 01.01.14-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml index d763711..adbf04d 100644 --- a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokojoombackup - 01.01.13-dev + 01.01.14-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml index 7d1cdef..dfd6709 100644 --- a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokojoombackup - 01.01.13-dev + 01.01.14-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index d4f1583..a99f5e6 100644 --- a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokojoombackup - 01.01.13-dev + 01.01.14-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokojoombackup.xml b/source/pkg_mokojoombackup.xml index 75ed447..617c302 100644 --- a/source/pkg_mokojoombackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokojoombackup - 01.01.13-dev + 01.01.14-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 2e1ec426cefc559dc2d1034b8332018b61555fcd Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 20:46:31 +0000 Subject: [PATCH 67/81] chore(version): pre-release bump to 01.01.15-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_console_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_content_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_system_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_task_mokojoombackup/mokojoombackup.xml | 2 +- .../packages/plg_webservices_mokojoombackup/mokojoombackup.xml | 2 +- source/pkg_mokojoombackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 7dca158..d1d2cfc 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.14-dev + 01.01.15-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 8bfab41..76b193f 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.14 +# VERSION: 01.01.15 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index 30d7ae1..fc0d4d1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokojoombackup/mokojoombackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml index 506f1f5..0cc6065 100644 --- a/source/packages/com_mokojoombackup/mokojoombackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> com_mokojoombackup - 01.01.14-dev + 01.01.15-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index c5a2250..3ce6ed0 100644 --- a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokojoombackup - 01.01.14-dev + 01.01.15-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml index 559d297..ca5a76a 100644 --- a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokojoombackup - 01.01.14-dev + 01.01.15-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml index 6b7ca1f..142a118 100644 --- a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokojoombackup - 01.01.14-dev + 01.01.15-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index b951b3b..23d8ecb 100644 --- a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokojoombackup - 01.01.14-dev + 01.01.15-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml index adbf04d..9bdca6a 100644 --- a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokojoombackup - 01.01.14-dev + 01.01.15-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml index dfd6709..4a5eb2f 100644 --- a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokojoombackup - 01.01.14-dev + 01.01.15-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index a99f5e6..4957940 100644 --- a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokojoombackup - 01.01.14-dev + 01.01.15-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokojoombackup.xml b/source/pkg_mokojoombackup.xml index 617c302..a772628 100644 --- a/source/pkg_mokojoombackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokojoombackup - 01.01.14-dev + 01.01.15-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 67329507f43d58139a718df13a92e2740be53938 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 16:32:28 -0500 Subject: [PATCH 68/81] feat: beforeunload warning during backup, placeholder-aware directory checks - Browser warns before closing/navigating while backup is in progress - FolderPickerField shows info status for paths with [placeholders] instead of "Directory not found" - Dashboard health check skips filesystem check when backup_dir uses placeholders (resolved at backup time by PlaceholderResolver) Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../language/en-GB/com_mokojoombackup.ini | 1 + .../src/Field/FolderPickerField.php | 22 +++-- .../src/Model/DashboardModel.php | 21 +++-- .../tmpl/backups/default.php | 13 +++ .../tmpl/dashboard/default.php | 8 ++ source/script.php | 89 +++++++++++++++++++ 6 files changed, 142 insertions(+), 12 deletions(-) diff --git a/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.ini b/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.ini index f192354..5c8ea6c 100644 --- a/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.ini +++ b/source/packages/com_mokojoombackup/language/en-GB/com_mokojoombackup.ini @@ -252,6 +252,7 @@ COM_MOKOJOOMBACKUP_CONFIG_WEBCRON_IP_DESC="Comma-separated list of IP addresses ; Folder picker COM_MOKOJOOMBACKUP_FOLDER_EXISTS="Directory exists" COM_MOKOJOOMBACKUP_FOLDER_NOT_FOUND="Directory not found" +COM_MOKOJOOMBACKUP_FOLDER_PLACEHOLDER="Uses placeholders (resolved at backup time)" COM_MOKOJOOMBACKUP_BACKUP_DIR_DEFAULT="Default (inside web root)" ; Exclude fields diff --git a/source/packages/com_mokojoombackup/src/Field/FolderPickerField.php b/source/packages/com_mokojoombackup/src/Field/FolderPickerField.php index 6e60dff..3cb0603 100644 --- a/source/packages/com_mokojoombackup/src/Field/FolderPickerField.php +++ b/source/packages/com_mokojoombackup/src/Field/FolderPickerField.php @@ -35,12 +35,22 @@ class FolderPickerField extends FormField $absPath = $rawValue; } - $exists = is_dir($absPath); - $statusClass = $exists ? 'text-success' : 'text-danger'; - $statusIcon = $exists ? 'icon-publish' : 'icon-unpublish'; - $statusText = $exists - ? Text::_('COM_MOKOJOOMBACKUP_FOLDER_EXISTS') - : Text::_('COM_MOKOJOOMBACKUP_FOLDER_NOT_FOUND'); + // If path contains placeholders, show info status instead of checking disk + $hasPlaceholders = preg_match('/\[.+\]/', $absPath); + + if ($hasPlaceholders) { + $statusClass = 'text-info'; + $statusIcon = 'icon-info-circle'; + $statusText = Text::_('COM_MOKOJOOMBACKUP_FOLDER_PLACEHOLDER'); + } else { + $exists = is_dir($absPath); + $statusClass = $exists ? 'text-success' : 'text-danger'; + $statusIcon = $exists ? 'icon-publish' : 'icon-unpublish'; + $statusText = $exists + ? Text::_('COM_MOKOJOOMBACKUP_FOLDER_EXISTS') + : Text::_('COM_MOKOJOOMBACKUP_FOLDER_NOT_FOUND'); + } + $absPathSafe = htmlspecialchars($absPath, ENT_QUOTES, 'UTF-8'); return << 'Backup Directory', - 'status' => $writable, - 'detail' => ($writable ? 'Writable' : 'Not writable or missing') . ' — ' . $backupDir, - ]; + // Skip filesystem check if path contains placeholders (resolved at backup time) + if (preg_match('/\[.+\]/', $backupDir)) { + $checks[] = (object) [ + 'label' => 'Backup Directory', + 'status' => true, + 'detail' => 'Uses placeholders (resolved at backup time) — ' . $backupDir, + ]; + } else { + $writable = is_dir($backupDir) && is_writable($backupDir); + $checks[] = (object) [ + 'label' => 'Backup Directory', + 'status' => $writable, + 'detail' => ($writable ? 'Writable' : 'Not writable or missing') . ' — ' . $backupDir, + ]; + } // Disk space $freeSpace = @disk_free_space($backupDir ?: JPATH_ROOT); diff --git a/source/packages/com_mokojoombackup/tmpl/backups/default.php b/source/packages/com_mokojoombackup/tmpl/backups/default.php index 233852d..fa79ddc 100644 --- a/source/packages/com_mokojoombackup/tmpl/backups/default.php +++ b/source/packages/com_mokojoombackup/tmpl/backups/default.php @@ -198,11 +198,24 @@ $listDirn = $this->escape($this->state->get('list.direction')); } }); + var backupRunning = false; + + function warnBeforeClose(e) { + if (backupRunning) { + e.preventDefault(); + e.returnValue = ''; + } + } + + window.addEventListener('beforeunload', warnBeforeClose); + function showModal() { + backupRunning = true; document.getElementById('mokojoombackup-modal').style.display = 'block'; } function hideModal() { + backupRunning = false; document.getElementById('mokojoombackup-modal').style.display = 'none'; } diff --git a/source/packages/com_mokojoombackup/tmpl/dashboard/default.php b/source/packages/com_mokojoombackup/tmpl/dashboard/default.php index d6c810a..cde9eeb 100644 --- a/source/packages/com_mokojoombackup/tmpl/dashboard/default.php +++ b/source/packages/com_mokojoombackup/tmpl/dashboard/default.php @@ -202,11 +202,19 @@ document.querySelectorAll('.mb-tile').forEach(function(tile) { const AJAX_URL = ; const TOKEN_NAME = ; + var backupRunning = false; + + window.addEventListener('beforeunload', function(e) { + if (backupRunning) { e.preventDefault(); e.returnValue = ''; } + }); + function showModal() { + backupRunning = true; document.getElementById('mokojoombackup-modal').style.display = 'block'; } function hideModal() { + backupRunning = false; document.getElementById('mokojoombackup-modal').style.display = 'none'; } diff --git a/source/script.php b/source/script.php index 017b868..972476d 100644 --- a/source/script.php +++ b/source/script.php @@ -49,6 +49,11 @@ class Pkg_MokoJoomBackupInstallerScript return false; } + // Save download key before Joomla re-registers the update site + if ($type === 'update') { + $this->preflight_saveKey(); + } + return true; } @@ -60,8 +65,53 @@ class Pkg_MokoJoomBackupInstallerScript * * @return void */ + /** + * Called before install/update to preserve the download key. + * + * Joomla re-registers update sites from the manifest on every update, + * which can reset the extra_query (download key). We save it here + * and restore it in postflight. + */ + private ?string $savedDownloadKey = null; + + public function preflight_saveKey(): void + { + try { + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('us.extra_query')) + ->from($db->quoteName('#__update_sites', 'us')) + ->join( + 'INNER', + $db->quoteName('#__update_sites_extensions', 'use') + . ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id') + ) + ->join( + 'INNER', + $db->quoteName('#__extensions', 'e') + . ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id') + ) + ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokojoombackup')) + ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) + ->setLimit(1); + $db->setQuery($query); + $key = $db->loadResult(); + + if (!empty($key)) { + $this->savedDownloadKey = $key; + } + } catch (\Throwable $e) { + // Not critical + } + } + public function postflight(string $type, InstallerAdapter $parent): void { + // Restore download key if it was saved before update + if ($this->savedDownloadKey !== null) { + $this->restoreDownloadKey(); + } + if ($type === 'install') { // Enable the system plugin automatically on fresh install $db = Factory::getDbo(); @@ -157,6 +207,45 @@ class Pkg_MokoJoomBackupInstallerScript $this->showUpdateSiteNotice(); } + /** + * Restore the download key to the (possibly new) update site record. + */ + private function restoreDownloadKey(): void + { + try { + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('us.update_site_id')) + ->from($db->quoteName('#__update_sites', 'us')) + ->join( + 'INNER', + $db->quoteName('#__update_sites_extensions', 'use') + . ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id') + ) + ->join( + 'INNER', + $db->quoteName('#__extensions', 'e') + . ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id') + ) + ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokojoombackup')) + ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) + ->setLimit(1); + $db->setQuery($query); + $updateSiteId = (int) $db->loadResult(); + + if ($updateSiteId > 0) { + $query = $db->getQuery(true) + ->update($db->quoteName('#__update_sites')) + ->set($db->quoteName('extra_query') . ' = ' . $db->quote($this->savedDownloadKey)) + ->where($db->quoteName('update_site_id') . ' = ' . $updateSiteId); + $db->setQuery($query); + $db->execute(); + } + } catch (\Throwable $e) { + // Not critical + } + } + /** * Show an info message linking directly to the update site record * so the user can configure their download key. -- 2.52.0 From e5c6ab5e2ac8d1223c88fe68769224591a75cb17 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 21:35:14 +0000 Subject: [PATCH 69/81] chore(version): pre-release bump to 01.01.16-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_console_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_content_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_system_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_task_mokojoombackup/mokojoombackup.xml | 2 +- .../packages/plg_webservices_mokojoombackup/mokojoombackup.xml | 2 +- source/pkg_mokojoombackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index d1d2cfc..a558764 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.15-dev + 01.01.16-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 76b193f..62aba25 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.15 +# VERSION: 01.01.16 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index fc0d4d1..da00aa8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokojoombackup/mokojoombackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml index 0cc6065..6a6bab6 100644 --- a/source/packages/com_mokojoombackup/mokojoombackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> com_mokojoombackup - 01.01.15-dev + 01.01.16-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index 3ce6ed0..9db1f91 100644 --- a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokojoombackup - 01.01.15-dev + 01.01.16-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml index ca5a76a..14e6622 100644 --- a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokojoombackup - 01.01.15-dev + 01.01.16-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml index 142a118..9ef6182 100644 --- a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokojoombackup - 01.01.15-dev + 01.01.16-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index 23d8ecb..5b2df71 100644 --- a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokojoombackup - 01.01.15-dev + 01.01.16-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml index 9bdca6a..9cab3cd 100644 --- a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokojoombackup - 01.01.15-dev + 01.01.16-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml index 4a5eb2f..bcf3235 100644 --- a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokojoombackup - 01.01.15-dev + 01.01.16-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index 4957940..607d476 100644 --- a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokojoombackup - 01.01.15-dev + 01.01.16-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokojoombackup.xml b/source/pkg_mokojoombackup.xml index a772628..25bce5a 100644 --- a/source/pkg_mokojoombackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokojoombackup - 01.01.15-dev + 01.01.16-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 2b5648101a742694bfcc2df376c815d867de6236 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 16:38:25 -0500 Subject: [PATCH 70/81] fix: suppress license warning when download key is already configured checkUpdateSite() now checks extra_query for dlid= before showing the notice. If a download key is present, no message is displayed. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/View/Backups/HtmlView.php | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/source/packages/com_mokojoombackup/src/View/Backups/HtmlView.php b/source/packages/com_mokojoombackup/src/View/Backups/HtmlView.php index 41b5257..fa2b00b 100644 --- a/source/packages/com_mokojoombackup/src/View/Backups/HtmlView.php +++ b/source/packages/com_mokojoombackup/src/View/Backups/HtmlView.php @@ -60,9 +60,12 @@ class HtmlView extends BaseHtmlView try { $db = Factory::getDbo(); - // Find the update site ID linked to pkg_mokojoombackup + // Find the update site linked to pkg_mokojoombackup $query = $db->getQuery(true) - ->select($db->quoteName('us.update_site_id')) + ->select([ + $db->quoteName('us.update_site_id'), + $db->quoteName('us.extra_query'), + ]) ->from($db->quoteName('#__update_sites', 'us')) ->join( 'INNER', @@ -79,23 +82,25 @@ class HtmlView extends BaseHtmlView ->setLimit(1); $db->setQuery($query); - $updateSiteId = (int) $db->loadResult(); + $site = $db->loadObject(); - if ($updateSiteId > 0) { + if (!$site) { + Factory::getApplication()->enqueueMessage( + Text::_('COM_MOKOJOOMBACKUP_UPDATE_SITE_MISSING'), + 'warning' + ); + } elseif (empty($site->extra_query) || strpos($site->extra_query, 'dlid=') === false) { + // Update site exists but no download key configured $editUrl = Route::_( - 'index.php?option=com_installer&view=updatesites&task=updatesite.edit&id=' . $updateSiteId + 'index.php?option=com_installer&view=updatesites&filter[search]=mokojoombackup' ); Factory::getApplication()->enqueueMessage( Text::sprintf('COM_MOKOJOOMBACKUP_UPDATE_SITE_NOTICE', $editUrl), 'info' ); - } else { - Factory::getApplication()->enqueueMessage( - Text::_('COM_MOKOJOOMBACKUP_UPDATE_SITE_MISSING'), - 'warning' - ); } + // If key is present, show nothing — all good } catch (\Throwable $e) { // Non-critical — silently ignore } -- 2.52.0 From df319f9bb351d717f0f8c0dc4e391b56a63a9a28 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 21:40:43 +0000 Subject: [PATCH 71/81] chore(version): pre-release bump to 01.01.17-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_console_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_content_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_system_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_task_mokojoombackup/mokojoombackup.xml | 2 +- .../packages/plg_webservices_mokojoombackup/mokojoombackup.xml | 2 +- source/pkg_mokojoombackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index a558764..fd9296d 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.16-dev + 01.01.17-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 62aba25..c3f1e75 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.16 +# VERSION: 01.01.17 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index da00aa8..57a965a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokojoombackup/mokojoombackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml index 6a6bab6..1d67e3d 100644 --- a/source/packages/com_mokojoombackup/mokojoombackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> com_mokojoombackup - 01.01.16-dev + 01.01.17-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index 9db1f91..49ba01c 100644 --- a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokojoombackup - 01.01.16-dev + 01.01.17-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml index 14e6622..95bd54e 100644 --- a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokojoombackup - 01.01.16-dev + 01.01.17-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml index 9ef6182..a628896 100644 --- a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokojoombackup - 01.01.16-dev + 01.01.17-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index 5b2df71..455459d 100644 --- a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokojoombackup - 01.01.16-dev + 01.01.17-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml index 9cab3cd..956aae4 100644 --- a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokojoombackup - 01.01.16-dev + 01.01.17-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml index bcf3235..e7bed75 100644 --- a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokojoombackup - 01.01.16-dev + 01.01.17-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index 607d476..be29145 100644 --- a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokojoombackup - 01.01.16-dev + 01.01.17-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokojoombackup.xml b/source/pkg_mokojoombackup.xml index 25bce5a..bf68ffb 100644 --- a/source/pkg_mokojoombackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokojoombackup - 01.01.16-dev + 01.01.17-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 8338eabf813aee5fa2dd37792b50c29ea4e05996 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 17:25:38 -0500 Subject: [PATCH 72/81] fix: replace showUpdateSiteNotice with warnMissingLicenseKey (MokoWaaS pattern) --- source/script.php | 71 ++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/source/script.php b/source/script.php index 972476d..96244fb 100644 --- a/source/script.php +++ b/source/script.php @@ -203,8 +203,8 @@ class Pkg_MokoJoomBackupInstallerScript } } - // Show update site link after install or update - $this->showUpdateSiteNotice(); + // Warn if no license key configured + $this->warnMissingLicenseKey(); } /** @@ -246,49 +246,38 @@ class Pkg_MokoJoomBackupInstallerScript } } - /** - * Show an info message linking directly to the update site record - * so the user can configure their download key. - * - * @return void - */ - private function showUpdateSiteNotice(): void + private function warnMissingLicenseKey(): void { - try { + try + { $db = Factory::getDbo(); + $db->setQuery( + $db->getQuery(true) + ->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')]) + ->from($db->quoteName('#__update_sites')) + ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoJoomBackup%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoJoomBackup%') . ')') + ->setLimit(1) + ); + $site = $db->loadObject(); - $query = $db->getQuery(true) - ->select($db->quoteName('us.update_site_id')) - ->from($db->quoteName('#__update_sites', 'us')) - ->join( - 'INNER', - $db->quoteName('#__update_sites_extensions', 'use') - . ' ON ' . $db->quoteName('use.update_site_id') . ' = ' . $db->quoteName('us.update_site_id') - ) - ->join( - 'INNER', - $db->quoteName('#__extensions', 'e') - . ' ON ' . $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('use.extension_id') - ) - ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokojoombackup')) - ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) - ->setLimit(1); - - $db->setQuery($query); - $updateSiteId = (int) $db->loadResult(); - - if ($updateSiteId > 0) { - $editUrl = Route::_( - 'index.php?option=com_installer&view=updatesites&filter[search]=mokojoombackup' - ); - - Factory::getApplication()->enqueueMessage( - Text::sprintf('PKG_MOKOJOOMBACKUP_POSTINSTALL_UPDATE_SITE', $editUrl), - 'info' - ); + if ($site) + { + $eq = (string) ($site->extra_query ?? ''); + if (!empty($eq) && strpos($eq, 'dlid=') !== false) { parse_str($eq, $p); if (!empty($p['dlid'])) { return; } } + $editUrl = 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . (int) $site->update_site_id; } - } catch (\Throwable $e) { - // Non-critical — silently ignore + else + { + $editUrl = 'index.php?option=com_installer&view=updatesites'; + } + + Factory::getApplication()->enqueueMessage( + 'Moko Consulting License Key Required — ' + . 'No download key is configured. Updates will not be available until a valid license key is entered. ' + . 'Enter License Key', + 'warning' + ); } + catch (\Throwable $e) {} } } -- 2.52.0 From f6c9f9b78df283ea0b6b2adb9c2617d435adbb97 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 6 Jun 2026 22:41:42 +0000 Subject: [PATCH 73/81] chore(version): pre-release bump to 01.01.18-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- README.md | 2 +- source/packages/com_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_console_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_content_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_system_mokojoombackup/mokojoombackup.xml | 2 +- source/packages/plg_task_mokojoombackup/mokojoombackup.xml | 2 +- .../packages/plg_webservices_mokojoombackup/mokojoombackup.xml | 2 +- source/pkg_mokojoombackup.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index fd9296d..d3afdbc 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -5,7 +5,7 @@ Package - MokoJoomBackup MokoConsulting Full-site backup and restore for Joomla — database, files, and configuration - 01.01.17-dev + 01.01.18-dev GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index c3f1e75..210cbcc 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 01.01.17 +# VERSION: 01.01.18 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/README.md b/README.md index 57a965a..5327852 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoJoomBackup - + Full-site backup and restore for Joomla — database, files, and configuration. diff --git a/source/packages/com_mokojoombackup/mokojoombackup.xml b/source/packages/com_mokojoombackup/mokojoombackup.xml index 1d67e3d..9f4ed4c 100644 --- a/source/packages/com_mokojoombackup/mokojoombackup.xml +++ b/source/packages/com_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> com_mokojoombackup - 01.01.17-dev + 01.01.18-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml index 49ba01c..9dfdb3e 100644 --- a/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_actionlog_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_actionlog_mokojoombackup - 01.01.17-dev + 01.01.18-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml index 95bd54e..cbf34fc 100644 --- a/source/packages/plg_console_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_console_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_console_mokojoombackup - 01.01.17-dev + 01.01.18-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml index a628896..7289484 100644 --- a/source/packages/plg_content_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_content_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_content_mokojoombackup - 01.01.17-dev + 01.01.18-dev 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml index 455459d..3867837 100644 --- a/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_quickicon_mokojoombackup/mokojoombackup.xml @@ -1,7 +1,7 @@ plg_quickicon_mokojoombackup - 01.01.17-dev + 01.01.18-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml index 956aae4..085394c 100644 --- a/source/packages/plg_system_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_system_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_system_mokojoombackup - 01.01.17-dev + 01.01.18-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml index e7bed75..0271b70 100644 --- a/source/packages/plg_task_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_task_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_task_mokojoombackup - 01.01.17-dev + 01.01.18-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml index be29145..744586b 100644 --- a/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml +++ b/source/packages/plg_webservices_mokojoombackup/mokojoombackup.xml @@ -8,7 +8,7 @@ --> plg_webservices_mokojoombackup - 01.01.17-dev + 01.01.18-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokojoombackup.xml b/source/pkg_mokojoombackup.xml index bf68ffb..f39f687 100644 --- a/source/pkg_mokojoombackup.xml +++ b/source/pkg_mokojoombackup.xml @@ -8,7 +8,7 @@ Package - MokoJoomBackup mokojoombackup - 01.01.17-dev + 01.01.18-dev 2026-06-02 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From ca7ab8f36369c67d0ed01d5abf517a2ec3bb8160 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 17:52:42 -0500 Subject: [PATCH 74/81] feat: JS placeholder resolution in FolderPicker for portable profiles FolderPickerField now resolves [site_name], [host], [date], etc. in JavaScript before browsing directories, and reverse-replaces actual values back to placeholders when user selects a path. This makes profiles portable across sites. Default placeholder hint uses [host] for directory paths. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/Field/FolderPickerField.php | 81 ++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/source/packages/com_mokojoombackup/src/Field/FolderPickerField.php b/source/packages/com_mokojoombackup/src/Field/FolderPickerField.php index 3cb0603..f0ac4d2 100644 --- a/source/packages/com_mokojoombackup/src/Field/FolderPickerField.php +++ b/source/packages/com_mokojoombackup/src/Field/FolderPickerField.php @@ -12,6 +12,7 @@ namespace Joomla\Component\MokoJoomBackup\Administrator\Field; defined('_JEXEC') or die; +use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; use Joomla\CMS\Language\Text; @@ -35,13 +36,43 @@ class FolderPickerField extends FormField $absPath = $rawValue; } - // If path contains placeholders, show info status instead of checking disk + // Build placeholder map for JS resolution + $hostname = preg_replace('/[^a-zA-Z0-9._-]/', '', $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? php_uname('n')); + $siteName = ''; + + try { + $siteName = Factory::getApplication()->get('sitename', ''); + } catch (\Throwable $e) { + // fallback + } + + $sanitizedSiteName = preg_replace('/[^a-zA-Z0-9._-]/', '', str_replace(' ', '-', trim($siteName))); + + $placeholders = [ + '[host]' => $hostname, + '[site_name]' => $sanitizedSiteName ?: 'joomla', + '[profile_id]' => '1', + '[profile_name]' => 'default', + '[type]' => 'full', + '[year]' => date('Y'), + '[month]' => date('m'), + '[day]' => date('d'), + '[date]' => date('Ymd'), + ]; + + $placeholdersJson = json_encode($placeholders); + + // Resolve placeholders for the status display + $resolvedPath = str_replace(array_keys($placeholders), array_values($placeholders), $absPath); $hasPlaceholders = preg_match('/\[.+\]/', $absPath); if ($hasPlaceholders) { - $statusClass = 'text-info'; - $statusIcon = 'icon-info-circle'; + $exists = is_dir($resolvedPath); + $statusClass = $exists ? 'text-success' : 'text-info'; + $statusIcon = $exists ? 'icon-publish' : 'icon-info-circle'; $statusText = Text::_('COM_MOKOJOOMBACKUP_FOLDER_PLACEHOLDER'); + $resolvedSafe = htmlspecialchars($resolvedPath, ENT_QUOTES, 'UTF-8'); + $statusDetail = "{$statusText}: {$resolvedSafe}"; } else { $exists = is_dir($absPath); $statusClass = $exists ? 'text-success' : 'text-danger'; @@ -49,24 +80,24 @@ class FolderPickerField extends FormField $statusText = $exists ? Text::_('COM_MOKOJOOMBACKUP_FOLDER_EXISTS') : Text::_('COM_MOKOJOOMBACKUP_FOLDER_NOT_FOUND'); + $absPathSafe = htmlspecialchars($absPath, ENT_QUOTES, 'UTF-8'); + $statusDetail = "{$statusText}: {$absPathSafe}"; } - $absPathSafe = htmlspecialchars($absPath, ENT_QUOTES, 'UTF-8'); - return << + placeholder="/home/user/backups/[host] or administrator/components/com_mokojoombackup/backups" /> -
    +
    - {$statusText}: {$absPathSafe} + {$statusDetail}