diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml
index 2570789..eefafbf 100644
--- a/.github/workflows/auto-release.yml
+++ b/.github/workflows/auto-release.yml
@@ -58,7 +58,7 @@ permissions:
jobs:
release:
name: Build & Release Pipeline
- runs-on: ubuntu-latest
+ runs-on: release
if: >-
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
@@ -75,6 +75,10 @@ jobs:
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_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
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
/tmp/mokostandards-api
@@ -564,7 +568,6 @@ jobs:
new_downloads = (
" \n"
f" {zip_url}\n"
- f" {tar_url}\n"
" "
)
block = re.sub(r' .*?', new_downloads, block, flags=re.DOTALL)
@@ -592,69 +595,29 @@ jobs:
--author="gitea-actions[bot] " || true
git push || true
- # Sync updates.xml to main via PR (respects branch protection)
- if [ "$CURRENT_BRANCH" != "main" ]; then
- GA_TOKEN="${{ secrets.GA_TOKEN }}"
- API="${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}"
- PR_BRANCH="chore/update-xml-${VERSION}"
+ # Sync updates.xml to main via direct API (always runs — may be on version/XX branch)
+ GA_TOKEN="${{ secrets.GA_TOKEN }}"
+ API="${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}"
- # Create branch from main
- MAIN_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
- "${API}/branches/main" | jq -r '.commit.sha // empty')
+ FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
+ "${API}/contents/updates.xml?ref=main" | jq -r '.sha // empty')
- if [ -n "$MAIN_SHA" ]; then
- curl -sf -X POST -H "Authorization: token ${GA_TOKEN}" \
- -H "Content-Type: application/json" \
- "${API}/branches" \
- -d "$(jq -n --arg name "$PR_BRANCH" --arg sha "$MAIN_SHA" \
- '{new_branch_name: $name, old_branch_name: "main"}')" > /dev/null 2>&1 || true
-
- # Update updates.xml on the PR branch
- FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
- "${API}/contents/updates.xml?ref=${PR_BRANCH}" | jq -r '.sha // empty')
-
- if [ -n "$FILE_SHA" ]; then
- CONTENT=$(base64 -w0 updates.xml)
- curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \
- -H "Content-Type: application/json" \
- "${API}/contents/updates.xml" \
- -d "$(jq -n \
- --arg content "$CONTENT" \
- --arg sha "$FILE_SHA" \
- --arg msg "chore: update stable channel to ${VERSION} [skip ci]" \
- --arg branch "$PR_BRANCH" \
- '{content: $content, sha: $sha, message: $msg, branch: $branch}'
- )" > /dev/null 2>&1
-
- # Create PR
- PR_URL=$(curl -sf -X POST -H "Authorization: token ${GA_TOKEN}" \
- -H "Content-Type: application/json" \
- "${API}/pulls" \
- -d "$(jq -n \
- --arg title "chore: update updates.xml for ${VERSION} [skip ci]" \
- --arg head "$PR_BRANCH" \
- --arg base "main" \
- --arg body "Auto-generated by release workflow. Updates updates.xml with SHA-256 and download URLs for ${VERSION}." \
- '{title: $title, head: $head, base: $base, body: $body}'
- )" | jq -r '.number // empty')
-
- # Auto-merge the PR
- if [ -n "$PR_URL" ]; then
- curl -sf -X POST -H "Authorization: token ${GA_TOKEN}" \
- -H "Content-Type: application/json" \
- "${API}/pulls/${PR_URL}/merge" \
- -d '{"Do":"merge","merge_message_field":"chore: update updates.xml for '"${VERSION}"' [skip ci]"}' > /dev/null 2>&1 \
- && echo "updates.xml synced to main via PR #${PR_URL}" \
- || echo "PR #${PR_URL} created but auto-merge failed — merge manually"
-
- # Cleanup: delete PR branch
- curl -sf -X DELETE -H "Authorization: token ${GA_TOKEN}" \
- "${API}/branches/${PR_BRANCH}" > /dev/null 2>&1 || true
- else
- echo "WARNING: failed to create PR for updates.xml sync"
- fi
- fi
- fi
+ if [ -n "$FILE_SHA" ]; then
+ CONTENT=$(base64 -w0 updates.xml)
+ curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \
+ -H "Content-Type: application/json" \
+ "${API}/contents/updates.xml" \
+ -d "$(jq -n \
+ --arg content "$CONTENT" \
+ --arg sha "$FILE_SHA" \
+ --arg msg "chore: sync updates.xml ${VERSION} [skip ci]" \
+ --arg branch "main" \
+ '{content: $content, sha: $sha, message: $msg, branch: $branch}'
+ )" > /dev/null 2>&1 \
+ && echo "updates.xml synced to main via API" \
+ || echo "WARNING: failed to sync updates.xml to main"
+ else
+ echo "WARNING: could not get updates.xml SHA from main"
fi
fi
diff --git a/.github/workflows/update-payload.yml b/.github/workflows/update-payload.yml
index 6a82008..d047ceb 100644
--- a/.github/workflows/update-payload.yml
+++ b/.github/workflows/update-payload.yml
@@ -1,4 +1,4 @@
-name: Update MokoCassiopeia Payload
+name: Update MokoOnyx Payload
on:
push:
@@ -14,25 +14,25 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Get latest MokoCassiopeia release URL
+ - name: Get latest MokoOnyx stable release URL
id: moko
run: |
- DOWNLOAD_URL=$(curl -s https://api.github.com/repos/mokoconsulting-tech/MokoCassiopeia/releases \
- | jq -r '[.[] | select(.prerelease == false and .draft == false and (.assets | length > 0)][0].assets[0].browser_download_url')
+ DOWNLOAD_URL=$(curl -s https://git.mokoconsulting.tech/api/v1/repos/MokoConsulting/MokoOnyx/releases \
+ | jq -r '[.[] | select(.prerelease == false and .draft == false and (.assets | length > 0))][0].assets[0].browser_download_url')
echo "url=$DOWNLOAD_URL" >> $GITHUB_OUTPUT
echo "Found: $DOWNLOAD_URL"
- - name: Download MokoCassiopeia zip
+ - name: Download MokoOnyx zip
if: steps.moko.outputs.url != 'null'
run: |
mkdir -p src/payload
- curl -sL "${{ steps.moko.outputs.url }}" -o src/payload/mokocassiopeia.zip
+ curl -sL "${{ steps.moko.outputs.url }}" -o src/payload/mokoonyx.zip
ls -la src/payload/
- name: Check if payload changed
id: diff
run: |
- git add src/payload/mokocassiopeia.zip
+ git add src/payload/mokoonyx.zip
if git diff --cached --quiet; then
echo "changed=false" >> $GITHUB_OUTPUT
else
@@ -44,5 +44,5 @@ jobs:
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- git commit -m "chore: update mokocassiopeia payload"
+ git commit -m "chore: update mokoonyx payload"
git push
diff --git a/.github/workflows/update-server.yml b/.github/workflows/update-server.yml
index cd9b515..f2972eb 100644
--- a/.github/workflows/update-server.yml
+++ b/.github/workflows/update-server.yml
@@ -13,16 +13,27 @@
# Writes updates.xml with multiple entries:
# - stable on push to main (from auto-release)
# - rc on push to rc/**
-# - development on push to dev/**
+# - development 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/**'
@@ -56,9 +67,9 @@ permissions:
jobs:
update-xml:
name: Update updates.xml
- runs-on: ubuntu-latest
+ runs-on: release
if: >-
- github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
+ github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
steps:
- name: Checkout repository
@@ -73,6 +84,9 @@ jobs:
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_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
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
@@ -109,7 +123,7 @@ jobs:
STABILITY="beta"
elif [[ "$BRANCH" == alpha/* ]]; then
STABILITY="alpha"
- elif [[ "$BRANCH" == dev/* ]]; then
+ elif [[ "$BRANCH" == dev/* ]] || [[ "$BRANCH" == "dev" ]]; then
STABILITY="development"
else
STABILITY="stable"
@@ -271,9 +285,7 @@ jobs:
NEW_ENTRY="${NEW_ENTRY} \n"
NEW_ENTRY="${NEW_ENTRY} ${INFO_URL}\n"
NEW_ENTRY="${NEW_ENTRY} \n"
- TAR_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
NEW_ENTRY="${NEW_ENTRY} ${DOWNLOAD_URL}\n"
- NEW_ENTRY="${NEW_ENTRY} ${TAR_URL}\n"
NEW_ENTRY="${NEW_ENTRY} \n"
[ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} ${SHA256}\n"
NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n"
@@ -367,6 +379,33 @@ jobs:
git push
}
+ # -- Sync updates.xml to main (for non-main branches) ----------------------
+ - name: Sync updates.xml to main
+ if: github.ref_name != 'main'
+ run: |
+ 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
+
# -- Mirror to GitHub (stable and rc only) --------------------------------
- name: Mirror release to GitHub
if: >-
@@ -388,7 +427,7 @@ jobs:
done
- name: SFTP deploy to dev server
- if: contains(github.ref, 'dev/')
+ if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
env:
DEV_HOST: ${{ vars.DEV_FTP_HOST }}
DEV_PATH: ${{ vars.DEV_FTP_PATH }}
diff --git a/.gitignore b/.gitignore
index 01449c1..4229e70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,8 @@ TODO.md
.env
.env.local
.env.*.local
+.joomla-api-mcp.json
+.mcp.json
*.local.php
*.secret.php
configuration.php
@@ -100,6 +102,7 @@ replit.md
*.tar.gz
*.tgz
*.zip
+!src/payload/*.zip
artifacts/
release/
releases/
diff --git a/README.md b/README.md
index 1c43871..e25489a 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
DEFGROUP: Joomla.Plugin
INGROUP: MokoWaaS
REPO: https://github.com/mokoconsulting-tech/mokowaas
- VERSION: 02.01.15
+ VERSION: 02.01.20
PATH: /README.md
BRIEF: Rebranding plugin for MokoWaaS platform
NOTE: Internal WaaS identity abstraction layer
@@ -23,7 +23,7 @@
# MokoWaaS Plugin
-[](https://github.com/mokoconsulting-tech/MokoWaaS/releases/tag/v02)
+[](https://github.com/mokoconsulting-tech/MokoWaaS/releases/tag/v02)
[](LICENSE)
[](https://www.joomla.org)
[](https://www.php.net)
@@ -322,22 +322,18 @@ See [LICENSE.md](LICENSE.md) for the full license text.
This extension follows the [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) version governance model using semantic versioning: `MAJOR.MINOR.PATCH`
-Current version: **01.04.00**
+Current version: **02.01.18**
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for a complete version history.
-### Recent Changes (v01.04.00 - 2026-02-22)
+### Recent Changes (v02.01.18 - 2026-04-23)
-- Added complete Joomla 5.x system plugin implementation
-- Created main plugin class with event handlers
-- Implemented plugin manifest with Joomla 5.x namespace support
-- Added dependency injection service provider
-- Created plugin language files
-- Integrated with language override system
-- Enhanced language overrides (57+ strings)
-- Fixed typo in error messages (OCCURRED)
+- Always install and lock MokoOnyx template on install/update
+- Always unlock MokoCassiopeia on install/update (allow uninstall)
+- Bundle MokoOnyx payload (replaces MokoCassiopeia payload)
+- Update payload workflow to fetch MokoOnyx from Gitea releases
## Contributing
diff --git a/src/Extension/MokoWaaS.php b/src/Extension/MokoWaaS.php
index 84575d4..e67eb2f 100644
--- a/src/Extension/MokoWaaS.php
+++ b/src/Extension/MokoWaaS.php
@@ -80,6 +80,9 @@ class MokoWaaS extends CMSPlugin
// Security: HTTPS redirect (runs for all clients)
$this->enforceHttps();
+ // Dev mode: disable caching
+ $this->enforceDevMode();
+
// Admin-only WaaS controls
if ($this->app->isClient('administrator'))
{
@@ -965,6 +968,27 @@ class MokoWaaS extends CMSPlugin
*
* @since 02.01.08
*/
+ /**
+ * Disable caching when development mode is active.
+ *
+ * Sets the Joomla caching config to 0 at runtime so no page
+ * or component cache is used. Does not modify configuration.php.
+ *
+ * @return void
+ *
+ * @since 02.01.15
+ */
+ protected function enforceDevMode()
+ {
+ if (!$this->params->get('dev_mode', 0))
+ {
+ return;
+ }
+
+ $config = Factory::getConfig();
+ $config->set('caching', 0);
+ }
+
protected function enforceHttps()
{
if (!$this->params->get('force_https', 0))
@@ -1529,8 +1553,78 @@ class MokoWaaS extends CMSPlugin
*/
protected function injectColorScheme($doc)
{
- // Colors are applied via Atum template style params in
- // enforceAtumBranding(). No additional CSS injection needed.
+ $primary = $this->params->get('color_primary', '');
+ $brandIcon = $this->params->get('brand_icon', '');
+ $css = '';
+
+ // Brand button colors derived from primary color
+ if (!empty($primary))
+ {
+ $primary = htmlspecialchars($primary, ENT_QUOTES, 'UTF-8');
+
+ $css .= '.btn-primary,'
+ . '.btn-primary:not(:disabled):not(.disabled){'
+ . 'background-color:' . $primary . ';'
+ . 'border-color:' . $primary . ';'
+ . 'color:#000;'
+ . 'box-shadow:0 2px 4px rgba(0,0,0,.15);}'
+ . '.btn-primary:hover,.btn-primary:focus{'
+ . 'background-color:' . $primary . ';'
+ . 'border-color:' . $primary . ';'
+ . 'color:#000;'
+ . 'filter:brightness(0.85);'
+ . 'box-shadow:0 4px 8px rgba(0,0,0,.2);}'
+ . '.btn-primary:active,.btn-primary.active{'
+ . 'background-color:' . $primary . ';'
+ . 'border-color:' . $primary . ';'
+ . 'color:#000;'
+ . 'filter:brightness(0.75);}'
+ . '.btn-outline-primary{'
+ . 'color:' . $primary . ';'
+ . 'border-color:' . $primary . ';'
+ . 'border-width:2px;}'
+ . '.btn-outline-primary:hover,.btn-outline-primary:focus{'
+ . 'background-color:' . $primary . ';'
+ . 'border-color:' . $primary . ';'
+ . 'color:#000;}';
+ }
+
+ // Drawer toggle button: flat edge on the sidebar-facing side
+ // Close button in drawer: styled to match brand
+ $css .= '[dir="ltr"] .main-nav-container .menu-collapse,'
+ . '[dir="ltr"] .sidebar-toggle{'
+ . 'border-top-left-radius:0!important;'
+ . 'border-bottom-left-radius:0!important;}'
+ . '[dir="rtl"] .main-nav-container .menu-collapse,'
+ . '[dir="rtl"] .sidebar-toggle{'
+ . 'border-top-right-radius:0!important;'
+ . 'border-bottom-right-radius:0!important;}'
+ . '.sidebar-wrapper .btn-close,'
+ . '.sidebar-wrapper [data-bs-dismiss]{'
+ . 'opacity:1;'
+ . 'filter:none;'
+ . 'font-size:1.1rem;}';
+
+ // Brand icon override (FontAwesome unicode codepoint)
+ if (!empty($brandIcon))
+ {
+ $brandIcon = preg_replace('/[^a-fA-F0-9]/', '', $brandIcon);
+
+ if (!empty($brandIcon))
+ {
+ $css .= '.icon-joomla::before,'
+ . '.icon-brands.fa-joomla::before{'
+ . 'font-family:"Font Awesome 6 Free"!important;'
+ . 'content:"\\' . $brandIcon . '"!important;'
+ . 'font-weight:900!important;}';
+ }
+ }
+
+ if (!empty(trim($css)))
+ {
+ $css = str_replace('', '', $css);
+ $doc->addStyleDeclaration($css);
+ }
}
/**
diff --git a/src/administrator/language/overrides/en-GB.override.ini b/src/administrator/language/overrides/en-GB.override.ini
index 7832edd..ca5081b 100644
--- a/src/administrator/language/overrides/en-GB.override.ini
+++ b/src/administrator/language/overrides/en-GB.override.ini
@@ -107,3 +107,14 @@ JLIB_INSTALLER_ERROR_NOTFINDJOOMLAXMLSETUPFILE="Installer: Can't find {{BRAND_NA
JLIB_HTML_POWERED_BY="Powered by {{BRAND_NAME}}"
COM_ADMIN_HELP_DOCUMENTATION="{{BRAND_NAME}} Documentation"
COM_ADMIN_HELP_SUPPORT="{{BRAND_NAME}} Support"
+
+; ===== Akeeba Ticket System (ATS) =====
+COM_ATS="{{BRAND_NAME}} Tickets"
+COM_ATS_TITLE_TICKETS="{{BRAND_NAME}} Tickets"
+COM_ATS_TITLE_TICKET="{{BRAND_NAME}} Ticket"
+COM_ATS_TITLE_NEWTICKET="New {{BRAND_NAME}} Ticket"
+COM_ATS_TITLE_CATEGORIES="Ticket Categories"
+COM_ATS_MSG_TICKET_SAVED="Your {{BRAND_NAME}} ticket has been saved."
+COM_ATS_MSG_TICKET_CLOSED="Your {{BRAND_NAME}} ticket has been closed."
+COM_ATS_MSG_REPLY_SAVED="Your reply has been saved."
+COM_ATS_LBL_POWEREDBY="Powered by {{BRAND_NAME}}"
diff --git a/src/administrator/language/overrides/en-US.override.ini b/src/administrator/language/overrides/en-US.override.ini
index dd17beb..02fc67f 100644
--- a/src/administrator/language/overrides/en-US.override.ini
+++ b/src/administrator/language/overrides/en-US.override.ini
@@ -107,3 +107,14 @@ JLIB_INSTALLER_ERROR_NOTFINDJOOMLAXMLSETUPFILE="Installer: Can't find {{BRAND_NA
JLIB_HTML_POWERED_BY="Powered by {{BRAND_NAME}}"
COM_ADMIN_HELP_DOCUMENTATION="{{BRAND_NAME}} Documentation"
COM_ADMIN_HELP_SUPPORT="{{BRAND_NAME}} Support"
+
+; ===== Akeeba Ticket System (ATS) =====
+COM_ATS="{{BRAND_NAME}} Tickets"
+COM_ATS_TITLE_TICKETS="{{BRAND_NAME}} Tickets"
+COM_ATS_TITLE_TICKET="{{BRAND_NAME}} Ticket"
+COM_ATS_TITLE_NEWTICKET="New {{BRAND_NAME}} Ticket"
+COM_ATS_TITLE_CATEGORIES="Ticket Categories"
+COM_ATS_MSG_TICKET_SAVED="Your {{BRAND_NAME}} ticket has been saved."
+COM_ATS_MSG_TICKET_CLOSED="Your {{BRAND_NAME}} ticket has been closed."
+COM_ATS_MSG_REPLY_SAVED="Your reply has been saved."
+COM_ATS_LBL_POWEREDBY="Powered by {{BRAND_NAME}}"
diff --git a/src/language/en-GB/plg_system_mokowaas.ini b/src/language/en-GB/plg_system_mokowaas.ini
index 9ab299c..8ba246f 100644
--- a/src/language/en-GB/plg_system_mokowaas.ini
+++ b/src/language/en-GB/plg_system_mokowaas.ini
@@ -55,6 +55,9 @@ PLG_SYSTEM_MOKOWAAS_ALLOWED_IPS_NOTE_DESC="Emergency access requires an IP white
PLG_SYSTEM_MOKOWAAS_FIELDSET_MAINTENANCE_LABEL="Maintenance"
PLG_SYSTEM_MOKOWAAS_FIELDSET_MAINTENANCE_DESC="One-time maintenance actions. Set to Yes and save to execute. Resets to No automatically after execution."
+PLG_SYSTEM_MOKOWAAS_DEV_MODE_LABEL="Development Mode"
+PLG_SYSTEM_MOKOWAAS_DEV_MODE_DESC="Disables all Joomla caching at runtime. Useful during development and testing. Does not modify configuration.php."
+
PLG_SYSTEM_MOKOWAAS_RESET_HITS_LABEL="Reset All Hits"
PLG_SYSTEM_MOKOWAAS_RESET_HITS_DESC="Set all article hit counters to zero across the site. This action executes on save and resets to No."
PLG_SYSTEM_MOKOWAAS_DELETE_VERSIONS_LABEL="Delete All Versions"
@@ -74,6 +77,8 @@ PLG_SYSTEM_MOKOWAAS_COLOR_HEADER_LABEL="Header Color"
PLG_SYSTEM_MOKOWAAS_COLOR_HEADER_DESC="Background color for the admin top header bar."
PLG_SYSTEM_MOKOWAAS_COLOR_LINK_LABEL="Link Color"
PLG_SYSTEM_MOKOWAAS_COLOR_LINK_DESC="Color for hyperlinks in the admin interface."
+PLG_SYSTEM_MOKOWAAS_BRAND_ICON_LABEL="Brand Icon (FontAwesome)"
+PLG_SYSTEM_MOKOWAAS_BRAND_ICON_DESC="FontAwesome unicode codepoint for the brand icon that replaces the Joomla logo icon. Enter the hex code only (e.g. f6d5 for fa-hat-cowboy). Find codes at fontawesome.com/icons."
PLG_SYSTEM_MOKOWAAS_CUSTOM_CSS_LABEL="Custom CSS"
PLG_SYSTEM_MOKOWAAS_CUSTOM_CSS_DESC="Additional CSS injected into admin pages. Use for fine-tuning visual presentation."
diff --git a/src/language/en-US/plg_system_mokowaas.ini b/src/language/en-US/plg_system_mokowaas.ini
index 9ab299c..8ba246f 100644
--- a/src/language/en-US/plg_system_mokowaas.ini
+++ b/src/language/en-US/plg_system_mokowaas.ini
@@ -55,6 +55,9 @@ PLG_SYSTEM_MOKOWAAS_ALLOWED_IPS_NOTE_DESC="Emergency access requires an IP white
PLG_SYSTEM_MOKOWAAS_FIELDSET_MAINTENANCE_LABEL="Maintenance"
PLG_SYSTEM_MOKOWAAS_FIELDSET_MAINTENANCE_DESC="One-time maintenance actions. Set to Yes and save to execute. Resets to No automatically after execution."
+PLG_SYSTEM_MOKOWAAS_DEV_MODE_LABEL="Development Mode"
+PLG_SYSTEM_MOKOWAAS_DEV_MODE_DESC="Disables all Joomla caching at runtime. Useful during development and testing. Does not modify configuration.php."
+
PLG_SYSTEM_MOKOWAAS_RESET_HITS_LABEL="Reset All Hits"
PLG_SYSTEM_MOKOWAAS_RESET_HITS_DESC="Set all article hit counters to zero across the site. This action executes on save and resets to No."
PLG_SYSTEM_MOKOWAAS_DELETE_VERSIONS_LABEL="Delete All Versions"
@@ -74,6 +77,8 @@ PLG_SYSTEM_MOKOWAAS_COLOR_HEADER_LABEL="Header Color"
PLG_SYSTEM_MOKOWAAS_COLOR_HEADER_DESC="Background color for the admin top header bar."
PLG_SYSTEM_MOKOWAAS_COLOR_LINK_LABEL="Link Color"
PLG_SYSTEM_MOKOWAAS_COLOR_LINK_DESC="Color for hyperlinks in the admin interface."
+PLG_SYSTEM_MOKOWAAS_BRAND_ICON_LABEL="Brand Icon (FontAwesome)"
+PLG_SYSTEM_MOKOWAAS_BRAND_ICON_DESC="FontAwesome unicode codepoint for the brand icon that replaces the Joomla logo icon. Enter the hex code only (e.g. f6d5 for fa-hat-cowboy). Find codes at fontawesome.com/icons."
PLG_SYSTEM_MOKOWAAS_CUSTOM_CSS_LABEL="Custom CSS"
PLG_SYSTEM_MOKOWAAS_CUSTOM_CSS_DESC="Additional CSS injected into admin pages. Use for fine-tuning visual presentation."
diff --git a/src/language/overrides/en-GB.override.ini b/src/language/overrides/en-GB.override.ini
index 2e5381d..d773e7b 100644
--- a/src/language/overrides/en-GB.override.ini
+++ b/src/language/overrides/en-GB.override.ini
@@ -53,3 +53,14 @@ JLIB_APPLICATION_ERROR_COMPONENT_NOT_FOUND="Component not found."
; ===== Version and About =====
JLIB_HTML_POWERED_BY="Powered by {{BRAND_NAME}}"
+
+; ===== Akeeba Ticket System (ATS) =====
+COM_ATS="{{BRAND_NAME}} Tickets"
+COM_ATS_TITLE_TICKETS="{{BRAND_NAME}} Tickets"
+COM_ATS_TITLE_TICKET="{{BRAND_NAME}} Ticket"
+COM_ATS_TITLE_NEWTICKET="New {{BRAND_NAME}} Ticket"
+COM_ATS_TITLE_CATEGORIES="Ticket Categories"
+COM_ATS_MSG_TICKET_SAVED="Your {{BRAND_NAME}} ticket has been saved."
+COM_ATS_MSG_TICKET_CLOSED="Your {{BRAND_NAME}} ticket has been closed."
+COM_ATS_MSG_REPLY_SAVED="Your reply has been saved."
+COM_ATS_LBL_POWEREDBY="Powered by {{BRAND_NAME}}"
diff --git a/src/language/overrides/en-US.override.ini b/src/language/overrides/en-US.override.ini
index d015444..a23405e 100644
--- a/src/language/overrides/en-US.override.ini
+++ b/src/language/overrides/en-US.override.ini
@@ -53,3 +53,14 @@ JLIB_APPLICATION_ERROR_COMPONENT_NOT_FOUND="Component not found."
; ===== Version and About =====
JLIB_HTML_POWERED_BY="Powered by {{BRAND_NAME}}"
+
+; ===== Akeeba Ticket System (ATS) =====
+COM_ATS="{{BRAND_NAME}} Tickets"
+COM_ATS_TITLE_TICKETS="{{BRAND_NAME}} Tickets"
+COM_ATS_TITLE_TICKET="{{BRAND_NAME}} Ticket"
+COM_ATS_TITLE_NEWTICKET="New {{BRAND_NAME}} Ticket"
+COM_ATS_TITLE_CATEGORIES="Ticket Categories"
+COM_ATS_MSG_TICKET_SAVED="Your {{BRAND_NAME}} ticket has been saved."
+COM_ATS_MSG_TICKET_CLOSED="Your {{BRAND_NAME}} ticket has been closed."
+COM_ATS_MSG_REPLY_SAVED="Your reply has been saved."
+COM_ATS_LBL_POWEREDBY="Powered by {{BRAND_NAME}}"
diff --git a/src/mokowaas.xml b/src/mokowaas.xml
index 65aceec..784927b 100644
--- a/src/mokowaas.xml
+++ b/src/mokowaas.xml
@@ -162,6 +162,13 @@
label="PLG_SYSTEM_MOKOWAAS_FIELDSET_MAINTENANCE_LABEL"
description="PLG_SYSTEM_MOKOWAAS_FIELDSET_MAINTENANCE_DESC"
>
+
+
+
+
+
getQuery(true)
- ->select([$db->quoteName('extension_id'), $db->quoteName('enabled')])
+ ->select($db->quoteName('extension_id'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('element') . ' = ' . $db->quote('mokoonyx'))
->where($db->quoteName('type') . ' = ' . $db->quote('template'));
$onyx = $db->setQuery($query)->loadObject();
+ // Install from payload if missing
+ if (!$onyx)
+ {
+ $this->installMokoOnyxFromPayload();
+
+ // Re-check after install
+ $onyx = $db->setQuery($query)->loadObject();
+ }
+
if ($onyx)
{
// Lock and protect MokoOnyx
@@ -245,73 +254,49 @@ class plgSystemMokoWaaSInstallerScript implements InstallerScriptInterface
)->execute();
$this->setDefaultTemplate('mokoonyx', 0);
-
- // Unlock MokoCassiopeia if present (allow uninstall)
- $db->setQuery(
- $db->getQuery(true)
- ->update($db->quoteName('#__extensions'))
- ->set($db->quoteName('locked') . ' = 0')
- ->set($db->quoteName('protected') . ' = 0')
- ->where($db->quoteName('element') . ' = ' . $db->quote('mokocassiopeia'))
- ->where($db->quoteName('type') . ' = ' . $db->quote('template'))
- )->execute();
-
- return;
}
- // Fallback: MokoCassiopeia
- $query = $db->getQuery(true)
- ->select([$db->quoteName('extension_id'), $db->quoteName('enabled')])
- ->from($db->quoteName('#__extensions'))
- ->where($db->quoteName('element') . ' = ' . $db->quote('mokocassiopeia'))
- ->where($db->quoteName('type') . ' = ' . $db->quote('template'));
- $template = $db->setQuery($query)->loadObject();
+ // Always unlock MokoCassiopeia (allow uninstall)
+ $db->setQuery(
+ $db->getQuery(true)
+ ->update($db->quoteName('#__extensions'))
+ ->set($db->quoteName('locked') . ' = 0')
+ ->set($db->quoteName('protected') . ' = 0')
+ ->where($db->quoteName('element') . ' = ' . $db->quote('mokocassiopeia'))
+ ->where($db->quoteName('type') . ' = ' . $db->quote('template'))
+ )->execute();
+ }
- if ($template)
- {
- // Lock, protect, and set as default
- $db->setQuery(
- $db->getQuery(true)
- ->update($db->quoteName('#__extensions'))
- ->set($db->quoteName('enabled') . ' = 1')
- ->set($db->quoteName('locked') . ' = 1')
- ->set($db->quoteName('protected') . ' = 1')
- ->where($db->quoteName('extension_id') . ' = '
- . (int) $template->extension_id)
- );
- $db->execute();
-
- $this->setDefaultTemplate('mokocassiopeia', 0);
-
- return;
- }
-
- // Template not installed — install from bundled payload
+ /**
+ * Install MokoOnyx from the bundled payload zip.
+ *
+ * @return void
+ *
+ * @since 02.01.17
+ */
+ private function installMokoOnyxFromPayload()
+ {
$pluginPath = JPATH_PLUGINS . '/system/mokowaas';
- $payloadZip = $pluginPath . '/payload/mokocassiopeia.zip';
+ $payloadZip = $pluginPath . '/payload/mokoonyx.zip';
if (!file_exists($payloadZip))
{
Factory::getApplication()->enqueueMessage(
- 'MokoCassiopeia payload not found at '
- . $payloadZip,
+ 'MokoOnyx payload not found. Please install MokoOnyx manually from '
+ . 'Gitea Releases.',
'warning'
);
return;
}
- $tmpDir = JPATH_ROOT . '/tmp/mokocassiopeia';
+ $tmpDir = JPATH_ROOT . '/tmp/mokoonyx';
try
{
-
- // Extract the bundled zip
$archive = new \Joomla\Archive\Archive();
$archive->extract($payloadZip, $tmpDir);
- // Release zips should have templateDetails.xml at root
- // or one level deep
$installDir = $tmpDir;
if (!file_exists($tmpDir . '/templateDetails.xml'))
@@ -325,8 +310,7 @@ class plgSystemMokoWaaSInstallerScript implements InstallerScriptInterface
else
{
Factory::getApplication()->enqueueMessage(
- 'MokoCassiopeia: templateDetails.xml not '
- . 'found in archive.',
+ 'MokoOnyx: templateDetails.xml not found in archive.',
'warning'
);
@@ -338,17 +322,15 @@ class plgSystemMokoWaaSInstallerScript implements InstallerScriptInterface
if ($installer->install($installDir))
{
- $this->ensureMokoCassiopeia();
-
Factory::getApplication()->enqueueMessage(
- 'MokoCassiopeia installed and locked.',
+ 'MokoOnyx installed successfully.',
'message'
);
}
else
{
Factory::getApplication()->enqueueMessage(
- 'MokoCassiopeia installation failed.',
+ 'MokoOnyx installation from payload failed.',
'warning'
);
}
@@ -356,7 +338,7 @@ class plgSystemMokoWaaSInstallerScript implements InstallerScriptInterface
catch (\Exception $e)
{
Factory::getApplication()->enqueueMessage(
- 'MokoCassiopeia error: ' . $e->getMessage(),
+ 'MokoOnyx error: ' . $e->getMessage(),
'warning'
);
}
diff --git a/updates.xml b/updates.xml
index b1dce64..ab97bcd 100644
--- a/updates.xml
+++ b/updates.xml
@@ -11,7 +11,7 @@
System - MokoWaaS (development)
mokowaas
plugin
- 02.01.15-dev
+ 02.01.20-dev
site
system
@@ -19,10 +19,9 @@
https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS
- https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/mokowaas-02.01.15-dev.zip
- https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/mokowaas-02.01.15-dev.tar.gz
+ https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/mokowaas-02.01.20-dev.zip
- 7fcd567f42dcdfee58aeb6fc82f904e1cd970be6ba2fae3690d279d7e48737ed
+ edb7ccb285d9d5889a559c55b85e2555006c0b41040ddf0f8a4dd34f1ca0f14b
Moko Consulting
https://mokoconsulting.tech