#106 — BatchController now paginates by id cursor (WHERE c.id > :lastId)
instead of always querying offset 0. A row that fails to insert falls behind
the cursor and is not re-fetched, so the batch always terminates and reaches
100% even with persistent failures. process() returns examined + last_id; the
editor JS drives the cursor and stops when a chunk examines 0 rows.
#100 — Sitemap:
- Throttle: regenerate at most once per 60s on content save (SITEMAP_MIN_INTERVAL)
so bulk edits/imports don't rebuild the whole file every save
- SEF URLs: route each article via Route::link('site', ...) with a fallback to
the non-SEF index.php URL if routing fails (worst case = prior behavior)
(access-level filtering + atomic write were done earlier in the cycle)
#95 — ACL + Options:
- Add access.xml (core actions + mokoog.batch / mokoog.import custom actions)
- Add config.xml (Permissions tab + note pointing settings to the system plugin)
- Declare both in the manifest; Batch/ImportExport controllers now check the
custom actions with a fallback to the prior core checks (no lockout)
#103 — CSV import is now reachable:
- Add an Import toolbar button that toggles a multipart file-upload form
(jform[csv_file]) posting to importexport.import with a CSRF token
#104 — Dead code + disk leak:
- Delete unused ImageGenerator class and JsonLdBuilder::buildOrganization()
- Add ImageHelper::pruneOldFiles() (deletes generated images older than 30d)
and call it on content save so the generated-image cache is bounded
#107 — Packaging:
- Declare language/en-US in the component manifest (was never installed)
- Remove undeclared empty stub dirs src/Field, src/Service
Removes accessors deprecated in Joomla 5 and slated for removal in 7
(extension already runs on 6; this future-proofs for 7):
- Factory::getDbo() -> Factory::getContainer()->get(DatabaseInterface::class)
across plugins, controllers, static helpers, and the install script
- Factory::getUser() -> Factory::getApplication()->getIdentity()
- Factory::getSession() -> Factory::getApplication()->getSession()
- jexit(Text::_('JINVALID_TOKEN')) -> throw new \RuntimeException(..., 403),
consistent with the access-denied checks already in those controllers
Note: SQL update-version concern is already resolved — the release bumped
to 01.05.00, which matches the 01.05.00.sql update slot.
- Add exception logging to BatchController batch skip (#76)
- Align form maxlength with DB schema limits (#77)
- applySeoTags() already uses public API — no change needed (#78)
- Add strip_tags() input sanitization on OG text fields (#79)
- Add missing language field to batch-generated records
- Wrap batch insert in try-catch to handle duplicate key races
- Add logging to all empty catch blocks (script.php, MokoOG license check)
- Guard loadShopProduct() with try-catch for missing MokoSuiteShop tables
- Guard reviews query in JsonLdBuilder for missing #__mokoshop_reviews
- Cap batch process limit to 200 per request to prevent DoS (#42)
- Add TagTable::check() validation: og_type enum, field max lengths,
canonical_url format, robots directives, content_type pattern (#43)
- Add language column to CSV export headers and data (#52)
- Parse language column on CSV import with format validation
- Include language in duplicate check query to match unique key
- Rename project source directory from src/ to source/
- Update CI workflows (ci-joomla, pr-check, repo-health) to check
source/ first, falling back to src/ and htdocs/ for compat
- Update .gitignore vendor exception path
- manifest.xml entry-point already updated
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>