Joomla aborts the entire package install on any SQL error in update
files. DROP COLUMN fails when catid doesn't exist (fresh installs,
or systems where it was already removed). Since install.mysql.sql
already omits catid, no runtime migration is needed.
Authored-by: Moko Consulting
Joomla's SQL update runner doesn't support DELIMITER or stored
procedures. DROP COLUMN IF EXISTS is MySQL 8.0.13+ only. Plain
DROP COLUMN is safe here because update files only run on upgrades
from versions that had the catid column.
Authored-by: Moko Consulting
- Add Leaflet map to location detail page with marker and popup (#57)
- Implement Leaflet.markercluster with toggleable module parameter (#61)
- Convert inline <script> to $wa->addInlineScript() for CSP nonce support (#34)
- Replace category INNER JOIN with EXISTS subquery for ONLY_FULL_GROUP_BY compat (#59)
- Add delete() override to LocationTable and CategoryTable for junction cleanup (#60)
- Drop dead catid column and idx_catid index via SQL update 01.00.02 (#58)
- Update CHANGELOG and README
Authored-by: Moko Consulting
Joomla's autoloader namespace scanner expects plugin manifests to be
named {element}.xml (e.g. mokosuitestorelocator.xml), not the full
prefixed name (plg_webservices_mokosuitestorelocator.xml). Without the
correct filename, the PSR-4 namespace mapping is not registered in
autoload_psr4.php, causing "Class not found" errors.
Pattern confirmed from MokoSuiteClient's webservices plugin.
Authored-by: Moko Consulting
Joomla's PluginAdapter requires plugin="xxx" on a <files> child to
determine filesystem deployment path and extension element. Without it,
plugin files are not deployed and the element field is empty in
#__extensions. Pattern confirmed from MokoSuiteClient.
Authored-by: Moko Consulting
Joomla's ModuleAdapter requires the `module` attribute on a <files> child
to identify the module element. Without it, install fails with "No module
file specified" even when namespace and services/provider.php are present.
Pattern confirmed against MokoSuiteClient modules.
Also hardcodes the package description since packages don't load language
files during install, causing the raw key to display.
Authored-by: Moko Consulting
Both modules were missing services/provider.php — the Joomla 5 DI
entry point that registers the ModuleDispatcherFactory. Without it,
Joomla falls back to a mod_*.php entry point file that doesn't exist,
causing "No module file specified" on install.
Also moves access.xml into admin/ and adds it to the component
manifest so it gets included in the package ZIP.
Authored-by: Moko Consulting
Multi-category support with parent/child hierarchy, junction table,
admin CRUD, and per-category custom marker icons on the Leaflet map.
REST API via Web Services plugin with JSON:API endpoints. ACL
permissions via access.xml. SQL update schema for safe upgrades.
Shop integration bridge (LocationBridgeHelper, LocationSavedEvent).
Security: CSV formula injection prevention, MIME validation, file size
limit, ORDER BY allowlist, map height CSS regex validation.
Authored-by: Moko Consulting
- Fix geocoding trigger: use isset+is_numeric instead of !empty for
coordinate detection (same 0.0 bug pattern as Haversine fix)
- Sanitize tel: href to digits/+/-/() only (prevents URI injection)
- Use Factory::getApplication() for ACL check (consistent with codebase)
Authored-by: Moko Consulting
Haversine proximity search:
- LocationsModel filters by distance using Haversine formula
- populateState captures lat/lng/radius/radius_unit from search form
- Distance-sorted results when proximity filter is active
- Hidden radius_unit field added to search module form
Get Directions:
- Google Maps directions link on location detail page (no API key)
- Directions link in Leaflet popup markers
Auto-geocoding:
- LocationModel::save() override auto-geocodes empty coordinates
- Calls Nominatim/OSM API when address present but coords missing
- Success/failure messages via Joomla enqueueMessage
CSV Import:
- ImportController handles file upload with CSRF token check
- ImportModel parses CSV via SplFileObject with configurable delimiter
- Auto-detects column headers (title/name, address/street, city, etc.)
- Per-row validation via LocationTable::bind()->check()->store()
- Import view with upload form, delimiter picker, and column help
- Toolbar button and submenu item for import access
Addresses #54
Authored-by: Moko Consulting
Security:
- Fix stored XSS in Leaflet popup — HTML-escape loc.title/address/phone
- Use HTMLHelper::_('content.prepare') for description output
Joomla 5/6 compatibility:
- Bump PHP minimum to 8.2, Joomla minimum to 5.0.0
- script.php implements InstallerScriptInterface with typed signatures
- Restore updateservers and dlid in package manifest
- Update all manifest creationDates to 2026-06-23
Code quality:
- Replace hard-coded English errors with Text::_() language strings
- Add COM_MOKOJOOMSTORELOCATOR_ERROR_* language keys
- Use Text::_() for Locations list toolbar title
- Import missing Text class in LocationTable and Locations HtmlView
Documentation:
- Update CLAUDE.md: MokoSuite naming, source/ paths, PHP 8.2, namespace
- Update README: Joomla 5/6, PHP 8.2+, MySQL 8.0+
Authored-by: Moko Consulting
Map module:
- Dispatcher queries published locations with coordinates from DB
- Leaflet.js + OpenStreetMap tile layer integration
- Markers with popup info (name, address, phone)
- Auto-fit bounds to show all markers
- Leaflet CSS/JS via Joomla Web Asset Manager
Search module:
- Dispatcher loads distinct cities/states for dropdown filters
- Builds radius options from module params
- City dropdown filter (toggled by show_city_filter param)
- Radius dropdown with configurable values and unit (miles/km)
- Geolocation "Use My Location" button via browser API
- Hidden lat/lng fields for proximity search passthrough
- New language strings for all search UI elements
Both dispatchers now implement DatabaseAwareInterface for DB access.
Addresses #51, #3, #35
Authored-by: Moko Consulting
Build the complete public-facing frontend for the store locator:
- Site DisplayController routing to list and detail views
- LocationsModel with search, city, and state filters (published only)
- LocationModel for single location by ID
- Locations list template with Schema.org LocalBusiness markup
- Location detail template with address, contact, hours, map placeholder
- SEF URL router with menu/standard/nomenu rules
- Menu item types: "All Locations" list and "Location Detail" picker
- Site language strings for views and menu items
- Router wired into component extension class and service provider
- .gitignore exception for source/packages/*/site/
Addresses #50
Authored-by: Moko Consulting
Add full admin editing capability for store locations:
- LocationController (FormController) for save/cancel/apply
- LocationsController (AdminController) for bulk publish/unpublish/delete
- Location edit view with tabbed form (Details, Address, Contact)
- Admin list renders data rows with edit links and published toggle
- LocationTable::check() validates title, auto-alias, lat/lng, timestamps
- LocationsModel with populateState(), search/published filters, ordering
- filter_locations.xml with search tools bar
- Language strings for filters, sort options, save messages
Also includes MokoSuite-renamed package scaffolding (source/packages/).
Removes unused Makefile.
Closes#32 — populateState for filter persistence
Closes#33 — filter XML forms for admin list
Authored-by: Moko Consulting