diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index eb67f8f..355c4ed 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: MokoGitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 01.02.38 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index f984fc6..4511090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.2.0] - Unreleased ### Added +- **Admin menu short-name constant** — the Administrator → Components menu now shows a compact label via `COM_MOKOSUITESTORELOCATOR_SHORT` ("Store Locator"); defined in the admin sys.ini (#75) — also renamed the `COM_MOKOJOOMSTORELOCATOR` language-key stem to `COM_MOKOSUITESTORELOCATOR`. - Multi-category support with parent/child hierarchy (#1) - Categories admin CRUD — list, edit, color picker, custom marker icon - Location-category junction table (many-to-many) @@ -51,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - SQL migration compatibility: removed `DROP COLUMN IF EXISTS` (MySQL 8.0.13+ only) in favor of plain `DROP COLUMN` +- Honest installer success: package postflight now verifies every bundled child extension (component, modules, plugin) actually landed in `#__extensions` before showing the license/next-steps message; if any is missing it shows an error and suppresses the success message instead of reporting a false success. Fails open so a transient DB/IO glitch never fakes a failure. ## [1.1.0] - 2026-06-23 diff --git a/CLAUDE.md b/CLAUDE.md index 100d18d..d524c63 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code when working with this repository. ## Project Overview -**MokoSuiteStoreLocator** -- A Joomla 5/6 package providing a store locator listing component with coordinating map and search modules. +**MokoSuiteStoreLocator** -- A Joomla 6 package providing a store locator listing component with coordinating map and search modules. | Field | Value | |---|---| diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index cd89cde..9fe3a3a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: INGROUP: Project.Documentation REPO: - VERSION: 01.02.00 + VERSION: 01.02.38 PATH: ./CODE_OF_CONDUCT.md BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default --> diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 095cdf2..fbb8627 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,12 +19,12 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.02.00 + VERSION: 01.02.38 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> -[![MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/wiki)](https://github.com/mokoconsulting-tech/MokoStandards) +[![MokoStandards](https://img.shields.io/badge/MokoStandards-04.00.04-blue)](https://github.com/mokoconsulting-tech/MokoStandards) # Project Governance @@ -32,7 +32,7 @@ This document defines the governance model for the `Template-Joomla` repository within the `mokoconsulting-tech` organization. It is automatically maintained by -[MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/wiki) v04.00.04. +[MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) v04.00.04. Full governance policy is defined in the MokoStandards source repository: [docs/policy/GOVERNANCE.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/GOVERNANCE.md) diff --git a/README.md b/README.md index 51dcca5..a5778c6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteStoreLocator -A Joomla 5/6 package providing a store locator listing component with coordinating map and search modules. +A Joomla 6 package providing a store locator listing component with coordinating map and search modules. ## Package Contents @@ -13,8 +13,8 @@ A Joomla 5/6 package providing a store locator listing component with coordinati ## Requirements -- Joomla 5.x or 6.x -- PHP 8.2+ +- Joomla 6 +- PHP 8.3+ - MySQL 8.0+ / MariaDB 10.4+ ## Installation diff --git a/SECURITY.md b/SECURITY.md index 1cf7776..e896ea1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME] INGROUP: [PROJECT_NAME].Documentation REPO: [REPOSITORY_URL] PATH: /SECURITY.md -VERSION: 01.02.00 +VERSION: 01.02.38 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/composer.json b/composer.json index 10afe3a..1f03461 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { "squizlabs/php_codesniffer": "^3.7", diff --git a/source/packages/com_mokosuitestorelocator/admin/forms/category.xml b/source/packages/com_mokosuitestorelocator/admin/forms/category.xml index 2453fab..cefd57c 100644 --- a/source/packages/com_mokosuitestorelocator/admin/forms/category.xml +++ b/source/packages/com_mokosuitestorelocator/admin/forms/category.xml @@ -26,11 +26,11 @@ - + -
+
diff --git a/source/packages/com_mokosuitestorelocator/admin/forms/filter_categories.xml b/source/packages/com_mokosuitestorelocator/admin/forms/filter_categories.xml index 04b3ff3..4d20b79 100644 --- a/source/packages/com_mokosuitestorelocator/admin/forms/filter_categories.xml +++ b/source/packages/com_mokosuitestorelocator/admin/forms/filter_categories.xml @@ -4,7 +4,7 @@ diff --git a/source/packages/com_mokosuitestorelocator/admin/forms/filter_locations.xml b/source/packages/com_mokosuitestorelocator/admin/forms/filter_locations.xml index 6e36331..bcb1b21 100644 --- a/source/packages/com_mokosuitestorelocator/admin/forms/filter_locations.xml +++ b/source/packages/com_mokosuitestorelocator/admin/forms/filter_locations.xml @@ -4,7 +4,7 @@ @@ -29,8 +29,8 @@ > - - + + diff --git a/source/packages/com_mokosuitestorelocator/admin/forms/location.xml b/source/packages/com_mokosuitestorelocator/admin/forms/location.xml index 2816f1e..b1b34bf 100644 --- a/source/packages/com_mokosuitestorelocator/admin/forms/location.xml +++ b/source/packages/com_mokosuitestorelocator/admin/forms/location.xml @@ -43,58 +43,58 @@
-
+
-
+
-
+
-
+
@@ -128,23 +128,23 @@
-
+
diff --git a/source/packages/com_mokosuitestorelocator/admin/language/en-GB/com_mokosuitestorelocator.ini b/source/packages/com_mokosuitestorelocator/admin/language/en-GB/com_mokosuitestorelocator.ini index d5d3a53..82127eb 100644 --- a/source/packages/com_mokosuitestorelocator/admin/language/en-GB/com_mokosuitestorelocator.ini +++ b/source/packages/com_mokosuitestorelocator/admin/language/en-GB/com_mokosuitestorelocator.ini @@ -3,79 +3,79 @@ ; License: GNU General Public License version 3 or later; see LICENSE COM_MOKOSUITESTORELOCATOR="MokoSuite Store Locator" -COM_MOKOJOOMSTORELOCATOR="Store Locator" -COM_MOKOJOOMSTORELOCATOR_DESC="A store locator component for managing and displaying location listings." -COM_MOKOJOOMSTORELOCATOR_LOCATIONS="Locations" -COM_MOKOJOOMSTORELOCATOR_LOCATION_NEW="New Location" -COM_MOKOJOOMSTORELOCATOR_LOCATION_EDIT="Edit Location" -COM_MOKOJOOMSTORELOCATOR_TABLE_CAPTION="Store Location List" +COM_MOKOSUITESTORELOCATOR_SHORT="Store Locator" +COM_MOKOSUITESTORELOCATOR_DESC="A store locator component for managing and displaying location listings." +COM_MOKOSUITESTORELOCATOR_LOCATIONS="Locations" +COM_MOKOSUITESTORELOCATOR_LOCATION_NEW="New Location" +COM_MOKOSUITESTORELOCATOR_LOCATION_EDIT="Edit Location" +COM_MOKOSUITESTORELOCATOR_TABLE_CAPTION="Store Location List" -COM_MOKOJOOMSTORELOCATOR_CITY="City" -COM_MOKOJOOMSTORELOCATOR_STATE="State" +COM_MOKOSUITESTORELOCATOR_CITY="City" +COM_MOKOSUITESTORELOCATOR_STATE="State" -COM_MOKOJOOMSTORELOCATOR_FIELDSET_ADDRESS="Address" -COM_MOKOJOOMSTORELOCATOR_FIELDSET_COORDINATES="Coordinates" -COM_MOKOJOOMSTORELOCATOR_FIELDSET_CONTACT="Contact Information" -COM_MOKOJOOMSTORELOCATOR_FIELDSET_IMAGE="Image" +COM_MOKOSUITESTORELOCATOR_FIELDSET_ADDRESS="Address" +COM_MOKOSUITESTORELOCATOR_FIELDSET_COORDINATES="Coordinates" +COM_MOKOSUITESTORELOCATOR_FIELDSET_CONTACT="Contact Information" +COM_MOKOSUITESTORELOCATOR_FIELDSET_IMAGE="Image" -COM_MOKOJOOMSTORELOCATOR_FIELD_ADDRESS="Street Address" -COM_MOKOJOOMSTORELOCATOR_FIELD_CITY="City" -COM_MOKOJOOMSTORELOCATOR_FIELD_STATE="State / Province" -COM_MOKOJOOMSTORELOCATOR_FIELD_POSTCODE="Postal Code" -COM_MOKOJOOMSTORELOCATOR_FIELD_COUNTRY="Country" -COM_MOKOJOOMSTORELOCATOR_FIELD_LATITUDE="Latitude" -COM_MOKOJOOMSTORELOCATOR_FIELD_LONGITUDE="Longitude" -COM_MOKOJOOMSTORELOCATOR_FIELD_PHONE="Phone" -COM_MOKOJOOMSTORELOCATOR_FIELD_WEBSITE="Website" -COM_MOKOJOOMSTORELOCATOR_FIELD_HOURS="Business Hours" -COM_MOKOJOOMSTORELOCATOR_FIELD_IMAGE="Location Image" +COM_MOKOSUITESTORELOCATOR_FIELD_ADDRESS="Street Address" +COM_MOKOSUITESTORELOCATOR_FIELD_CITY="City" +COM_MOKOSUITESTORELOCATOR_FIELD_STATE="State / Province" +COM_MOKOSUITESTORELOCATOR_FIELD_POSTCODE="Postal Code" +COM_MOKOSUITESTORELOCATOR_FIELD_COUNTRY="Country" +COM_MOKOSUITESTORELOCATOR_FIELD_LATITUDE="Latitude" +COM_MOKOSUITESTORELOCATOR_FIELD_LONGITUDE="Longitude" +COM_MOKOSUITESTORELOCATOR_FIELD_PHONE="Phone" +COM_MOKOSUITESTORELOCATOR_FIELD_WEBSITE="Website" +COM_MOKOSUITESTORELOCATOR_FIELD_HOURS="Business Hours" +COM_MOKOSUITESTORELOCATOR_FIELD_IMAGE="Location Image" -COM_MOKOJOOMSTORELOCATOR_FILTER_SEARCH_LABEL="Search Locations" -COM_MOKOJOOMSTORELOCATOR_CITY_ASC="City ascending" -COM_MOKOJOOMSTORELOCATOR_CITY_DESC="City descending" +COM_MOKOSUITESTORELOCATOR_FILTER_SEARCH_LABEL="Search Locations" +COM_MOKOSUITESTORELOCATOR_CITY_ASC="City ascending" +COM_MOKOSUITESTORELOCATOR_CITY_DESC="City descending" -COM_MOKOJOOMSTORELOCATOR_LOCATION_SAVE_SUCCESS="Location saved successfully." -COM_MOKOJOOMSTORELOCATOR_LOCATIONS_N_ITEMS_PUBLISHED="%d location(s) published." -COM_MOKOJOOMSTORELOCATOR_LOCATIONS_N_ITEMS_UNPUBLISHED="%d location(s) unpublished." -COM_MOKOJOOMSTORELOCATOR_LOCATIONS_N_ITEMS_DELETED="%d location(s) deleted." +COM_MOKOSUITESTORELOCATOR_LOCATION_SAVE_SUCCESS="Location saved successfully." +COM_MOKOSUITESTORELOCATOR_LOCATIONS_N_ITEMS_PUBLISHED="%d location(s) published." +COM_MOKOSUITESTORELOCATOR_LOCATIONS_N_ITEMS_UNPUBLISHED="%d location(s) unpublished." +COM_MOKOSUITESTORELOCATOR_LOCATIONS_N_ITEMS_DELETED="%d location(s) deleted." -COM_MOKOJOOMSTORELOCATOR_ERROR_TITLE_REQUIRED="A location title is required." -COM_MOKOJOOMSTORELOCATOR_ERROR_LATITUDE_RANGE="Latitude must be between -90 and 90." -COM_MOKOJOOMSTORELOCATOR_ERROR_LONGITUDE_RANGE="Longitude must be between -180 and 180." +COM_MOKOSUITESTORELOCATOR_ERROR_TITLE_REQUIRED="A location title is required." +COM_MOKOSUITESTORELOCATOR_ERROR_LATITUDE_RANGE="Latitude must be between -90 and 90." +COM_MOKOSUITESTORELOCATOR_ERROR_LONGITUDE_RANGE="Longitude must be between -180 and 180." -COM_MOKOJOOMSTORELOCATOR_GEOCODING_SUCCESS="Coordinates were auto-populated from the address via OpenStreetMap." -COM_MOKOJOOMSTORELOCATOR_GEOCODING_FAILED="Geocoding failed: %s. You can enter coordinates manually." -COM_MOKOJOOMSTORELOCATOR_GET_DIRECTIONS="Get Directions" +COM_MOKOSUITESTORELOCATOR_GEOCODING_SUCCESS="Coordinates were auto-populated from the address via OpenStreetMap." +COM_MOKOSUITESTORELOCATOR_GEOCODING_FAILED="Geocoding failed: %s. You can enter coordinates manually." +COM_MOKOSUITESTORELOCATOR_GET_DIRECTIONS="Get Directions" -COM_MOKOJOOMSTORELOCATOR_IMPORT="Import Locations" -COM_MOKOJOOMSTORELOCATOR_IMPORT_DESC="Import store locations from a CSV file." -COM_MOKOJOOMSTORELOCATOR_IMPORT_UPLOAD="Upload CSV File" -COM_MOKOJOOMSTORELOCATOR_IMPORT_FILE="CSV File" -COM_MOKOJOOMSTORELOCATOR_IMPORT_FILE_DESC="Select a CSV file with location data. First row must be column headers." -COM_MOKOJOOMSTORELOCATOR_IMPORT_DELIMITER="Delimiter" -COM_MOKOJOOMSTORELOCATOR_IMPORT_DELIMITER_DESC="The character separating fields in your CSV file." -COM_MOKOJOOMSTORELOCATOR_IMPORT_SUCCESS="%d location(s) imported successfully." -COM_MOKOJOOMSTORELOCATOR_IMPORT_SKIPPED="%d row(s) skipped due to errors." -COM_MOKOJOOMSTORELOCATOR_IMPORT_NO_FILE="No file was uploaded." -COM_MOKOJOOMSTORELOCATOR_IMPORT_INVALID_FILE="The uploaded file is not a valid CSV." -COM_MOKOJOOMSTORELOCATOR_IMPORT_NO_ROWS="The CSV file contains no data rows." -COM_MOKOJOOMSTORELOCATOR_IMPORT_MISSING_TITLE="Row %d: Title is required." +COM_MOKOSUITESTORELOCATOR_IMPORT="Import Locations" +COM_MOKOSUITESTORELOCATOR_IMPORT_DESC="Import store locations from a CSV file." +COM_MOKOSUITESTORELOCATOR_IMPORT_UPLOAD="Upload CSV File" +COM_MOKOSUITESTORELOCATOR_IMPORT_FILE="CSV File" +COM_MOKOSUITESTORELOCATOR_IMPORT_FILE_DESC="Select a CSV file with location data. First row must be column headers." +COM_MOKOSUITESTORELOCATOR_IMPORT_DELIMITER="Delimiter" +COM_MOKOSUITESTORELOCATOR_IMPORT_DELIMITER_DESC="The character separating fields in your CSV file." +COM_MOKOSUITESTORELOCATOR_IMPORT_SUCCESS="%d location(s) imported successfully." +COM_MOKOSUITESTORELOCATOR_IMPORT_SKIPPED="%d row(s) skipped due to errors." +COM_MOKOSUITESTORELOCATOR_IMPORT_NO_FILE="No file was uploaded." +COM_MOKOSUITESTORELOCATOR_IMPORT_INVALID_FILE="The uploaded file is not a valid CSV." +COM_MOKOSUITESTORELOCATOR_IMPORT_NO_ROWS="The CSV file contains no data rows." +COM_MOKOSUITESTORELOCATOR_IMPORT_MISSING_TITLE="Row %d: Title is required." -COM_MOKOJOOMSTORELOCATOR_IMPORT_FP_TITLE="Import from FocalPoint" -COM_MOKOJOOMSTORELOCATOR_IMPORT_FP_DESC="Migrate locations from an installed FocalPoint (Shack Locations) component. Coordinates, custom fields (email, website, hours), and metadata are mapped automatically." -COM_MOKOJOOMSTORELOCATOR_IMPORT_FP_BUTTON="Import FocalPoint Locations" -COM_MOKOJOOMSTORELOCATOR_IMPORT_FP_SUCCESS="%d location(s) imported from FocalPoint." -COM_MOKOJOOMSTORELOCATOR_IMPORT_FILE_TOO_LARGE="The uploaded file exceeds the 2 MB size limit." +COM_MOKOSUITESTORELOCATOR_IMPORT_FP_TITLE="Import from FocalPoint" +COM_MOKOSUITESTORELOCATOR_IMPORT_FP_DESC="Migrate locations from an installed FocalPoint (Shack Locations) component. Coordinates, custom fields (email, website, hours), and metadata are mapped automatically." +COM_MOKOSUITESTORELOCATOR_IMPORT_FP_BUTTON="Import FocalPoint Locations" +COM_MOKOSUITESTORELOCATOR_IMPORT_FP_SUCCESS="%d location(s) imported from FocalPoint." +COM_MOKOSUITESTORELOCATOR_IMPORT_FILE_TOO_LARGE="The uploaded file exceeds the 2 MB size limit." -COM_MOKOJOOMSTORELOCATOR_CATEGORIES="Categories" -COM_MOKOJOOMSTORELOCATOR_CATEGORY_NEW="New Category" -COM_MOKOJOOMSTORELOCATOR_CATEGORY_EDIT="Edit Category" -COM_MOKOJOOMSTORELOCATOR_CATEGORY_PARENT="Parent Category" -COM_MOKOJOOMSTORELOCATOR_CATEGORY_NO_PARENT="- No Parent -" -COM_MOKOJOOMSTORELOCATOR_CATEGORY_COLOR="Color" -COM_MOKOJOOMSTORELOCATOR_CATEGORY_MARKER_ICON="Custom Marker Icon" -COM_MOKOJOOMSTORELOCATOR_CATEGORIES_TABLE_CAPTION="Store Location Categories" -COM_MOKOJOOMSTORELOCATOR_ERROR_CATEGORY_TITLE_REQUIRED="A category title is required." +COM_MOKOSUITESTORELOCATOR_CATEGORIES="Categories" +COM_MOKOSUITESTORELOCATOR_CATEGORY_NEW="New Category" +COM_MOKOSUITESTORELOCATOR_CATEGORY_EDIT="Edit Category" +COM_MOKOSUITESTORELOCATOR_CATEGORY_PARENT="Parent Category" +COM_MOKOSUITESTORELOCATOR_CATEGORY_NO_PARENT="- No Parent -" +COM_MOKOSUITESTORELOCATOR_CATEGORY_COLOR="Color" +COM_MOKOSUITESTORELOCATOR_CATEGORY_MARKER_ICON="Custom Marker Icon" +COM_MOKOSUITESTORELOCATOR_CATEGORIES_TABLE_CAPTION="Store Location Categories" +COM_MOKOSUITESTORELOCATOR_ERROR_CATEGORY_TITLE_REQUIRED="A category title is required." -COM_MOKOJOOMSTORELOCATOR_FIELDSET_CATEGORIES="Categories" -COM_MOKOJOOMSTORELOCATOR_FIELDSET_APPEARANCE="Appearance" +COM_MOKOSUITESTORELOCATOR_FIELDSET_CATEGORIES="Categories" +COM_MOKOSUITESTORELOCATOR_FIELDSET_APPEARANCE="Appearance" diff --git a/source/packages/com_mokosuitestorelocator/admin/language/en-GB/com_mokosuitestorelocator.sys.ini b/source/packages/com_mokosuitestorelocator/admin/language/en-GB/com_mokosuitestorelocator.sys.ini index 2ef78d9..1dbd4e6 100644 --- a/source/packages/com_mokosuitestorelocator/admin/language/en-GB/com_mokosuitestorelocator.sys.ini +++ b/source/packages/com_mokosuitestorelocator/admin/language/en-GB/com_mokosuitestorelocator.sys.ini @@ -3,6 +3,6 @@ ; License: GNU General Public License version 3 or later; see LICENSE COM_MOKOSUITESTORELOCATOR="MokoSuite Store Locator" -COM_MOKOJOOMSTORELOCATOR="Store Locator" -COM_MOKOJOOMSTORELOCATOR_DESC="A store locator component for managing and displaying location listings." -COM_MOKOJOOMSTORELOCATOR_LOCATIONS="Locations" +COM_MOKOSUITESTORELOCATOR_SHORT="Store Locator" +COM_MOKOSUITESTORELOCATOR_DESC="A store locator component for managing and displaying location listings." +COM_MOKOSUITESTORELOCATOR_LOCATIONS="Locations" diff --git a/source/packages/com_mokosuitestorelocator/admin/src/Controller/ImportController.php b/source/packages/com_mokosuitestorelocator/admin/src/Controller/ImportController.php index 63f7b76..436da7a 100644 --- a/source/packages/com_mokosuitestorelocator/admin/src/Controller/ImportController.php +++ b/source/packages/com_mokosuitestorelocator/admin/src/Controller/ImportController.php @@ -59,7 +59,7 @@ class ImportController extends BaseController if (!$csvFile || $csvFile['error'] !== UPLOAD_ERR_OK || !is_uploaded_file($csvFile['tmp_name'])) { - $this->setMessage(Text::_('COM_MOKOJOOMSTORELOCATOR_IMPORT_NO_FILE'), 'error'); + $this->setMessage(Text::_('COM_MOKOSUITESTORELOCATOR_IMPORT_NO_FILE'), 'error'); $this->setRedirect(Route::_('index.php?option=com_mokosuitestorelocator&view=import', false)); return; @@ -68,7 +68,7 @@ class ImportController extends BaseController // Enforce 2 MB file size limit if ($csvFile['size'] > 2 * 1024 * 1024) { - $this->setMessage(Text::_('COM_MOKOJOOMSTORELOCATOR_IMPORT_FILE_TOO_LARGE'), 'error'); + $this->setMessage(Text::_('COM_MOKOSUITESTORELOCATOR_IMPORT_FILE_TOO_LARGE'), 'error'); $this->setRedirect(Route::_('index.php?option=com_mokosuitestorelocator&view=import', false)); return; @@ -79,7 +79,7 @@ class ImportController extends BaseController if ($ext !== 'csv' && $ext !== 'txt') { - $this->setMessage(Text::_('COM_MOKOJOOMSTORELOCATOR_IMPORT_INVALID_FILE'), 'error'); + $this->setMessage(Text::_('COM_MOKOSUITESTORELOCATOR_IMPORT_INVALID_FILE'), 'error'); $this->setRedirect(Route::_('index.php?option=com_mokosuitestorelocator&view=import', false)); return; @@ -92,7 +92,7 @@ class ImportController extends BaseController if (!$mime || !\in_array($mime, $allowedMimes, true)) { - $this->setMessage(Text::_('COM_MOKOJOOMSTORELOCATOR_IMPORT_INVALID_FILE'), 'error'); + $this->setMessage(Text::_('COM_MOKOSUITESTORELOCATOR_IMPORT_INVALID_FILE'), 'error'); $this->setRedirect(Route::_('index.php?option=com_mokosuitestorelocator&view=import', false)); return; @@ -102,12 +102,12 @@ class ImportController extends BaseController if ($result['imported'] > 0) { - $this->setMessage(Text::sprintf('COM_MOKOJOOMSTORELOCATOR_IMPORT_SUCCESS', $result['imported'])); + $this->setMessage(Text::sprintf('COM_MOKOSUITESTORELOCATOR_IMPORT_SUCCESS', $result['imported'])); } if ($result['skipped'] > 0) { - $this->setMessage(Text::sprintf('COM_MOKOJOOMSTORELOCATOR_IMPORT_SKIPPED', $result['skipped']), 'warning'); + $this->setMessage(Text::sprintf('COM_MOKOSUITESTORELOCATOR_IMPORT_SKIPPED', $result['skipped']), 'warning'); } $this->setRedirect(Route::_('index.php?option=com_mokosuitestorelocator&view=locations', false)); @@ -138,12 +138,12 @@ class ImportController extends BaseController if ($result['imported'] > 0) { - $this->setMessage(Text::sprintf('COM_MOKOJOOMSTORELOCATOR_IMPORT_FP_SUCCESS', $result['imported'])); + $this->setMessage(Text::sprintf('COM_MOKOSUITESTORELOCATOR_IMPORT_FP_SUCCESS', $result['imported'])); } if ($result['skipped'] > 0) { - $this->setMessage(Text::sprintf('COM_MOKOJOOMSTORELOCATOR_IMPORT_SKIPPED', $result['skipped']), 'warning'); + $this->setMessage(Text::sprintf('COM_MOKOSUITESTORELOCATOR_IMPORT_SKIPPED', $result['skipped']), 'warning'); } foreach ($result['errors'] as $error) diff --git a/source/packages/com_mokosuitestorelocator/admin/src/Controller/LocationController.php b/source/packages/com_mokosuitestorelocator/admin/src/Controller/LocationController.php index 68a56dc..59b8b2f 100644 --- a/source/packages/com_mokosuitestorelocator/admin/src/Controller/LocationController.php +++ b/source/packages/com_mokosuitestorelocator/admin/src/Controller/LocationController.php @@ -25,7 +25,7 @@ class LocationController extends FormController * @var string * @since 1.0.0 */ - protected $text_prefix = 'COM_MOKOJOOMSTORELOCATOR_LOCATION'; + protected $text_prefix = 'COM_MOKOSUITESTORELOCATOR_LOCATION'; /** * The view list to redirect to after save. diff --git a/source/packages/com_mokosuitestorelocator/admin/src/Controller/LocationsController.php b/source/packages/com_mokosuitestorelocator/admin/src/Controller/LocationsController.php index 840a1d4..fb550ac 100644 --- a/source/packages/com_mokosuitestorelocator/admin/src/Controller/LocationsController.php +++ b/source/packages/com_mokosuitestorelocator/admin/src/Controller/LocationsController.php @@ -25,7 +25,7 @@ class LocationsController extends AdminController * @var string * @since 1.0.0 */ - protected $text_prefix = 'COM_MOKOJOOMSTORELOCATOR_LOCATIONS'; + protected $text_prefix = 'COM_MOKOSUITESTORELOCATOR_LOCATIONS'; /** * Proxy for getModel. diff --git a/source/packages/com_mokosuitestorelocator/admin/src/Model/LocationModel.php b/source/packages/com_mokosuitestorelocator/admin/src/Model/LocationModel.php index 1c314eb..a6cc49a 100644 --- a/source/packages/com_mokosuitestorelocator/admin/src/Model/LocationModel.php +++ b/source/packages/com_mokosuitestorelocator/admin/src/Model/LocationModel.php @@ -99,7 +99,7 @@ class LocationModel extends AdminModel $data['latitude'] = $coords['lat']; $data['longitude'] = $coords['lng']; Factory::getApplication()->enqueueMessage( - Text::_('COM_MOKOJOOMSTORELOCATOR_GEOCODING_SUCCESS'), + Text::_('COM_MOKOSUITESTORELOCATOR_GEOCODING_SUCCESS'), 'success' ); } @@ -252,7 +252,7 @@ class LocationModel extends AdminModel catch (\Exception $e) { Factory::getApplication()->enqueueMessage( - Text::sprintf('COM_MOKOJOOMSTORELOCATOR_GEOCODING_FAILED', $e->getMessage()), + Text::sprintf('COM_MOKOSUITESTORELOCATOR_GEOCODING_FAILED', $e->getMessage()), 'warning' ); } diff --git a/source/packages/com_mokosuitestorelocator/admin/src/View/Categories/HtmlView.php b/source/packages/com_mokosuitestorelocator/admin/src/View/Categories/HtmlView.php index 4b3b069..9545a1d 100644 --- a/source/packages/com_mokosuitestorelocator/admin/src/View/Categories/HtmlView.php +++ b/source/packages/com_mokosuitestorelocator/admin/src/View/Categories/HtmlView.php @@ -42,7 +42,7 @@ class HtmlView extends BaseHtmlView protected function addToolbar(): void { - ToolbarHelper::title(Text::_('COM_MOKOJOOMSTORELOCATOR_CATEGORIES'), 'folder'); + ToolbarHelper::title(Text::_('COM_MOKOSUITESTORELOCATOR_CATEGORIES'), 'folder'); ToolbarHelper::addNew('category.add'); ToolbarHelper::publish('categories.publish', 'JTOOLBAR_PUBLISH', true); ToolbarHelper::unpublish('categories.unpublish', 'JTOOLBAR_UNPUBLISH', true); diff --git a/source/packages/com_mokosuitestorelocator/admin/src/View/Category/HtmlView.php b/source/packages/com_mokosuitestorelocator/admin/src/View/Category/HtmlView.php index 2067665..56d181d 100644 --- a/source/packages/com_mokosuitestorelocator/admin/src/View/Category/HtmlView.php +++ b/source/packages/com_mokosuitestorelocator/admin/src/View/Category/HtmlView.php @@ -42,7 +42,7 @@ class HtmlView extends BaseHtmlView $isNew = ($this->item->id == 0); ToolbarHelper::title( - Text::_('COM_MOKOJOOMSTORELOCATOR_CATEGORY_' . ($isNew ? 'NEW' : 'EDIT')), + Text::_('COM_MOKOSUITESTORELOCATOR_CATEGORY_' . ($isNew ? 'NEW' : 'EDIT')), 'folder' ); diff --git a/source/packages/com_mokosuitestorelocator/admin/src/View/Import/HtmlView.php b/source/packages/com_mokosuitestorelocator/admin/src/View/Import/HtmlView.php index 0e074e9..d6ed19e 100644 --- a/source/packages/com_mokosuitestorelocator/admin/src/View/Import/HtmlView.php +++ b/source/packages/com_mokosuitestorelocator/admin/src/View/Import/HtmlView.php @@ -32,7 +32,7 @@ class HtmlView extends BaseHtmlView */ public function display($tpl = null): void { - ToolbarHelper::title(Text::_('COM_MOKOJOOMSTORELOCATOR_IMPORT'), 'upload'); + ToolbarHelper::title(Text::_('COM_MOKOSUITESTORELOCATOR_IMPORT'), 'upload'); parent::display($tpl); } diff --git a/source/packages/com_mokosuitestorelocator/admin/src/View/Location/HtmlView.php b/source/packages/com_mokosuitestorelocator/admin/src/View/Location/HtmlView.php index f8208a0..9014322 100644 --- a/source/packages/com_mokosuitestorelocator/admin/src/View/Location/HtmlView.php +++ b/source/packages/com_mokosuitestorelocator/admin/src/View/Location/HtmlView.php @@ -71,7 +71,7 @@ class HtmlView extends BaseHtmlView $isNew = ($this->item->id == 0); ToolbarHelper::title( - Text::_('COM_MOKOJOOMSTORELOCATOR_LOCATION_' . ($isNew ? 'NEW' : 'EDIT')), + Text::_('COM_MOKOSUITESTORELOCATOR_LOCATION_' . ($isNew ? 'NEW' : 'EDIT')), 'location' ); diff --git a/source/packages/com_mokosuitestorelocator/admin/src/View/Locations/HtmlView.php b/source/packages/com_mokosuitestorelocator/admin/src/View/Locations/HtmlView.php index cd59316..bef9432 100644 --- a/source/packages/com_mokosuitestorelocator/admin/src/View/Locations/HtmlView.php +++ b/source/packages/com_mokosuitestorelocator/admin/src/View/Locations/HtmlView.php @@ -82,11 +82,11 @@ class HtmlView extends BaseHtmlView */ protected function addToolbar(): void { - ToolbarHelper::title(Text::_('COM_MOKOJOOMSTORELOCATOR_LOCATIONS'), 'location'); + ToolbarHelper::title(Text::_('COM_MOKOSUITESTORELOCATOR_LOCATIONS'), 'location'); ToolbarHelper::addNew('location.add'); ToolbarHelper::publish('locations.publish', 'JTOOLBAR_PUBLISH', true); ToolbarHelper::unpublish('locations.unpublish', 'JTOOLBAR_UNPUBLISH', true); ToolbarHelper::deleteList('', 'locations.delete', 'JTOOLBAR_DELETE'); - ToolbarHelper::custom('import.display', 'upload', '', 'COM_MOKOJOOMSTORELOCATOR_IMPORT', false); + ToolbarHelper::custom('import.display', 'upload', '', 'COM_MOKOSUITESTORELOCATOR_IMPORT', false); } } diff --git a/source/packages/com_mokosuitestorelocator/admin/tmpl/categories/default.php b/source/packages/com_mokosuitestorelocator/admin/tmpl/categories/default.php index 45523f7..ae9d3cf 100644 --- a/source/packages/com_mokosuitestorelocator/admin/tmpl/categories/default.php +++ b/source/packages/com_mokosuitestorelocator/admin/tmpl/categories/default.php @@ -31,7 +31,7 @@ use Joomla\CMS\Router\Route; @@ -42,10 +42,10 @@ use Joomla\CMS\Router\Route;
- +
- + - + diff --git a/source/packages/com_mokosuitestorelocator/admin/tmpl/category/edit.php b/source/packages/com_mokosuitestorelocator/admin/tmpl/category/edit.php index 4eb2923..23a7e68 100644 --- a/source/packages/com_mokosuitestorelocator/admin/tmpl/category/edit.php +++ b/source/packages/com_mokosuitestorelocator/admin/tmpl/category/edit.php @@ -36,7 +36,7 @@ HTMLHelper::_('behavior.keepalive'); - +
form->renderField('color'); ?> diff --git a/source/packages/com_mokosuitestorelocator/admin/tmpl/import/default.php b/source/packages/com_mokosuitestorelocator/admin/tmpl/import/default.php index b311f27..a40e6bb 100644 --- a/source/packages/com_mokosuitestorelocator/admin/tmpl/import/default.php +++ b/source/packages/com_mokosuitestorelocator/admin/tmpl/import/default.php @@ -22,12 +22,12 @@ use Joomla\CMS\Session\Session;
-

-

+

+

@@ -35,7 +35,7 @@ use Joomla\CMS\Session\Session;
@@ -42,10 +42,10 @@ use Joomla\CMS\Router\Route;
- +
- + - + diff --git a/source/packages/com_mokosuitestorelocator/mokosuitestorelocator.xml b/source/packages/com_mokosuitestorelocator/mokosuitestorelocator.xml index 80c7745..f0328e1 100644 --- a/source/packages/com_mokosuitestorelocator/mokosuitestorelocator.xml +++ b/source/packages/com_mokosuitestorelocator/mokosuitestorelocator.xml @@ -15,14 +15,14 @@ --> com_mokosuitestorelocator - 01.02.00 + 01.02.38 2026-06-23 Moko Consulting hello@mokoconsulting.tech https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GNU General Public License version 3 or later; see LICENSE - COM_MOKOJOOMSTORELOCATOR_DESC + COM_MOKOSUITESTORELOCATOR_DESC Moko\Component\MokoSuiteStoreLocator @@ -67,11 +67,11 @@ tmpl - COM_MOKOJOOMSTORELOCATOR + COM_MOKOSUITESTORELOCATOR_SHORT - COM_MOKOJOOMSTORELOCATOR_LOCATIONS - COM_MOKOJOOMSTORELOCATOR_CATEGORIES - COM_MOKOJOOMSTORELOCATOR_IMPORT + COM_MOKOSUITESTORELOCATOR_LOCATIONS + COM_MOKOSUITESTORELOCATOR_CATEGORIES + COM_MOKOSUITESTORELOCATOR_IMPORT diff --git a/source/packages/com_mokosuitestorelocator/site/language/en-GB/com_mokosuitestorelocator.ini b/source/packages/com_mokosuitestorelocator/site/language/en-GB/com_mokosuitestorelocator.ini index e6861bb..618f15e 100644 --- a/source/packages/com_mokosuitestorelocator/site/language/en-GB/com_mokosuitestorelocator.ini +++ b/source/packages/com_mokosuitestorelocator/site/language/en-GB/com_mokosuitestorelocator.ini @@ -3,19 +3,19 @@ ; License: GNU General Public License version 3 or later; see LICENSE COM_MOKOSUITESTORELOCATOR="MokoSuite Store Locator" -COM_MOKOJOOMSTORELOCATOR="Store Locator" -COM_MOKOJOOMSTORELOCATOR_LOCATIONS="Locations" -COM_MOKOJOOMSTORELOCATOR_NO_LOCATIONS="No locations found." +COM_MOKOSUITESTORELOCATOR_SHORT="Store Locator" +COM_MOKOSUITESTORELOCATOR_LOCATIONS="Locations" +COM_MOKOSUITESTORELOCATOR_NO_LOCATIONS="No locations found." -COM_MOKOJOOMSTORELOCATOR_FIELDSET_ADDRESS="Address" -COM_MOKOJOOMSTORELOCATOR_FIELDSET_CONTACT="Contact Information" -COM_MOKOJOOMSTORELOCATOR_FIELD_PHONE="Phone" -COM_MOKOJOOMSTORELOCATOR_FIELD_WEBSITE="Website" -COM_MOKOJOOMSTORELOCATOR_FIELD_HOURS="Business Hours" +COM_MOKOSUITESTORELOCATOR_FIELDSET_ADDRESS="Address" +COM_MOKOSUITESTORELOCATOR_FIELDSET_CONTACT="Contact Information" +COM_MOKOSUITESTORELOCATOR_FIELD_PHONE="Phone" +COM_MOKOSUITESTORELOCATOR_FIELD_WEBSITE="Website" +COM_MOKOSUITESTORELOCATOR_FIELD_HOURS="Business Hours" -COM_MOKOJOOMSTORELOCATOR_LOCATIONS_VIEW_DEFAULT_TITLE="All Locations" -COM_MOKOJOOMSTORELOCATOR_LOCATIONS_VIEW_DEFAULT_DESC="Displays a list of all store locations." -COM_MOKOJOOMSTORELOCATOR_LOCATION_VIEW_DEFAULT_TITLE="Location Detail" -COM_MOKOJOOMSTORELOCATOR_LOCATION_VIEW_DEFAULT_DESC="Displays a single store location with full details." -COM_MOKOJOOMSTORELOCATOR_FIELD_LOCATION="Select Location" -COM_MOKOJOOMSTORELOCATOR_GET_DIRECTIONS="Get Directions" +COM_MOKOSUITESTORELOCATOR_LOCATIONS_VIEW_DEFAULT_TITLE="All Locations" +COM_MOKOSUITESTORELOCATOR_LOCATIONS_VIEW_DEFAULT_DESC="Displays a list of all store locations." +COM_MOKOSUITESTORELOCATOR_LOCATION_VIEW_DEFAULT_TITLE="Location Detail" +COM_MOKOSUITESTORELOCATOR_LOCATION_VIEW_DEFAULT_DESC="Displays a single store location with full details." +COM_MOKOSUITESTORELOCATOR_FIELD_LOCATION="Select Location" +COM_MOKOSUITESTORELOCATOR_GET_DIRECTIONS="Get Directions" diff --git a/source/packages/com_mokosuitestorelocator/site/tmpl/location/default.php b/source/packages/com_mokosuitestorelocator/site/tmpl/location/default.php index ef2df01..c561603 100644 --- a/source/packages/com_mokosuitestorelocator/site/tmpl/location/default.php +++ b/source/packages/com_mokosuitestorelocator/site/tmpl/location/default.php @@ -42,7 +42,7 @@ if ($item->latitude && $item->longitude)
-

+

address) : ?> escape($item->address); ?>
@@ -68,17 +68,17 @@ if ($item->latitude && $item->longitude) data-lat="latitude; ?>" data-lng="longitude; ?>" data-title="escape($item->title); ?>"> - +
-

+

phone) : ?>
- : + : phone); ?> escape($item->phone); ?> @@ -97,7 +97,7 @@ if ($item->latitude && $item->longitude) website && preg_match('#^https?://#i', $item->website)) : ?>
- : + : @@ -107,7 +107,7 @@ if ($item->latitude && $item->longitude) hours) : ?>
-

+

escape($item->hours)); ?>
diff --git a/source/packages/com_mokosuitestorelocator/site/tmpl/locations/default.php b/source/packages/com_mokosuitestorelocator/site/tmpl/locations/default.php index e5e21dd..9f5522f 100644 --- a/source/packages/com_mokosuitestorelocator/site/tmpl/locations/default.php +++ b/source/packages/com_mokosuitestorelocator/site/tmpl/locations/default.php @@ -14,10 +14,10 @@ use Joomla\CMS\Router\Route; /** @var \Moko\Component\MokoSuiteStoreLocator\Site\View\Locations\HtmlView $this */ ?>
-

+

items)) : ?> -

+

items as $item) : ?> diff --git a/source/packages/mod_mokosuitestorelocator_map/mod_mokosuitestorelocator_map.xml b/source/packages/mod_mokosuitestorelocator_map/mod_mokosuitestorelocator_map.xml index a1367e4..2e4dbff 100644 --- a/source/packages/mod_mokosuitestorelocator_map/mod_mokosuitestorelocator_map.xml +++ b/source/packages/mod_mokosuitestorelocator_map/mod_mokosuitestorelocator_map.xml @@ -14,7 +14,7 @@ --> mod_mokosuitestorelocator_map - 01.02.00 + 01.02.38 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/mod_mokosuitestorelocator_map/tmpl/default.php b/source/packages/mod_mokosuitestorelocator_map/tmpl/default.php index 92b2865..b2d0fb9 100644 --- a/source/packages/mod_mokosuitestorelocator_map/tmpl/default.php +++ b/source/packages/mod_mokosuitestorelocator_map/tmpl/default.php @@ -55,7 +55,7 @@ if ($provider === 'leaflet')
addInlineScript(<< mod_mokosuitestorelocator_search - 01.02.00 + 01.02.38 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitestorelocator/mokosuitestorelocator.xml b/source/packages/plg_webservices_mokosuitestorelocator/mokosuitestorelocator.xml index 84195b9..f2530fb 100644 --- a/source/packages/plg_webservices_mokosuitestorelocator/mokosuitestorelocator.xml +++ b/source/packages/plg_webservices_mokosuitestorelocator/mokosuitestorelocator.xml @@ -5,7 +5,7 @@ ========================================================================= --> plg_webservices_mokosuitestorelocator - 01.02.00 + 01.02.38 2026-06-24 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitestorelocator.xml b/source/pkg_mokosuitestorelocator.xml index 3e35574..48eea44 100644 --- a/source/pkg_mokosuitestorelocator.xml +++ b/source/pkg_mokosuitestorelocator.xml @@ -18,7 +18,7 @@ MokoSuite Store Locator mokosuitestorelocator - 01.02.00 + 01.02.38 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/script.php b/source/script.php index 1f4058e..75616ca 100644 --- a/source/script.php +++ b/source/script.php @@ -128,12 +128,121 @@ class Pkg_MokosuitestorelocatorInstallerScript implements InstallerScriptInterfa */ public function postflight(string $type, InstallerAdapter $parent): bool { + // Housekeeping runs unconditionally. $this->restoreDownloadKey(); + + // Be honest about success. Joomla's package installer only LOGS a failed + // child sub-install but still runs this postflight, so don't show the + // license / next-steps message if a bundled extension is actually missing. + // Fails open (see missingChildExtensions) so a query/IO glitch never fakes + // a failure. + $missing = $this->missingChildExtensions($parent); + + if (!empty($missing)) + { + \Joomla\CMS\Factory::getApplication()->enqueueMessage( + '

MokoSuiteStoreLocator did not install correctly.

' + . '

The following bundled extensions are missing: ' + . htmlspecialchars(implode(', ', $missing), ENT_QUOTES) . '

' + . '

Please uninstall MokoSuiteStoreLocator and reinstall the full package.

', + 'error' + ); + + return true; + } + + // Success / next-steps path — only reached when every child installed. $this->warnMissingLicenseKey(); return true; } + /** + * Verify every child extension declared in the package manifest actually landed + * in #__extensions. Returns readable labels of any that are missing. + * + * Matching per Joomla's #__extensions uniqueness: + * - type = the "type" attribute + * - element = the "id" attribute, EXCEPT for plugins the manifest often + * declares the full `plg__` id, so strip a leading + * `plg__` to get the real element; components/modules use id as-is + * - plugins ALSO match folder = the "group" attribute + * + * FAILS OPEN: any error (unreadable manifest, query failure) returns [] so a + * transient glitch never turns a good install into a false failure. + * + * @param InstallerAdapter $parent The parent installer object. + * + * @return array Readable labels of missing child extensions. + * + * @since 1.0.0 + */ + private function missingChildExtensions(InstallerAdapter $parent): array + { + try + { + $manifest = $parent->getParent()->getManifest(); + + if (!$manifest || !isset($manifest->files) || !isset($manifest->files->file)) + { + return []; + } + + $db = \Joomla\CMS\Factory::getDbo(); + $missing = []; + + foreach ($manifest->files->file as $file) + { + $attrs = $file->attributes(); + $id = isset($attrs['id']) ? (string) $attrs['id'] : ''; + $exType = isset($attrs['type']) ? (string) $attrs['type'] : ''; + + if ($id === '' || $exType === '') + { + continue; + } + + $group = isset($attrs['group']) ? (string) $attrs['group'] : ''; + $element = $id; + + // Plugin element in #__extensions is the id minus any leading plg__. + if ($exType === 'plugin' && $group !== '') + { + $prefix = 'plg_' . $group . '_'; + + if (strpos($element, $prefix) === 0) + { + $element = substr($element, \strlen($prefix)); + } + } + + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($element)) + ->where($db->quoteName('type') . ' = ' . $db->quote($exType)); + + if ($exType === 'plugin' && $group !== '') + { + $query->where($db->quoteName('folder') . ' = ' . $db->quote($group)); + } + + if ((int) $db->setQuery($query)->loadResult() === 0) + { + $label = trim((string) $file); + $missing[] = $label !== '' ? preg_replace('/\.zip$/i', '', $label) : $id; + } + } + + return $missing; + } + catch (\Throwable $e) + { + // Fail open — never fake a failure on a glitch. + return []; + } + } + private ?string $savedDownloadKey = null; private function saveDownloadKey(): void