fix: resolve merge conflicts and pretty name resolution #7
@@ -1,730 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Joomla
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
||||
# PATH: /templates/workflows/joomla/update-server.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Update Joomla update server XML feed with stable/rc/dev entries
|
||||
#
|
||||
# Writes updates.xml with multiple <update> entries:
|
||||
# - <tag>stable</tag> on push to main (from auto-release)
|
||||
# - <tag>rc</tag> on push to rc/**
|
||||
# - <tag>development</tag> on push to dev or dev/**
|
||||
#
|
||||
# Joomla filters by user's "Minimum Stability" setting.
|
||||
|
||||
name: Update Joomla Update Server XML Feed
|
||||
|
||||
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 updates.xml
|
||||
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
<<<<<<< HEAD
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}'
|
||||
=======
|
||||
COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.GA_TOKEN }}"}}}'
|
||||
>>>>>>> main
|
||||
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
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
|
||||
/tmp/mokostandards-api 2>/dev/null || true
|
||||
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
|
||||
cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Generate updates.xml entry
|
||||
id: update
|
||||
run: |
|
||||
BRANCH="${{ github.ref_name }}"
|
||||
REPO="${{ github.repository }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "0.0.0")
|
||||
|
||||
# Auto-bump patch on all branches (dev, alpha, beta, rc)
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
BUMPED=$(php /tmp/mokostandards-api/cli/version_bump.php --path . 2>/dev/null || true)
|
||||
if [ -n "$BUMPED" ]; then
|
||||
VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "$VERSION")
|
||||
git add -A
|
||||
git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" 2>/dev/null || true
|
||||
git push 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Determine stability from branch or 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"
|
||||
elif [[ "$BRANCH" == dev/* ]] || [[ "$BRANCH" == "dev" ]]; then
|
||||
STABILITY="development"
|
||||
else
|
||||
STABILITY="stable"
|
||||
fi
|
||||
|
||||
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Parse manifest (portable — no grep -P)
|
||||
<<<<<<< HEAD
|
||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
=======
|
||||
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" ! -path "./build/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
>>>>>>> main
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "No Joomla manifest found — skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract fields using sed (works on all runners)
|
||||
EXT_NAME=$(sed -n 's/.*<name>\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_CLIENT=$(sed -n 's/.*<extension[^>]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
EXT_VERSION=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1)
|
||||
TARGET_PLATFORM=$(sed -n 's/.*\(<targetplatform[^/]*\/>\).*/\1/p' "$MANIFEST" | head -1)
|
||||
PHP_MINIMUM=$(sed -n 's/.*<php_minimum>\([^<]*\)<\/php_minimum>.*/\1/p' "$MANIFEST" | head -1)
|
||||
|
||||
# Fallbacks
|
||||
[ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
|
||||
[ -z "$EXT_TYPE" ] && EXT_TYPE="component"
|
||||
|
||||
# Derive element if not in manifest: try XML filename, then repo name
|
||||
if [ -z "$EXT_ELEMENT" ]; then
|
||||
EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
|
||||
case "$EXT_ELEMENT" in
|
||||
templatedetails|manifest|*.xml) EXT_ELEMENT=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Use manifest version if README version is empty
|
||||
[ "$VERSION" = "0.0.0" ] && [ -n "$EXT_VERSION" ] && VERSION="$EXT_VERSION"
|
||||
|
||||
[ -z "$TARGET_PLATFORM" ] && TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" %s>' "/")
|
||||
|
||||
CLIENT_TAG=""
|
||||
[ -n "$EXT_CLIENT" ] && CLIENT_TAG="<client>${EXT_CLIENT}</client>"
|
||||
[ -z "$CLIENT_TAG" ] && ([ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]) && CLIENT_TAG="<client>site</client>"
|
||||
|
||||
FOLDER_TAG=""
|
||||
[ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ] && FOLDER_TAG="<folder>${EXT_FOLDER}</folder>"
|
||||
|
||||
PHP_TAG=""
|
||||
[ -n "$PHP_MINIMUM" ] && PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>"
|
||||
|
||||
# Version suffix for non-stable
|
||||
DISPLAY_VERSION="$VERSION"
|
||||
case "$STABILITY" in
|
||||
development) DISPLAY_VERSION="${VERSION}-dev" ;;
|
||||
alpha) DISPLAY_VERSION="${VERSION}-alpha" ;;
|
||||
beta) DISPLAY_VERSION="${VERSION}-beta" ;;
|
||||
rc) DISPLAY_VERSION="${VERSION}-rc" ;;
|
||||
esac
|
||||
|
||||
MAJOR=$(echo "$VERSION" | awk -F. '{print $1}')
|
||||
|
||||
# Each stability level has its own release tag
|
||||
case "$STABILITY" in
|
||||
development) RELEASE_TAG="development" ;;
|
||||
alpha) RELEASE_TAG="alpha" ;;
|
||||
beta) RELEASE_TAG="beta" ;;
|
||||
rc) RELEASE_TAG="release-candidate" ;;
|
||||
*) RELEASE_TAG="v${MAJOR}" ;;
|
||||
esac
|
||||
|
||||
PACKAGE_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.zip"
|
||||
DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
||||
INFO_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
# -- Build install packages (ZIP + tar.gz) --------------------
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
if [ -d "$SOURCE_DIR" ]; then
|
||||
EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*"
|
||||
TAR_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
|
||||
|
||||
cd "$SOURCE_DIR"
|
||||
zip -r "/tmp/${PACKAGE_NAME}" . -x $EXCLUDES
|
||||
cd ..
|
||||
tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" \
|
||||
--exclude='.ftpignore' --exclude='sftp-config*' \
|
||||
--exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
|
||||
|
||||
SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
|
||||
|
||||
# Ensure release exists on Gitea
|
||||
<<<<<<< HEAD
|
||||
echo "Checking for existing release: ${RELEASE_TAG}"
|
||||
REL_HTTP=$(curl -sS -o /tmp/rel_check.json -w "%{http_code}" \
|
||||
-H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>&1) || true
|
||||
echo "Release lookup (HTTP ${REL_HTTP}):"
|
||||
cat /tmp/rel_check.json | python3 -m json.tool 2>/dev/null || cat /tmp/rel_check.json; echo
|
||||
RELEASE_ID=$(python3 -c "import sys,json; print(json.load(open('/tmp/rel_check.json')).get('id',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
# Create release
|
||||
echo "Creating new release for tag: ${RELEASE_TAG}"
|
||||
CREATE_PAYLOAD=$(python3 -c "import json; print(json.dumps({
|
||||
'tag_name': '${RELEASE_TAG}',
|
||||
'name': '${RELEASE_TAG} (${DISPLAY_VERSION})',
|
||||
'body': '${STABILITY} release',
|
||||
'prerelease': True,
|
||||
'target_commitish': 'main'
|
||||
}))")
|
||||
echo "Create payload: ${CREATE_PAYLOAD}"
|
||||
CREATE_HTTP=$(curl -sS -o /tmp/rel_create.json -w "%{http_code}" \
|
||||
-X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/releases" \
|
||||
-d "$CREATE_PAYLOAD")
|
||||
echo "Create release response (HTTP ${CREATE_HTTP}):"
|
||||
cat /tmp/rel_create.json | python3 -m json.tool 2>/dev/null || cat /tmp/rel_create.json; echo
|
||||
if [ "$CREATE_HTTP" -ge 400 ]; then
|
||||
echo "::error::Failed to create release (HTTP ${CREATE_HTTP})"
|
||||
fi
|
||||
RELEASE_ID=$(python3 -c "import sys,json; print(json.load(open('/tmp/rel_create.json')).get('id',''))" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
if [ -n "$RELEASE_ID" ]; then
|
||||
echo "Release ID: ${RELEASE_ID}"
|
||||
# Delete existing assets with same name before uploading
|
||||
ASSETS_HTTP=$(curl -sS -o /tmp/assets.json -w "%{http_code}" \
|
||||
-H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets")
|
||||
echo "List assets (HTTP ${ASSETS_HTTP}):"
|
||||
cat /tmp/assets.json | python3 -m json.tool 2>/dev/null || cat /tmp/assets.json; echo
|
||||
ASSETS=$(cat /tmp/assets.json)
|
||||
[ "$ASSETS_HTTP" -ge 400 ] && ASSETS="[]"
|
||||
|
||||
=======
|
||||
RELEASE_JSON=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null || true)
|
||||
RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
# Create release
|
||||
RELEASE_JSON=$(curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/releases" \
|
||||
-d "$(python3 -c "import json; print(json.dumps({
|
||||
'tag_name': '${RELEASE_TAG}',
|
||||
'name': '${RELEASE_TAG} (${DISPLAY_VERSION})',
|
||||
'body': '${STABILITY} release',
|
||||
'prerelease': True,
|
||||
'target_commitish': 'main'
|
||||
}))")" 2>/dev/null || true)
|
||||
RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
if [ -n "$RELEASE_ID" ]; then
|
||||
# Delete existing assets with same name before uploading
|
||||
ASSETS=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]")
|
||||
>>>>>>> main
|
||||
for ASSET_FILE in "$PACKAGE_NAME" "$TAR_NAME"; do
|
||||
ASSET_ID=$(echo "$ASSETS" | python3 -c "
|
||||
import sys,json
|
||||
assets = json.load(sys.stdin)
|
||||
for a in assets:
|
||||
if a['name'] == '${ASSET_FILE}':
|
||||
print(a['id']); break
|
||||
" 2>/dev/null || true)
|
||||
if [ -n "$ASSET_ID" ]; then
|
||||
<<<<<<< HEAD
|
||||
echo "Deleting existing asset: ${ASSET_FILE} (id=${ASSET_ID})"
|
||||
DEL_HTTP=$(curl -sS -o /tmp/asset_del.json -w "%{http_code}" \
|
||||
-X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}")
|
||||
echo "Delete asset response (HTTP ${DEL_HTTP})"
|
||||
=======
|
||||
curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
|
||||
>>>>>>> main
|
||||
fi
|
||||
done
|
||||
|
||||
# Upload both formats
|
||||
<<<<<<< HEAD
|
||||
echo "Uploading ${PACKAGE_NAME}..."
|
||||
UP_ZIP_HTTP=$(curl -sS -o /tmp/up_zip.json -w "%{http_code}" \
|
||||
-X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"/tmp/${PACKAGE_NAME}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${PACKAGE_NAME}")
|
||||
echo "Upload ZIP response (HTTP ${UP_ZIP_HTTP}):"
|
||||
cat /tmp/up_zip.json | python3 -m json.tool 2>/dev/null || cat /tmp/up_zip.json; echo
|
||||
[ "$UP_ZIP_HTTP" -ge 400 ] && echo "::warning::ZIP upload failed (HTTP ${UP_ZIP_HTTP})"
|
||||
|
||||
echo "Uploading ${TAR_NAME}..."
|
||||
UP_TAR_HTTP=$(curl -sS -o /tmp/up_tar.json -w "%{http_code}" \
|
||||
-X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"/tmp/${TAR_NAME}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${TAR_NAME}")
|
||||
echo "Upload tar.gz response (HTTP ${UP_TAR_HTTP}):"
|
||||
cat /tmp/up_tar.json | python3 -m json.tool 2>/dev/null || cat /tmp/up_tar.json; echo
|
||||
[ "$UP_TAR_HTTP" -ge 400 ] && echo "::warning::tar.gz upload failed (HTTP ${UP_TAR_HTTP})"
|
||||
else
|
||||
echo "::error::No release ID available — cannot upload packages"
|
||||
=======
|
||||
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"/tmp/${PACKAGE_NAME}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${PACKAGE_NAME}" > /dev/null 2>&1 || true
|
||||
|
||||
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"/tmp/${TAR_NAME}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${TAR_NAME}" > /dev/null 2>&1 || true
|
||||
>>>>>>> main
|
||||
fi
|
||||
|
||||
echo "Packages: ${PACKAGE_NAME} + ${TAR_NAME} (SHA: ${SHA256})" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
SHA256=""
|
||||
fi
|
||||
|
||||
<<<<<<< HEAD
|
||||
# -- Build the new entry -----------------------------------------
|
||||
NEW_ENTRY=""
|
||||
NEW_ENTRY="${NEW_ENTRY} <update>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <name>${EXT_NAME}</name>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <description>${EXT_NAME} (${STABILITY})</description>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <element>${EXT_ELEMENT}</element>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <type>${EXT_TYPE}</type>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <version>${DISPLAY_VERSION}</version>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <creationDate>$(date +%Y-%m-%d)</creationDate>\n"
|
||||
[ -n "$CLIENT_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${CLIENT_TAG}\n"
|
||||
[ -n "$FOLDER_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${FOLDER_TAG}\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <tags>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <tag>${STABILITY}</tag>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} </tags>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <downloads>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} </downloads>\n"
|
||||
[ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} <sha256>${SHA256}</sha256>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n"
|
||||
[ -n "$PHP_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${PHP_TAG}\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <maintainer>Moko Consulting</maintainer>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <maintainerurl>https://mokoconsulting.tech</maintainerurl>\n"
|
||||
=======
|
||||
# -- Build the new entry (canonical format matching release.yml) --
|
||||
NEW_ENTRY=""
|
||||
NEW_ENTRY="${NEW_ENTRY} <update>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <name>${EXT_NAME}</name>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <description>${EXT_NAME} ${STABILITY} build.</description>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <element>${EXT_ELEMENT}</element>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <type>${EXT_TYPE}</type>\n"
|
||||
[ -n "$CLIENT_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${CLIENT_TAG}\n"
|
||||
[ -n "$FOLDER_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${FOLDER_TAG}\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <version>${VERSION}</version>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <creationDate>$(date +%Y-%m-%d)</creationDate>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <infourl title='${EXT_NAME}'>https://git.mokoconsulting.tech/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${RELEASE_TAG}</infourl>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <downloads>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <downloadurl type='full' format='zip'>${DOWNLOAD_URL}</downloadurl>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} </downloads>\n"
|
||||
[ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} <sha256>${SHA256}</sha256>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <tags><tag>${STABILITY}</tag></tags>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <maintainer>Moko Consulting</maintainer>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <maintainerurl>https://mokoconsulting.tech</maintainerurl>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <targetplatform name='joomla' version='(5|6).*'/>\n"
|
||||
[ -n "$PHP_MINIMUM" ] && NEW_ENTRY="${NEW_ENTRY} <php_minimum>${PHP_MINIMUM}</php_minimum>\n"
|
||||
>>>>>>> main
|
||||
NEW_ENTRY="${NEW_ENTRY} </update>"
|
||||
|
||||
# -- Write new entry to temp file --------------------------------
|
||||
printf '%b' "$NEW_ENTRY" > /tmp/new_entry.xml
|
||||
|
||||
<<<<<<< HEAD
|
||||
# -- Merge into updates.xml (only update this stability channel) -
|
||||
# Cascading update: each stability level updates itself and all lower levels
|
||||
# stable → all | rc → rc,beta,alpha,dev | beta → beta,alpha,dev | alpha → alpha,dev | dev → dev
|
||||
=======
|
||||
# -- Merge into updates.xml ----------------------------------------
|
||||
# Cascade: stable→all | rc→rc+lower | beta→beta+lower | alpha→alpha+dev | dev→dev
|
||||
>>>>>>> main
|
||||
CASCADE_MAP="stable:development,alpha,beta,rc,stable rc:development,alpha,beta,rc beta:development,alpha,beta alpha:development,alpha development:development"
|
||||
TARGETS=""
|
||||
for entry in $CASCADE_MAP; do
|
||||
key="${entry%%:*}"
|
||||
vals="${entry#*:}"
|
||||
if [ "$key" = "${STABILITY}" ]; then
|
||||
TARGETS="$vals"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -z "$TARGETS" ] && TARGETS="${STABILITY}"
|
||||
|
||||
<<<<<<< HEAD
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
printf '%s\n' "<?xml version='1.0' encoding='UTF-8'?>" > updates.xml
|
||||
printf '%s\n' "<!-- Copyright (C) $(date +%Y) Moko Consulting <hello@mokoconsulting.tech>" >> updates.xml
|
||||
printf '%s\n' " SPDX-License-Identifier: GPL-3.0-or-later" >> updates.xml
|
||||
printf '%s\n' " VERSION: ${VERSION}" >> updates.xml
|
||||
printf '%s\n' " -->" >> updates.xml
|
||||
printf '%s\n' "" >> updates.xml
|
||||
printf '%s\n' '<updates>' >> updates.xml
|
||||
cat /tmp/new_entry.xml >> updates.xml
|
||||
printf '\n%s\n' '</updates>' >> updates.xml
|
||||
else
|
||||
# Replace each cascading channel with the new entry (different tag)
|
||||
export PY_TARGETS="$TARGETS"
|
||||
python3 << PYEOF
|
||||
import re, os
|
||||
targets = os.environ["PY_TARGETS"].split(",")
|
||||
stability = "${STABILITY}"
|
||||
=======
|
||||
echo "Cascade: ${STABILITY} → ${TARGETS}"
|
||||
|
||||
# Create updates.xml if missing
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
printf '%s\n' "<?xml version='1.0' encoding='UTF-8'?>" > updates.xml
|
||||
printf '%s\n' "<!-- Copyright (C) $(date +%Y) Moko Consulting -->" >> updates.xml
|
||||
printf '%s\n' "<updates>" >> updates.xml
|
||||
printf '%s\n' "</updates>" >> updates.xml
|
||||
fi
|
||||
|
||||
# Update existing blocks or create missing ones
|
||||
export PY_TARGETS="$TARGETS" PY_VERSION="$VERSION" PY_DATE="$(date +%Y-%m-%d)"
|
||||
python3 << 'PYEOF'
|
||||
import re, os
|
||||
|
||||
targets = os.environ["PY_TARGETS"].split(",")
|
||||
version = os.environ["PY_VERSION"]
|
||||
date = os.environ["PY_DATE"]
|
||||
|
||||
>>>>>>> main
|
||||
with open("updates.xml") as f:
|
||||
content = f.read()
|
||||
with open("/tmp/new_entry.xml") as f:
|
||||
new_entry_template = f.read()
|
||||
<<<<<<< HEAD
|
||||
for tag in targets:
|
||||
tag = tag.strip()
|
||||
# Build entry with this tag
|
||||
new_entry = re.sub(r"<tag>[^<]*</tag>", f"<tag>{tag}</tag>", new_entry_template)
|
||||
# Remove existing entry for this tag
|
||||
pattern = r" <update>.*?<tag>" + re.escape(tag) + r"</tag>.*?</update>\n?"
|
||||
content = re.sub(pattern, "", content, flags=re.DOTALL)
|
||||
# Insert before </updates>
|
||||
content = content.replace("</updates>", new_entry + "\n</updates>")
|
||||
content = re.sub(r"\n{3,}", "\n\n", content)
|
||||
with open("updates.xml", "w") as f:
|
||||
f.write(content)
|
||||
PYEOF
|
||||
if [ $? -ne 0 ]; then
|
||||
# Fallback: rebuild keeping other stability entries
|
||||
{
|
||||
printf '%s\n' "<?xml version='1.0' encoding='UTF-8'?>"
|
||||
printf '%s\n' "<!-- Copyright (C) $(date +%Y) Moko Consulting <hello@mokoconsulting.tech>"
|
||||
printf '%s\n' " SPDX-License-Identifier: GPL-3.0-or-later"
|
||||
printf '%s\n' " VERSION: ${VERSION}"
|
||||
printf '%s\n' " -->"
|
||||
printf '%s\n' ""
|
||||
printf '%s\n' '<updates>'
|
||||
for TAG in stable rc development; do
|
||||
[ "$TAG" = "${STABILITY}" ] && continue
|
||||
if grep -q "<tag>${TAG}</tag>" updates.xml 2>/dev/null; then
|
||||
sed -n "/<update>/,/<\/update>/{ /<tag>${TAG}<\/tag>/p; }" updates.xml
|
||||
fi
|
||||
done
|
||||
cat /tmp/new_entry.xml
|
||||
printf '\n%s\n' '</updates>'
|
||||
} > /tmp/updates_new.xml
|
||||
mv /tmp/updates_new.xml updates.xml
|
||||
fi
|
||||
fi
|
||||
=======
|
||||
|
||||
for tag in targets:
|
||||
tag = tag.strip()
|
||||
# Build entry with this tag's name
|
||||
new_entry = re.sub(r"<tag>[^<]*</tag>", f"<tag>{tag}</tag>", new_entry_template)
|
||||
|
||||
# Try to find existing block (handles both single-line and multi-line <tags>)
|
||||
block_pattern = r"(<update>(?:(?!</update>).)*?<tag>" + re.escape(tag) + r"</tag>.*?</update>)"
|
||||
match = re.search(block_pattern, content, re.DOTALL)
|
||||
|
||||
if match:
|
||||
# Update in place — replace entire block
|
||||
content = content.replace(match.group(1), new_entry.strip())
|
||||
print(f" UPDATED: <tag>{tag}</tag> → {version}")
|
||||
else:
|
||||
# Create — insert before </updates>
|
||||
content = content.replace("</updates>", "\n" + new_entry.strip() + "\n\n</updates>")
|
||||
print(f" CREATED: <tag>{tag}</tag> → {version}")
|
||||
|
||||
# Clean up excessive blank lines
|
||||
content = re.sub(r"\n{3,}", "\n\n", content)
|
||||
|
||||
with open("updates.xml", "w") as f:
|
||||
f.write(content)
|
||||
PYEOF
|
||||
>>>>>>> main
|
||||
|
||||
# Commit
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git add updates.xml
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore: update updates.xml (${STABILITY}: ${DISPLAY_VERSION}) [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||
git push
|
||||
}
|
||||
|
||||
# -- Sync updates.xml to main (for non-main branches) ----------------------
|
||||
- name: Sync updates.xml to main
|
||||
if: github.ref_name != 'main'
|
||||
run: |
|
||||
<<<<<<< HEAD
|
||||
set -euo pipefail
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
GA_TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
STABILITY="${{ steps.update.outputs.stability }}"
|
||||
|
||||
echo "Fetching updates.xml SHA from main..."
|
||||
SHA_HTTP=$(curl -sS -o /tmp/main_sha.json -w "%{http_code}" \
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
"${API_BASE}/contents/updates.xml?ref=main") || true
|
||||
echo "File SHA response (HTTP ${SHA_HTTP}):"
|
||||
cat /tmp/main_sha.json | python3 -m json.tool 2>/dev/null || cat /tmp/main_sha.json; echo
|
||||
|
||||
FILE_SHA=$(python3 -c "import json; print(json.load(open('/tmp/main_sha.json')).get('sha',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then
|
||||
echo "Syncing updates.xml to main (sha=${FILE_SHA})..."
|
||||
CONTENT=$(base64 -w0 updates.xml)
|
||||
SYNC_PAYLOAD=$(python3 -c "import json; print(json.dumps({
|
||||
'content': '${CONTENT}',
|
||||
'sha': '${FILE_SHA}',
|
||||
'message': 'chore: sync updates.xml from ${STABILITY} [skip ci]',
|
||||
'branch': 'main'
|
||||
}))")
|
||||
SYNC_HTTP=$(curl -sS -o /tmp/sync_main.json -w "%{http_code}" \
|
||||
-X PUT -H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/contents/updates.xml" \
|
||||
-d "$SYNC_PAYLOAD")
|
||||
echo "Sync response (HTTP ${SYNC_HTTP}):"
|
||||
cat /tmp/sync_main.json | python3 -m json.tool 2>/dev/null || cat /tmp/sync_main.json; echo
|
||||
if [ "$SYNC_HTTP" -ge 400 ]; then
|
||||
echo "::warning::Failed to sync updates.xml to main (HTTP ${SYNC_HTTP})"
|
||||
echo "WARNING: failed to sync updates.xml to main (HTTP ${SYNC_HTTP})" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "updates.xml synced to main (${STABILITY})" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
else
|
||||
echo "::warning::Could not get updates.xml SHA from main (HTTP ${SHA_HTTP})"
|
||||
echo "WARNING: could not get updates.xml SHA from main" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# -- Mirror to GitHub (stable and rc only) --------------------------------
|
||||
- name: Mirror release to GitHub
|
||||
if: >-
|
||||
(steps.update.outputs.stability == 'stable' || steps.update.outputs.stability == 'rc') &&
|
||||
secrets.GH_TOKEN != ''
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
run: |
|
||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||
STABILITY="${{ steps.update.outputs.stability }}"
|
||||
echo "GitHub mirror sync for ${STABILITY} — ${GH_REPO}" >> $GITHUB_STEP_SUMMARY
|
||||
# Mirror packages if they exist
|
||||
for PKG in /tmp/*.zip /tmp/*.tar.gz; do
|
||||
if [ -f "$PKG" ]; then
|
||||
PKG_NAME=$(basename "$PKG")
|
||||
echo "Mirroring ${PKG_NAME}..."
|
||||
MIRROR_HTTP=$(curl -sS -o /tmp/mirror_rel.json -w "%{http_code}" \
|
||||
-H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/tags/${RELEASE_TAG}") || true
|
||||
echo "Mirror release lookup (HTTP ${MIRROR_HTTP})"
|
||||
_RELID=$(jq -r '.id // empty' /tmp/mirror_rel.json 2>/dev/null)
|
||||
if [ -n "$_RELID" ]; then
|
||||
UP_HTTP=$(curl -sS -o /tmp/mirror_up.json -w "%{http_code}" \
|
||||
-X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/${_RELID}/assets?name=${PKG_NAME}" \
|
||||
--data-binary "@$PKG")
|
||||
echo "Mirror upload ${PKG_NAME} (HTTP ${UP_HTTP})"
|
||||
[ "$UP_HTTP" -ge 400 ] && echo "::warning::Mirror upload failed for ${PKG_NAME} (HTTP ${UP_HTTP})"
|
||||
else
|
||||
echo "::warning::No release found for mirroring (tag=${RELEASE_TAG})"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
=======
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
GA_TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
|
||||
FILE_SHA=$(curl -sf -H "Authorization: token ${GA_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
|
||||
CONTENT=$(base64 -w0 updates.xml)
|
||||
curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/contents/updates.xml" \
|
||||
-d "$(python3 -c "import json; print(json.dumps({
|
||||
'content': '${CONTENT}',
|
||||
'sha': '${FILE_SHA}',
|
||||
'message': 'chore: sync updates.xml from ${STABILITY} [skip ci]',
|
||||
'branch': 'main'
|
||||
}))")" > /dev/null 2>&1 \
|
||||
&& echo "updates.xml synced to main (${STABILITY})" >> $GITHUB_STEP_SUMMARY \
|
||||
|| echo "WARNING: failed to sync updates.xml to main" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "WARNING: could not get updates.xml SHA from main" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
>>>>>>> main
|
||||
- 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 }}"
|
||||
REPO="${{ github.repository }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
<<<<<<< HEAD
|
||||
echo "Checking deploy permission for ${ACTOR}..."
|
||||
PERM_HTTP=$(curl -sS -o /tmp/perm.json -w "%{http_code}" \
|
||||
-H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/collaborators/${ACTOR}/permission") || true
|
||||
echo "Permission check (HTTP ${PERM_HTTP}):"
|
||||
cat /tmp/perm.json | python3 -m json.tool 2>/dev/null || cat /tmp/perm.json; echo
|
||||
PERMISSION=$(python3 -c "import json; print(json.load(open('/tmp/perm.json')).get('permission','read'))" 2>/dev/null || echo "read")
|
||||
=======
|
||||
PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.GA_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")
|
||||
>>>>>>> main
|
||||
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 /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/mokostandards-api/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
elif [ -f "/tmp/mokostandards-api/deploy/deploy-sftp.php" ]; then
|
||||
php /tmp/mokostandards-api/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: |
|
||||
echo "## Joomla 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_VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Element | \`${EXT_ELEMENT}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Download | [ZIP](${DOWNLOAD_URL}) |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# Composer
|
||||
/vendor/
|
||||
composer.lock
|
||||
|
||||
# Node
|
||||
/node_modules/
|
||||
|
||||
# Build
|
||||
/build/
|
||||
/dist/
|
||||
*.zip
|
||||
*.tar.gz
|
||||
|
||||
# Secrets
|
||||
.env
|
||||
.env.*
|
||||
*.ppk
|
||||
*.pem
|
||||
*.key
|
||||
sftp-config*
|
||||
.ftpignore
|
||||
|
||||
# Temporary
|
||||
tmp/
|
||||
tmp-overrides/
|
||||
profile.ps1
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
.mcp.json
|
||||
>>>>>>> main
|
||||
|
||||
+8
-13
@@ -1,10 +1,6 @@
|
||||
# Changelog
|
||||
## [Unreleased]
|
||||
|
||||
## [01.03.00] --- 2026-05-09
|
||||
|
||||
|
||||
## [01.02.00] --- 2026-05-09
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Auto-create "DPCalendar Events" template category in JoomGallery on install
|
||||
@@ -13,6 +9,10 @@
|
||||
- Release workflow: changelog promotion, dev reset, SHA checksums
|
||||
- Release workflow: extension type prefix in ZIP and release name
|
||||
- Release workflow: element detection from plugin=/module= attributes
|
||||
- Initial JoomGallery and DPCalendar integration
|
||||
- Auto-creates gallery category per event
|
||||
- Mapping table for event-to-category links
|
||||
- Seed existing events on install
|
||||
|
||||
### Changed
|
||||
- Display name updated to "Moko Gallery Calendar"
|
||||
@@ -22,11 +22,6 @@
|
||||
### Fixed
|
||||
- ZIP filename missing element prefix
|
||||
- Element detection for plugins without element tag
|
||||
|
||||
## [01.00.10] - 2026-05-01
|
||||
|
||||
### Added
|
||||
- Initial JoomGallery and DPCalendar integration
|
||||
- Auto-creates gallery category per event
|
||||
- Mapping table for event-to-category links
|
||||
- Seed existing events on install
|
||||
- Release name and body: pretty name resolution from .ini language key
|
||||
- Release body: duplicate version header removed
|
||||
- Release body: SHA-256 checksums now display correctly
|
||||
|
||||
@@ -6,42 +6,27 @@ This file is part of a Moko Consulting project.
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: MokoJGDPC.Documentation
|
||||
INGROUP: MokoJGDPC
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJGDPC
|
||||
DEFGROUP: MokoGalleryCalendar.Documentation
|
||||
INGROUP: MokoGalleryCalendar
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar
|
||||
FILE: ./README.md
|
||||
<<<<<<< HEAD
|
||||
VERSION: 01.03.00
|
||||
=======
|
||||
VERSION: 01.04.00
|
||||
>>>>>>> main
|
||||
BRIEF: Documentation for MokoJGDPC plugin
|
||||
VERSION: 01.01.00
|
||||
BRIEF: Documentation for Moko Gallery Calendar plugin
|
||||
-->
|
||||
|
||||
# MokoJGDPC
|
||||
# Moko Gallery Calendar
|
||||
|
||||
<<<<<<< HEAD
|
||||
> **MokoJGDPC** is a Joomla system plugin that automatically creates a JoomGallery category whenever a DPCalendar event is created. Link your events to photo galleries without manual setup.
|
||||
=======
|
||||
> **MokoJGDPC** is a Joomla system plugin that automatically creates a JoomGallery category whenever a DPCalendar event's start date arrives. Link your events to photo galleries without manual setup.
|
||||
>>>>>>> main
|
||||
> **Moko Gallery Calendar** is a Joomla system plugin that automatically creates a JoomGallery category whenever a DPCalendar event's start date arrives. Link your events to photo galleries without manual setup.
|
||||
|
||||
**DPCalendar + JoomGallery Bridge**
|
||||
|
||||
[](https://git.mokoconsulting.tech/MokoConsulting/MokoJGDPC/releases/tag/stable)
|
||||
[](https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/tag/stable)
|
||||
[](LICENSE)
|
||||
[](https://www.joomla.org)
|
||||
[](https://www.php.net)
|
||||
|
||||
## Features
|
||||
|
||||
<<<<<<< HEAD
|
||||
- Automatically creates a JoomGallery category when a DPCalendar event is saved
|
||||
- Category title mirrors the event title for easy identification
|
||||
- Configurable parent category in JoomGallery for all event galleries
|
||||
- Optionally deletes the gallery category when the event is deleted
|
||||
- Supports event updates (renames the gallery category when the event title changes)
|
||||
=======
|
||||
- **Deferred category creation** — gallery categories are created when the event date arrives, not when the event is first saved
|
||||
- **Joomla Task Scheduler** — runs daily via `com_scheduler` (Joomla 4.1+); auto-registered on install
|
||||
- **Frontend fallback** — if the scheduled task hasn't run in 7 days, the next site visit triggers processing
|
||||
@@ -51,7 +36,7 @@ BRIEF: Documentation for MokoJGDPC plugin
|
||||
- **Recurring event aware** — only the original event gets a gallery; recurring instances are skipped
|
||||
- **Unique aliases** — automatically deduplicates category URL aliases
|
||||
- **Seed on install** — existing DPCalendar events get mapped on first install
|
||||
>>>>>>> main
|
||||
- **Auto-creates template category** — creates "DPCalendar Events" parent category in JoomGallery on install
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -62,33 +47,16 @@ BRIEF: Documentation for MokoJGDPC plugin
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the latest release ZIP from the [releases page](https://git.mokoconsulting.tech/MokoConsulting/MokoJGDPC/releases/tag/stable)
|
||||
1. Download the latest release ZIP from the [releases page](https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/tag/stable)
|
||||
2. Install via **System > Install > Extensions** in your Joomla administrator
|
||||
3. Enable the plugin at **System > Manage > Plugins** (search for "MokoJGDPC")
|
||||
<<<<<<< HEAD
|
||||
4. Configure the parent gallery category in the plugin settings
|
||||
=======
|
||||
3. Enable the plugin at **System > Manage > Plugins** (search for "Moko Gallery Calendar")
|
||||
4. Configure the plugin settings (see below)
|
||||
5. Verify the scheduled task is registered at **System > Scheduled Tasks**
|
||||
>>>>>>> main
|
||||
|
||||
## Configuration
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------|-------------|---------|
|
||||
<<<<<<< HEAD
|
||||
| Parent Category | JoomGallery category under which event galleries are created | Root (1) |
|
||||
| Delete on Event Remove | Remove gallery category when event is trashed/deleted | No |
|
||||
| Sync Title Changes | Rename gallery category when event title changes | Yes |
|
||||
|
||||
## How It Works
|
||||
|
||||
1. A user creates or saves an event in DPCalendar
|
||||
2. MokoJGDPC intercepts the `onContentAfterSave` event
|
||||
3. If the content type is a DPCalendar event and no linked gallery category exists, one is created in JoomGallery
|
||||
4. The link between event and gallery category is stored in the plugin's mapping table
|
||||
5. When an event is deleted (if configured), the gallery category is also removed
|
||||
=======
|
||||
| **Parent Category** | JoomGallery category ID under which event galleries are created | `1` (root) |
|
||||
| **Delete on Event Remove** | Remove gallery category when the DPCalendar event is trashed/deleted | No |
|
||||
| **Sync Title Changes** | Rename gallery category when the event title changes | Yes |
|
||||
@@ -146,14 +114,6 @@ The plugin creates one table:
|
||||
| `event_date` | DATE | Event start date — category created when this date arrives |
|
||||
| `created` | DATETIME | Row creation timestamp |
|
||||
|
||||
### Asset Records
|
||||
|
||||
Each created gallery category gets a corresponding `#__assets` row for Joomla ACL enforcement. The asset is:
|
||||
- Positioned as a child of the parent category's asset (or `com_joomgallery` root)
|
||||
- Populated with rules from the permissions template category (if configured)
|
||||
- Linked back to the category via `asset_id`
|
||||
- Cleaned up when the category is deleted
|
||||
|
||||
## Uninstall
|
||||
|
||||
Uninstalling the plugin:
|
||||
@@ -162,7 +122,6 @@ Uninstalling the plugin:
|
||||
- Deletes the `/tmp/mokojgdpc_lastrun` cache file
|
||||
|
||||
**Note:** Gallery categories and their asset records created by the plugin are **not** removed on uninstall. They become standalone JoomGallery categories.
|
||||
>>>>>>> main
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<<<<<<< HEAD
|
||||
; VERSION: 01.00.00
|
||||
=======
|
||||
; VERSION: 01.00.01
|
||||
>>>>>>> main
|
||||
|
||||
PLG_SYSTEM_MOKOJGDPC="MokoJGDPC"
|
||||
PLG_SYSTEM_MOKOJGDPC="Moko Gallery Calendar"
|
||||
PLG_SYSTEM_MOKOJGDPC_DESCRIPTION="Automatically creates a JoomGallery category when a DPCalendar event is created. Links events to photo galleries."
|
||||
PLG_SYSTEM_MOKOJGDPC_PARENT_CATEGORY_LABEL="Parent Gallery Category"
|
||||
PLG_SYSTEM_MOKOJGDPC_PARENT_CATEGORY_DESC="JoomGallery category ID under which event galleries will be created. Default is the root category (1)."
|
||||
@@ -14,12 +10,9 @@ PLG_SYSTEM_MOKOJGDPC_DELETE_ON_REMOVE_LABEL="Delete Gallery on Event Remove"
|
||||
PLG_SYSTEM_MOKOJGDPC_DELETE_ON_REMOVE_DESC="When enabled, the linked JoomGallery category will be deleted when the DPCalendar event is deleted."
|
||||
PLG_SYSTEM_MOKOJGDPC_SYNC_TITLE_LABEL="Sync Title Changes"
|
||||
PLG_SYSTEM_MOKOJGDPC_SYNC_TITLE_DESC="When enabled, renaming a DPCalendar event will also rename its linked JoomGallery category."
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
PLG_SYSTEM_MOKOJGDPC_DEFAULT_ACCESS_LABEL="Default Access Level"
|
||||
PLG_SYSTEM_MOKOJGDPC_DEFAULT_ACCESS_DESC="Access level assigned to newly created gallery categories. Controls who can view the category."
|
||||
PLG_SYSTEM_MOKOJGDPC_PERMISSIONS_TEMPLATE_LABEL="Permissions Template Category"
|
||||
PLG_SYSTEM_MOKOJGDPC_PERMISSIONS_TEMPLATE_DESC="JoomGallery category ID whose permission rules will be copied to new categories. Set to 0 to inherit from parent. This controls who can create, edit, and delete images within the category."
|
||||
PLG_SYSTEM_MOKOJGDPC_TASK_PROCESS_PENDING_TITLE="MokoJGDPC: Process Pending Gallery Categories"
|
||||
PLG_SYSTEM_MOKOJGDPC_TASK_PROCESS_PENDING_TITLE="Moko Gallery Calendar: Process Pending Gallery Categories"
|
||||
PLG_SYSTEM_MOKOJGDPC_TASK_PROCESS_PENDING_DESC="Creates JoomGallery categories for DPCalendar events whose start date has arrived. Runs daily via the Joomla Task Scheduler."
|
||||
>>>>>>> main
|
||||
|
||||
@@ -2,10 +2,5 @@
|
||||
; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
; VERSION: 01.00.00
|
||||
|
||||
<<<<<<< HEAD
|
||||
PLG_SYSTEM_MOKOJGDPC="Moko Gallery Calendar"
|
||||
PLG_SYSTEM_MOKOJGDPC_DESCRIPTION="Automatically creates a JoomGallery category when a DPCalendar event is created. Links events to photo galleries.<br><br><strong>After Install:</strong><br>1. Go to System → Manage → Plugins, search 'Moko Gallery Calendar'<br>2. Edit plugin parameters — set the parent JoomGallery category (auto-created as 'DPCalendar Events')<br>3. In JoomGallery → Categories, set permissions on 'DPCalendar Events' category for user access levels<br>4. New events will automatically get a photo gallery sub-category"
|
||||
=======
|
||||
PLG_SYSTEM_MOKOJGDPC="MokoJGDPC"
|
||||
PLG_SYSTEM_MOKOJGDPC_DESCRIPTION="Automatically creates a JoomGallery category when a DPCalendar event is created. Links events to photo galleries."
|
||||
>>>>>>> main
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<<<<<<< HEAD
|
||||
; VERSION: 01.00.00
|
||||
=======
|
||||
; VERSION: 01.00.01
|
||||
>>>>>>> main
|
||||
|
||||
PLG_SYSTEM_MOKOJGDPC="MokoJGDPC"
|
||||
PLG_SYSTEM_MOKOJGDPC="Moko Gallery Calendar"
|
||||
PLG_SYSTEM_MOKOJGDPC_DESCRIPTION="Automatically creates a JoomGallery category when a DPCalendar event is created. Links events to photo galleries."
|
||||
PLG_SYSTEM_MOKOJGDPC_PARENT_CATEGORY_LABEL="Parent Gallery Category"
|
||||
PLG_SYSTEM_MOKOJGDPC_PARENT_CATEGORY_DESC="JoomGallery category ID under which event galleries will be created. Default is the root category (1)."
|
||||
@@ -14,12 +10,9 @@ PLG_SYSTEM_MOKOJGDPC_DELETE_ON_REMOVE_LABEL="Delete Gallery on Event Remove"
|
||||
PLG_SYSTEM_MOKOJGDPC_DELETE_ON_REMOVE_DESC="When enabled, the linked JoomGallery category will be deleted when the DPCalendar event is deleted."
|
||||
PLG_SYSTEM_MOKOJGDPC_SYNC_TITLE_LABEL="Sync Title Changes"
|
||||
PLG_SYSTEM_MOKOJGDPC_SYNC_TITLE_DESC="When enabled, renaming a DPCalendar event will also rename its linked JoomGallery category."
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
PLG_SYSTEM_MOKOJGDPC_DEFAULT_ACCESS_LABEL="Default Access Level"
|
||||
PLG_SYSTEM_MOKOJGDPC_DEFAULT_ACCESS_DESC="Access level assigned to newly created gallery categories. Controls who can view the category."
|
||||
PLG_SYSTEM_MOKOJGDPC_PERMISSIONS_TEMPLATE_LABEL="Permissions Template Category"
|
||||
PLG_SYSTEM_MOKOJGDPC_PERMISSIONS_TEMPLATE_DESC="JoomGallery category ID whose permission rules will be copied to new categories. Set to 0 to inherit from parent. This controls who can create, edit, and delete images within the category."
|
||||
PLG_SYSTEM_MOKOJGDPC_TASK_PROCESS_PENDING_TITLE="MokoJGDPC: Process Pending Gallery Categories"
|
||||
PLG_SYSTEM_MOKOJGDPC_TASK_PROCESS_PENDING_TITLE="Moko Gallery Calendar: Process Pending Gallery Categories"
|
||||
PLG_SYSTEM_MOKOJGDPC_TASK_PROCESS_PENDING_DESC="Creates JoomGallery categories for DPCalendar events whose start date has arrived. Runs daily via the Joomla Task Scheduler."
|
||||
>>>>>>> main
|
||||
|
||||
@@ -2,10 +2,5 @@
|
||||
; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
; VERSION: 01.00.00
|
||||
|
||||
<<<<<<< HEAD
|
||||
PLG_SYSTEM_MOKOJGDPC="Moko Gallery Calendar"
|
||||
PLG_SYSTEM_MOKOJGDPC_DESCRIPTION="Automatically creates a JoomGallery category when a DPCalendar event is created. Links events to photo galleries.<br><br><strong>After Install:</strong><br>1. Go to System → Manage → Plugins, search 'Moko Gallery Calendar'<br>2. Edit plugin parameters — set the parent JoomGallery category (auto-created as 'DPCalendar Events')<br>3. In JoomGallery → Categories, set permissions on 'DPCalendar Events' category for user access levels<br>4. New events will automatically get a photo gallery sub-category"
|
||||
=======
|
||||
PLG_SYSTEM_MOKOJGDPC="MokoJGDPC"
|
||||
PLG_SYSTEM_MOKOJGDPC_DESCRIPTION="Automatically creates a JoomGallery category when a DPCalendar event is created. Links events to photo galleries."
|
||||
>>>>>>> main
|
||||
|
||||
+4
-21
@@ -10,23 +10,13 @@ FILE INFORMATION
|
||||
DEFGROUP: Joomla
|
||||
INGROUP: MokoJGDPC
|
||||
PATH: /src/mokojgdpc.xml
|
||||
<<<<<<< HEAD
|
||||
VERSION: 01.00.00
|
||||
BRIEF: Plugin manifest XML file for MokoJGDPC
|
||||
-->
|
||||
<extension type="plugin" group="system" method="upgrade">
|
||||
<name>plg_system_mokojgdpc</name>
|
||||
<version>01.03.00</version>
|
||||
<name>PLG_SYSTEM_MOKOJGDPC</name>
|
||||
<version>01.01.00</version>
|
||||
<creationDate>2026-05-09</creationDate>
|
||||
=======
|
||||
VERSION: 01.00.01
|
||||
BRIEF: Plugin manifest XML file for MokoJGDPC
|
||||
-->
|
||||
<extension type="plugin" group="system" method="upgrade">
|
||||
<name>Moko Gallery Calendar</name>
|
||||
<version>01.03.00</version>
|
||||
<creationDate>2026-05-09</creationDate>
|
||||
>>>>>>> main
|
||||
<author>Jonathan Miller || Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
@@ -52,8 +42,8 @@ BRIEF: Plugin manifest XML file for MokoJGDPC
|
||||
<scriptfile>script.php</scriptfile>
|
||||
|
||||
<updateservers>
|
||||
<server type="extension" priority="1" name="MokoJGDPC Update Server (Gitea)">
|
||||
https://git.mokoconsulting.tech/MokoConsulting/MokoJGDPC/raw/branch/main/updates.xml
|
||||
<server type="extension" priority="1" name="Moko Gallery Calendar Update Server">
|
||||
https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/raw/branch/main/updates.xml
|
||||
</server>
|
||||
</updateservers>
|
||||
|
||||
@@ -90,8 +80,6 @@ BRIEF: Plugin manifest XML file for MokoJGDPC
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
<field
|
||||
name="default_access"
|
||||
type="accesslevel"
|
||||
@@ -107,12 +95,7 @@ BRIEF: Plugin manifest XML file for MokoJGDPC
|
||||
label="PLG_SYSTEM_MOKOJGDPC_PERMISSIONS_TEMPLATE_LABEL"
|
||||
description="PLG_SYSTEM_MOKOJGDPC_PERMISSIONS_TEMPLATE_DESC"
|
||||
/>
|
||||
>>>>>>> main
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
<<<<<<< HEAD
|
||||
<!-- dev release trigger -->
|
||||
=======
|
||||
>>>>>>> main
|
||||
|
||||
+120
-287
@@ -8,7 +8,7 @@
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoJGDPC.System
|
||||
* INGROUP: MokoJGDPC
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJGDPC
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar
|
||||
* PATH: /src/script.php
|
||||
* VERSION: 01.00.00
|
||||
* BRIEF: Install/update/uninstall script — creates mapping table, seeds existing events
|
||||
@@ -24,39 +24,21 @@ use Joomla\CMS\Plugin\PluginHelper;
|
||||
|
||||
class plg_system_mokojgdpc_InstallerScript
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
/**
|
||||
* Runs on install — creates table then seeds categories for existing events.
|
||||
*/
|
||||
=======
|
||||
>>>>>>> main
|
||||
public function install(InstallerAdapter $adapter): bool
|
||||
{
|
||||
if (!$this->createTable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
$this->createTemplateCategory();
|
||||
$this->seedExistingEvents();
|
||||
=======
|
||||
$this->seedExistingEvents();
|
||||
$this->ensureScheduledTask();
|
||||
>>>>>>> main
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function update(InstallerAdapter $adapter): bool
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
return $this->createTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on uninstall — drops the mapping table.
|
||||
*/
|
||||
=======
|
||||
if (!$this->createTable()) {
|
||||
return false;
|
||||
}
|
||||
@@ -67,13 +49,10 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
return true;
|
||||
}
|
||||
|
||||
>>>>>>> main
|
||||
public function uninstall(InstallerAdapter $adapter): bool
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
// Remove scheduled task
|
||||
try {
|
||||
$query = $db->getQuery(true)
|
||||
@@ -85,7 +64,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
// Table may not exist on older Joomla
|
||||
}
|
||||
|
||||
>>>>>>> main
|
||||
try {
|
||||
$db->setQuery('DROP TABLE IF EXISTS ' . $db->quoteName('#__mokojgdpc_map'));
|
||||
$db->execute();
|
||||
@@ -93,15 +71,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
Log::add('MokoJGDPC: Failed to drop mapping table: ' . $e->getMessage(), Log::WARNING, 'jerror');
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all existing DPCalendar events and create JoomGallery categories for them.
|
||||
* Skips events that already have a mapping row.
|
||||
*/
|
||||
=======
|
||||
// Clean up throttle cache file
|
||||
$tmpPath = Factory::getApplication()->get('tmp_path', sys_get_temp_dir());
|
||||
$cacheFile = $tmpPath . '/mokojgdpc_lastrun';
|
||||
@@ -113,17 +82,12 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
return true;
|
||||
}
|
||||
|
||||
>>>>>>> main
|
||||
private function seedExistingEvents(): void
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
$tables = $db->getTableList();
|
||||
$prefix = $db->getPrefix();
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Bail if either DPCalendar or JoomGallery is not installed
|
||||
=======
|
||||
>>>>>>> main
|
||||
if (
|
||||
!\in_array($prefix . 'dpcalendar_events', $tables, true)
|
||||
|| !\in_array($prefix . 'joomgallery_categories', $tables, true)
|
||||
@@ -133,15 +97,10 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
return;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Read parent category from plugin params (defaults to root = 1)
|
||||
$parentId = 1;
|
||||
=======
|
||||
// Read plugin params
|
||||
$parentId = 1;
|
||||
$access = 1;
|
||||
$rules = '{}';
|
||||
>>>>>>> main
|
||||
$plugin = PluginHelper::getPlugin('system', 'mokojgdpc');
|
||||
|
||||
if ($plugin && !empty($plugin->params)) {
|
||||
@@ -150,13 +109,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
if (isset($pluginParams->parent_category)) {
|
||||
$parentId = (int) $pluginParams->parent_category;
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
}
|
||||
|
||||
// Fetch all published DPCalendar events not yet mapped
|
||||
$query = $db->getQuery(true)
|
||||
->select([$db->quoteName('e.id'), $db->quoteName('e.title')])
|
||||
=======
|
||||
|
||||
if (isset($pluginParams->default_access)) {
|
||||
$access = (int) $pluginParams->default_access;
|
||||
@@ -176,19 +128,14 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
$db->quoteName('e.title'),
|
||||
$db->quoteName('e.start_date'),
|
||||
])
|
||||
>>>>>>> main
|
||||
->from($db->quoteName('#__dpcalendar_events', 'e'))
|
||||
->leftJoin(
|
||||
$db->quoteName('#__mokojgdpc_map', 'm')
|
||||
. ' ON ' . $db->quoteName('m.event_id') . ' = ' . $db->quoteName('e.id')
|
||||
)
|
||||
->where($db->quoteName('m.id') . ' IS NULL')
|
||||
<<<<<<< HEAD
|
||||
->where($db->quoteName('e.state') . ' = 1');
|
||||
=======
|
||||
->where($db->quoteName('e.state') . ' = 1')
|
||||
->where('(' . $db->quoteName('e.original_id') . ' = 0 OR ' . $db->quoteName('e.original_id') . ' IS NULL)');
|
||||
>>>>>>> main
|
||||
|
||||
$db->setQuery($query);
|
||||
$events = $db->loadObjectList();
|
||||
@@ -199,10 +146,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
return;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Get parent node for nested set insertion
|
||||
=======
|
||||
>>>>>>> main
|
||||
$query = $db->getQuery(true)
|
||||
->select([$db->quoteName('lft'), $db->quoteName('rgt'), $db->quoteName('level')])
|
||||
->from($db->quoteName('#__joomgallery_categories'))
|
||||
@@ -219,216 +162,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
}
|
||||
|
||||
$seeded = 0;
|
||||
<<<<<<< HEAD
|
||||
$now = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');
|
||||
|
||||
foreach ($events as $event) {
|
||||
$title = trim($event->title);
|
||||
$alias = OutputFilter::stringURLSafe($title);
|
||||
|
||||
if (empty($alias)) {
|
||||
$alias = 'event-gallery-' . $event->id;
|
||||
}
|
||||
|
||||
// Re-read parent rgt each iteration (shifted by previous inserts)
|
||||
$query = $db->getQuery(true)
|
||||
->select([$db->quoteName('rgt'), $db->quoteName('level')])
|
||||
->from($db->quoteName('#__joomgallery_categories'))
|
||||
->where($db->quoteName('id') . ' = :pid')
|
||||
->bind(':pid', $parentId, \Joomla\Database\ParameterType::INTEGER);
|
||||
$db->setQuery($query);
|
||||
$parentNow = $db->loadObject();
|
||||
|
||||
if (!$parentNow) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$newLft = (int) $parentNow->rgt;
|
||||
$newRgt = $newLft + 1;
|
||||
$newLevel = (int) $parentNow->level + 1;
|
||||
|
||||
$db->transactionStart();
|
||||
|
||||
try {
|
||||
// Shift rgt
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__joomgallery_categories'))
|
||||
->set($db->quoteName('rgt') . ' = ' . $db->quoteName('rgt') . ' + 2')
|
||||
->where($db->quoteName('rgt') . ' >= :rgt')
|
||||
->bind(':rgt', $newLft, \Joomla\Database\ParameterType::INTEGER);
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
|
||||
// Shift lft
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__joomgallery_categories'))
|
||||
->set($db->quoteName('lft') . ' = ' . $db->quoteName('lft') . ' + 2')
|
||||
->where($db->quoteName('lft') . ' > :lft')
|
||||
->bind(':lft', $newLft, \Joomla\Database\ParameterType::INTEGER);
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
|
||||
// Insert category
|
||||
$category = (object) [
|
||||
'parent_id' => $parentId,
|
||||
'lft' => $newLft,
|
||||
'rgt' => $newRgt,
|
||||
'level' => $newLevel,
|
||||
'title' => $title,
|
||||
'alias' => $alias,
|
||||
'description' => '',
|
||||
'published' => 1,
|
||||
'access' => 1,
|
||||
'language' => '*',
|
||||
'created_by' => 0,
|
||||
'created_time' => $now,
|
||||
'modified_time' => $now,
|
||||
];
|
||||
|
||||
$db->insertObject('#__joomgallery_categories', $category, 'id');
|
||||
$newCatId = (int) $category->id;
|
||||
|
||||
// Insert mapping
|
||||
$map = (object) [
|
||||
'event_id' => (int) $event->id,
|
||||
'category_id' => $newCatId,
|
||||
];
|
||||
$db->insertObject('#__mokojgdpc_map', $map);
|
||||
|
||||
$db->transactionCommit();
|
||||
$seeded++;
|
||||
} catch (\Exception $e) {
|
||||
$db->transactionRollback();
|
||||
Log::add('MokoJGDPC: Seed failed for event #' . $event->id . ': ' . $e->getMessage(), Log::WARNING, 'plg_system_mokojgdpc');
|
||||
}
|
||||
}
|
||||
|
||||
Log::add('MokoJGDPC: Seeded ' . $seeded . ' gallery categories from existing events', Log::INFO, 'plg_system_mokojgdpc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a "DPCalendar Events" template category in JoomGallery and set it as the default parent.
|
||||
*/
|
||||
private function createTemplateCategory(): void
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
$tables = $db->getTableList();
|
||||
$prefix = $db->getPrefix();
|
||||
|
||||
if (!\in_array($prefix . 'joomgallery_categories', $tables, true)) {
|
||||
Log::add('MokoGalleryCalendar: JoomGallery not installed — skipping template category', Log::INFO, 'plg_system_mokojgdpc');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if template category already exists
|
||||
$query = $db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__joomgallery_categories'))
|
||||
->where($db->quoteName('alias') . ' = ' . $db->quote('dpcalendar-events'));
|
||||
$db->setQuery($query);
|
||||
$existingId = $db->loadResult();
|
||||
|
||||
if ($existingId) {
|
||||
$this->setPluginParentCategory((int) $existingId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get root category (id=1) for nested set insertion
|
||||
$rootId = 1;
|
||||
$query = $db->getQuery(true)
|
||||
->select([$db->quoteName('rgt'), $db->quoteName('level')])
|
||||
->from($db->quoteName('#__joomgallery_categories'))
|
||||
->where($db->quoteName('id') . ' = :rootId')
|
||||
->bind(':rootId', $rootId, \Joomla\Database\ParameterType::INTEGER);
|
||||
$db->setQuery($query);
|
||||
$root = $db->loadObject();
|
||||
|
||||
if (!$root) {
|
||||
Log::add('MokoGalleryCalendar: Root category not found', Log::WARNING, 'plg_system_mokojgdpc');
|
||||
return;
|
||||
}
|
||||
|
||||
$newLft = (int) $root->rgt;
|
||||
$newRgt = $newLft + 1;
|
||||
$newLevel = (int) $root->level + 1;
|
||||
$now = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');
|
||||
|
||||
$db->transactionStart();
|
||||
try {
|
||||
// Shift rgt
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__joomgallery_categories'))
|
||||
->set($db->quoteName('rgt') . ' = ' . $db->quoteName('rgt') . ' + 2')
|
||||
->where($db->quoteName('rgt') . ' >= :rgt')
|
||||
->bind(':rgt', $newLft, \Joomla\Database\ParameterType::INTEGER);
|
||||
$db->setQuery($query)->execute();
|
||||
|
||||
// Shift lft
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__joomgallery_categories'))
|
||||
->set($db->quoteName('lft') . ' = ' . $db->quoteName('lft') . ' + 2')
|
||||
->where($db->quoteName('lft') . ' > :lft')
|
||||
->bind(':lft', $newLft, \Joomla\Database\ParameterType::INTEGER);
|
||||
$db->setQuery($query)->execute();
|
||||
|
||||
// Insert template category
|
||||
$category = (object) [
|
||||
'parent_id' => $rootId,
|
||||
'lft' => $newLft,
|
||||
'rgt' => $newRgt,
|
||||
'level' => $newLevel,
|
||||
'title' => 'DPCalendar Events',
|
||||
'alias' => 'dpcalendar-events',
|
||||
'description' => 'Auto-created by MokoGalleryCalendar. Event photo galleries are created as sub-categories here.',
|
||||
'published' => 1,
|
||||
'access' => 1,
|
||||
'language' => '*',
|
||||
'created_by' => 0,
|
||||
'created_time' => $now,
|
||||
'modified_time' => $now,
|
||||
];
|
||||
$db->insertObject('#__joomgallery_categories', $category, 'id');
|
||||
|
||||
$db->transactionCommit();
|
||||
|
||||
$newId = (int) $category->id;
|
||||
$this->setPluginParentCategory($newId);
|
||||
Log::add('MokoGalleryCalendar: Created template category "DPCalendar Events" (ID: ' . $newId . ')', Log::INFO, 'plg_system_mokojgdpc');
|
||||
} catch (\Exception $e) {
|
||||
$db->transactionRollback();
|
||||
Log::add('MokoGalleryCalendar: Failed to create template category: ' . $e->getMessage(), Log::WARNING, 'plg_system_mokojgdpc');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent_category param on the plugin to the given category ID.
|
||||
*/
|
||||
private function setPluginParentCategory(int $categoryId): void
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->select('params')
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('mokojgdpc'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'));
|
||||
$db->setQuery($query);
|
||||
$params = json_decode($db->loadResult() ?: '{}', true);
|
||||
|
||||
$params['parent_category'] = $categoryId;
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__extensions'))
|
||||
->set($db->quoteName('params') . ' = ' . $db->quote(json_encode($params)))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('mokojgdpc'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'));
|
||||
$db->setQuery($query)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the event-to-category mapping table if it does not exist.
|
||||
*/
|
||||
=======
|
||||
$today = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d');
|
||||
$now = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');
|
||||
|
||||
@@ -522,6 +255,122 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
Log::add('MokoJGDPC: Seeded ' . $seeded . ' mappings from existing events', Log::INFO, 'plg_system_mokojgdpc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a "DPCalendar Events" template category in JoomGallery and set it as the default parent.
|
||||
*/
|
||||
private function createTemplateCategory(): void
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
$tables = $db->getTableList();
|
||||
$prefix = $db->getPrefix();
|
||||
|
||||
if (!\in_array($prefix . 'joomgallery_categories', $tables, true)) {
|
||||
Log::add('MokoGalleryCalendar: JoomGallery not installed — skipping template category', Log::INFO, 'plg_system_mokojgdpc');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if template category already exists
|
||||
$query = $db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__joomgallery_categories'))
|
||||
->where($db->quoteName('alias') . ' = ' . $db->quote('dpcalendar-events'));
|
||||
$db->setQuery($query);
|
||||
$existingId = $db->loadResult();
|
||||
|
||||
if ($existingId) {
|
||||
$this->setPluginParentCategory((int) $existingId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get root category (id=1) for nested set insertion
|
||||
$rootId = 1;
|
||||
$query = $db->getQuery(true)
|
||||
->select([$db->quoteName('rgt'), $db->quoteName('level')])
|
||||
->from($db->quoteName('#__joomgallery_categories'))
|
||||
->where($db->quoteName('id') . ' = :rootId')
|
||||
->bind(':rootId', $rootId, \Joomla\Database\ParameterType::INTEGER);
|
||||
$db->setQuery($query);
|
||||
$root = $db->loadObject();
|
||||
|
||||
if (!$root) {
|
||||
Log::add('MokoGalleryCalendar: Root category not found', Log::WARNING, 'plg_system_mokojgdpc');
|
||||
return;
|
||||
}
|
||||
|
||||
$newLft = (int) $root->rgt;
|
||||
$newRgt = $newLft + 1;
|
||||
$newLevel = (int) $root->level + 1;
|
||||
$now = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');
|
||||
|
||||
$db->transactionStart();
|
||||
try {
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__joomgallery_categories'))
|
||||
->set($db->quoteName('rgt') . ' = ' . $db->quoteName('rgt') . ' + 2')
|
||||
->where($db->quoteName('rgt') . ' >= :rgt')
|
||||
->bind(':rgt', $newLft, \Joomla\Database\ParameterType::INTEGER);
|
||||
$db->setQuery($query)->execute();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__joomgallery_categories'))
|
||||
->set($db->quoteName('lft') . ' = ' . $db->quoteName('lft') . ' + 2')
|
||||
->where($db->quoteName('lft') . ' > :lft')
|
||||
->bind(':lft', $newLft, \Joomla\Database\ParameterType::INTEGER);
|
||||
$db->setQuery($query)->execute();
|
||||
|
||||
$category = (object) [
|
||||
'parent_id' => $rootId,
|
||||
'lft' => $newLft,
|
||||
'rgt' => $newRgt,
|
||||
'level' => $newLevel,
|
||||
'title' => 'DPCalendar Events',
|
||||
'alias' => 'dpcalendar-events',
|
||||
'description' => 'Auto-created by Moko Gallery Calendar. Event photo galleries are created as sub-categories here.',
|
||||
'published' => 1,
|
||||
'access' => 1,
|
||||
'language' => '*',
|
||||
'created_by' => 0,
|
||||
'created_time' => $now,
|
||||
'modified_time' => $now,
|
||||
];
|
||||
$db->insertObject('#__joomgallery_categories', $category, 'id');
|
||||
|
||||
$db->transactionCommit();
|
||||
|
||||
$newId = (int) $category->id;
|
||||
$this->setPluginParentCategory($newId);
|
||||
Log::add('MokoGalleryCalendar: Created template category "DPCalendar Events" (ID: ' . $newId . ')', Log::INFO, 'plg_system_mokojgdpc');
|
||||
} catch (\Exception $e) {
|
||||
$db->transactionRollback();
|
||||
Log::add('MokoGalleryCalendar: Failed to create template category: ' . $e->getMessage(), Log::WARNING, 'plg_system_mokojgdpc');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent_category param on the plugin to the given category ID.
|
||||
*/
|
||||
private function setPluginParentCategory(int $categoryId): void
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->select('params')
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('mokojgdpc'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'));
|
||||
$db->setQuery($query);
|
||||
$params = json_decode($db->loadResult() ?: '{}', true);
|
||||
|
||||
$params['parent_category'] = $categoryId;
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__extensions'))
|
||||
->set($db->quoteName('params') . ' = ' . $db->quote(json_encode($params)))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('mokojgdpc'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'));
|
||||
$db->setQuery($query)->execute();
|
||||
}
|
||||
|
||||
private function uniqueAlias($db, string $title, int $eventId): string
|
||||
{
|
||||
$alias = OutputFilter::stringURLSafe($title);
|
||||
@@ -582,14 +431,12 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a daily scheduled task with Joomla's Task Scheduler (com_scheduler).
|
||||
* Idempotent — skips if the task already exists.
|
||||
* Register a daily scheduled task with Joomla's Task Scheduler.
|
||||
*/
|
||||
private function ensureScheduledTask(): void
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
|
||||
// Check if #__scheduler_tasks table exists (Joomla 4.1+)
|
||||
$tables = $db->getTableList();
|
||||
$prefix = $db->getPrefix();
|
||||
|
||||
@@ -601,7 +448,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
|
||||
$taskType = 'plg_system_mokojgdpc.process_pending';
|
||||
|
||||
// Check if task already registered
|
||||
$query = $db->getQuery(true)
|
||||
->select('COUNT(*)')
|
||||
->from($db->quoteName('#__scheduler_tasks'))
|
||||
@@ -614,11 +460,10 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert a daily task — runs at 2:00 AM server time
|
||||
$now = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');
|
||||
|
||||
$task = (object) [
|
||||
'title' => 'MokoJGDPC: Process pending gallery categories',
|
||||
'title' => 'Moko Gallery Calendar: Process pending gallery categories',
|
||||
'type' => $taskType,
|
||||
'execution_rules' => json_encode([
|
||||
'rule-type' => 'interval-days',
|
||||
@@ -629,7 +474,7 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
'state' => 1,
|
||||
'cli_exclusive' => 0,
|
||||
'params' => '{}',
|
||||
'note' => 'Auto-registered by MokoJGDPC plugin. Creates JoomGallery categories for DPCalendar events whose date has arrived.',
|
||||
'note' => 'Auto-registered by Moko Gallery Calendar plugin.',
|
||||
'created' => $now,
|
||||
'next_execution' => $now,
|
||||
'times_executed' => 0,
|
||||
@@ -646,7 +491,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
}
|
||||
}
|
||||
|
||||
>>>>>>> main
|
||||
private function createTable(): bool
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
@@ -654,13 +498,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
$query = 'CREATE TABLE IF NOT EXISTS ' . $db->quoteName('#__mokojgdpc_map') . ' ('
|
||||
. $db->quoteName('id') . ' INT UNSIGNED NOT NULL AUTO_INCREMENT, '
|
||||
. $db->quoteName('event_id') . ' INT UNSIGNED NOT NULL COMMENT \'DPCalendar event ID\', '
|
||||
<<<<<<< HEAD
|
||||
. $db->quoteName('category_id') . ' INT UNSIGNED NOT NULL COMMENT \'JoomGallery category ID\', '
|
||||
. $db->quoteName('created') . ' DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, '
|
||||
. 'PRIMARY KEY (' . $db->quoteName('id') . '), '
|
||||
. 'UNIQUE KEY ' . $db->quoteName('idx_event') . ' (' . $db->quoteName('event_id') . '), '
|
||||
. 'KEY ' . $db->quoteName('idx_category') . ' (' . $db->quoteName('category_id') . ')'
|
||||
=======
|
||||
. $db->quoteName('category_id') . ' INT UNSIGNED NOT NULL DEFAULT 0 COMMENT \'JoomGallery category ID (0 = pending)\', '
|
||||
. $db->quoteName('event_date') . ' DATE NULL DEFAULT NULL COMMENT \'Event start date — category created when this date arrives\', '
|
||||
. $db->quoteName('created') . ' DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, '
|
||||
@@ -668,7 +505,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
. 'UNIQUE KEY ' . $db->quoteName('idx_event') . ' (' . $db->quoteName('event_id') . '), '
|
||||
. 'KEY ' . $db->quoteName('idx_category') . ' (' . $db->quoteName('category_id') . '), '
|
||||
. 'KEY ' . $db->quoteName('idx_pending') . ' (' . $db->quoteName('category_id') . ', ' . $db->quoteName('event_date') . ')'
|
||||
>>>>>>> main
|
||||
. ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci';
|
||||
|
||||
try {
|
||||
@@ -682,8 +518,6 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
|
||||
return true;
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
private function migrateSchema(): void
|
||||
{
|
||||
@@ -713,5 +547,4 @@ class plg_system_mokojgdpc_InstallerScript
|
||||
}
|
||||
}
|
||||
}
|
||||
>>>>>>> main
|
||||
}
|
||||
|
||||
+16
-16
@@ -1,7 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
VERSION: 01.03.00
|
||||
VERSION: 01.01.00
|
||||
-->
|
||||
|
||||
<updates>
|
||||
@@ -10,15 +10,15 @@
|
||||
<description>plg_system_mokojgdpc update</description>
|
||||
<element>mokojgdpc</element>
|
||||
<type>plugin</type>
|
||||
<version>01.03.00</version>
|
||||
<version>01.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>development</tag></tags>
|
||||
<infourl title="plg_system_mokojgdpc">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/plg_system_mokojgdpc-01.03.00.zip</downloadurl>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/mokojgdpc-01.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>c1173a65cda03345d313484457062871c0eb39039cf2844830c8221aad126e82</sha256>
|
||||
<sha256>ae7f485ad9469c5bb231ace49393f0d5dee4b0250d1beca165d5c77620edc0f0</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
@@ -28,15 +28,15 @@
|
||||
<description>plg_system_mokojgdpc update</description>
|
||||
<element>mokojgdpc</element>
|
||||
<type>plugin</type>
|
||||
<version>01.03.00</version>
|
||||
<version>01.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>alpha</tag></tags>
|
||||
<infourl title="plg_system_mokojgdpc">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/plg_system_mokojgdpc-01.03.00.zip</downloadurl>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/mokojgdpc-01.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>c1173a65cda03345d313484457062871c0eb39039cf2844830c8221aad126e82</sha256>
|
||||
<sha256>ae7f485ad9469c5bb231ace49393f0d5dee4b0250d1beca165d5c77620edc0f0</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
@@ -46,15 +46,15 @@
|
||||
<description>plg_system_mokojgdpc update</description>
|
||||
<element>mokojgdpc</element>
|
||||
<type>plugin</type>
|
||||
<version>01.03.00</version>
|
||||
<version>01.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>beta</tag></tags>
|
||||
<infourl title="plg_system_mokojgdpc">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/plg_system_mokojgdpc-01.03.00.zip</downloadurl>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/mokojgdpc-01.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>c1173a65cda03345d313484457062871c0eb39039cf2844830c8221aad126e82</sha256>
|
||||
<sha256>ae7f485ad9469c5bb231ace49393f0d5dee4b0250d1beca165d5c77620edc0f0</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
@@ -64,15 +64,15 @@
|
||||
<description>plg_system_mokojgdpc update</description>
|
||||
<element>mokojgdpc</element>
|
||||
<type>plugin</type>
|
||||
<version>01.03.00</version>
|
||||
<version>01.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>rc</tag></tags>
|
||||
<infourl title="plg_system_mokojgdpc">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/plg_system_mokojgdpc-01.03.00.zip</downloadurl>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/mokojgdpc-01.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>c1173a65cda03345d313484457062871c0eb39039cf2844830c8221aad126e82</sha256>
|
||||
<sha256>ae7f485ad9469c5bb231ace49393f0d5dee4b0250d1beca165d5c77620edc0f0</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
@@ -82,15 +82,15 @@
|
||||
<description>plg_system_mokojgdpc update</description>
|
||||
<element>mokojgdpc</element>
|
||||
<type>plugin</type>
|
||||
<version>01.03.00</version>
|
||||
<version>01.01.00</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder>
|
||||
<tags><tag>stable</tag></tags>
|
||||
<infourl title="plg_system_mokojgdpc">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/plg_system_mokojgdpc-01.03.00.zip</downloadurl>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGalleryCalendar/releases/download/stable/mokojgdpc-01.01.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>c1173a65cda03345d313484457062871c0eb39039cf2844830c8221aad126e82</sha256>
|
||||
<sha256>ae7f485ad9469c5bb231ace49393f0d5dee4b0250d1beca165d5c77620edc0f0</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
|
||||
Reference in New Issue
Block a user