Add explicit guidance in AGENTS.md and app/Plugins/README.md
stating core code must never reference a specific plugin.
- Clarify no plugin name/path may appear in composer.json,
package.json, app/Config/*, or other core files
- State plugins resolve solely via PSR-4 autoloading and
PluginManager runtime auto-discovery
- Note installing/removing a plugin must never require
editing files outside app/Plugins/<PluginName>/
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
- Add `editable` override support in `bootstrap_tables_locale.php` to fix misaligned language file lookups for plugins.
- Update documentation explaining the issue and providing a resolution.
- Adjust `Sales` and `Attribute` logic for clearer naming (`sale_type` to `saleType`) and better attribute mapping (`attribute_id` added).
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Add three missing plugin hook points for sale documents so plugins can
inject buttons into invoice, quote, and work order views alongside the
existing receipt hook. All four pass ['saleId' => $sale_id_num] and
follow the same naming pattern (view:sales_{type}_buttons).
Exempt plugins/*/webhook from CSRF filtering to support server-to-server
provider callbacks. Also convert the CSRF except list from a string to an
array — the previous 'login|migrate' string produced a single unanchored
pattern, making login/anything CSRF-exempt. Separate array entries anchor
each one individually. Plugin webhook handlers are responsible for their
own authentication.
Add WhatsApp Business Cloud API plugin (app/Plugins/WhatsAppPlugin/):
- Free-form messaging page registered as the 'whatsapp' office module
with its own permission, plus per-customer modal and thread view
- "Send via WhatsApp" button on all four sale document types via the new
hooks; renders only when the customer has a phone number
- PDF delivery using the same sales/{type}_email view core uses for email
- Inbound webhook at plugins/whatsapp/webhook authenticated by
X-Hub-Signature-256 HMAC; fails closed on missing/bad signature;
always returns 200 to suppress Meta retries
- Out-of-order status callbacks cannot downgrade sent → delivered → read
- Conversation log table via plugin migration; dropped on uninstall with
version reset so re-install recreates it cleanly
- Access token and app secret encrypted at rest in plugin_config; no
writes to app_config or initial_schema.sql
- utf8mb4_unicode_520_ci collation throughout (MySQL and MariaDB compat)
- Language file stubs for all existing locales; English strings complete
- README covering credentials, install, webhook setup, and uninstall
Document best practices for handling schema changes in plugins after release, emphasizing the need for creating new migration files instead of editing existing ones to ensure proper application of changes.
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Expand README with guidance drawn from recurring plugin review issues.
- Clarify that 'debug' is a log level, not a toggle — debug-only
log lines must be gated behind a plugin's own debug_mode setting,
and real errors belong in the standard log, not a named channel
- Warn that successfully parsed JSON does not mean an HTTP API call
succeeded — branch on HTTP status code before trusting the body
- Note that PHP session flash data only works for forms that
navigate to a new page; add a new section covering non-navigating
AJAX/modal forms (e.g. item_saved, customer_saved), showing how to
store the outcome, expose it via an endpoint, and poll/toast it
from the injected view partial
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Add new view hook `item_form_plugin_fields` to item add/edit
form, letting plugins inject custom UI controls (checkboxes,
inputs, etc.) into that form. Multiple plugins may register
callbacks for this hook.
- app/Views/items/form.php: call pluginContent() with item
context after main item fields
- app/Plugins/README.md: document new hook in hook reference
table
Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
- Relocate PluginTestCase from App\\Libraries\\Plugins to Tests\\Support
- Update all references in AGENTS.md, README.md, and test files
- Add TestModulePlugin fixture for module registration tests
- Add PluginModuleRegistrationTest covering namespace registration
- Add plugin_data_helper.js for zero-JS plugin form field collection
Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
- Add `idMatchesPluginConvention()` guard to `BasePlugin` — rejects
module/permission ids that don't match `{plugin_id}` or `{plugin_id}_*`
- Add `unregisterPluginModules()` to `PluginManager` — called automatically
on uninstall; removes all modules and sub-permissions by convention prefix
- Plugins no longer need to call `unregisterModule()` in `uninstall()`
- Update README to document enforced naming convention, auto-cleanup
behavior, and corrected language key / file location guidance
Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
* feat(plugins): add sale document view hooks and a webhook CSRF exemption
Plugins can already inject buttons into the receipt via
view:sales_receipt_buttons, but the invoice, quote and work order views have
no hook point, so a plugin that delivers sale documents can only reach one of
the four. Add the matching hooks, keeping the same name/data shape:
view:sales_invoice_buttons
view:sales_quote_buttons
view:sales_work_order_buttons
Each passes ['saleId' => $sale_id_num] and sits in the same position as the
existing receipt hook, so one plugin callback can serve all four and branch on
the document type.
Also allow a plugin to expose an inbound provider webhook. A server-to-server
delivery carries no CSRF token and no session, so the global csrf filter now
excludes the pattern plugins/*/webhook. Plugins are responsible for
authenticating such requests themselves (typically by verifying the provider's
signature header against a shared secret) — this is documented alongside the
change.
While editing that line, the except list becomes an array. CodeIgniter matches
every entry as \A<pattern>\z, so the previous 'login|migrate' string produced a
single pattern whose inner alternatives were unanchored: 'login/anything' was
CSRF-exempt. Listing the entries separately anchors each one, which closes that
and keeps the new plugin pattern tight — plugins/whatsapp/webhook is exempt
while plugins/whatsapp/send is not.
* refactor(plugins): drop explanatory comments from csrf filter exceptions
---------
Co-authored-by: Joshua Fernandes <“joshua.1234511@yahoo.in”>
- Add Plugins testsuite to phpunit.xml.dist for auto-discovery
- Document PluginTestCase base class and test directory convention in README.md and AGENTS.md
- Tests resolve via existing App\\ PSR-4 mapping; no composer.json changes needed
Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
- Fire user_logged_in event on successful login authentication
- Add return_completed event distinct from sale_completed for return mode
- Update README with new events and clarify sale_completed fires non-return only
- Import CodeIgniter\\Events\\Events in Login controller
Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
- Add `$pluginData` parameter to `Events::trigger` calls for key events (e.g., `sale_completed`, `customer_saved`)
- Update core views to include `data-plugin-form` attribute for plugin-field serialization
- Bundle `plugin_data_helper.js` to handle form field extraction for plugins
- Extend README.md with comprehensive documentation on using `$pluginData`
- Refactor language files to support `install` translations and streamline UI labels
- Add new view hooks for sales receipt and register UI extension
Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
- Intercept uninstall action to show confirmation modal before AJAX call
- Change uninstall to soft-delete: set installed=0 instead of purging all config
- Add deleteAllNonControlForPlugin to preserve control rows on uninstall
- Add isPluginInstalled helper to PluginManager for installed state checks
- Fix isInstalled guard to treat installed=0 as not installed
Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Add missing `none_selected_text` translation key to all locale files
and prepend a blank \"none selected\" option to the subscription status
dropdown in the customer tab view.
Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
- Add first CASPOSPlugin migration: adds named FK caspos_sale_id_foreign
and converts table collation to utf8mb4_0900_ai_ci
- Document plugin migration system in app/Plugins/README.md (directory
structure, naming convention, class example, version tracking table)
- Add plugin system bullet to README.md feature list
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Create log_plugin_message() to prevent plugin logs from spamming the core logs
- Create ability to log to different logs or a base log if parameter is not specified.
- Update README.md
- Change BasePlugin::log() wrapper function to log to log_plugin_message() and add logTo() to log to a specific plugin log.
- Add plugin logger service to keep the logger loaded.
Signed-off-by: objec <objecttothis@gmail.com>
- Customer CSV import will potentially have many customerIds to send to.
- Rework mailchimp onCustomerSaved() to receive an array of ids instead of a single ID
Signed-off-by: objec <objecttothis@gmail.com>
- send only bare required data to trigger callbacks.
- Plugins for now access model, library and helpers but in the future access REST APIs only for data.
Signed-off-by: objec <objecttothis@gmail.com>
- Add missing `MailchimpPlugin.` prefix to lang() calls.
- Do not subscribe customer if consent is not true.
- Escape output in tabular_helper.php
- Removed testConnection() as unneeded code
- Fix activity count logic
- Whitelist Sort Column Headers for Plugins.php
- Store encrypted API key as base64 instead of raw binary to prevent truncation
- Rollback on batchSave partial failure.
- Remove dead code.
- Disable plugin before uninstalling it.
- Fix getPluginSettings() internal key leak
- Add action column to plugin headers function
- Automatically add grant to all admins in case person_id 1 is not active
Signed-off-by: objec <objecttothis@gmail.com>
- Add function to correctly interpret subscription status from the API
- Error validation on customer deletion.
- Corrected PHPDoc to reflect reponse codes.
- Pass complete data to synchronize subscription function
- Rework request function to properly interpret response
- Add data to trigger
- Unsubscribe customer before deleting them from Mailchimp to prevent error.
Signed-off-by: objec <objecttothis@gmail.com>
- Fix the output of pluginContent in the pluginHelper
- Register view injection events
- Correct the parameter type in getMailchimpViewData
- Correct the statusOptions creation business logic
- Removed unnecessary view injection point
- Corrected which variable was passed to the customer_saved event
- Assigned $customer_data['person_id'] on customer update
- Added renderView() function to BasePlugin.php
Signed-off-by: objec <objecttothis@gmail.com>
- Add PHPdoc including @noinspection to prevent AJAX function from causing a warning.
- Add lists array to settings retrieval in MailchimpPlugin.php
- Close modal window on Submit
- Don't check API key on empty value
Signed-off-by: objec <objecttothis@gmail.com>
- Update README.md to reflect information about routes
- Add registerAllNamespaces() function to correctly load plugin namespaces
- center text in modal title
- Properly decrypt the api key
- Refactor getAllLists to getLists
- Naming simplification of strings when mailchimp_ is redundant or unnecessary
- Do not attempt to decrypt a plaintext api_key pasted into the form
- Register namespaces early on in system init
Signed-off-by: objec <objecttothis@gmail.com>
- Remove unneeded keys from Config.php
- Remove unneeded lang() function override from BasePlugin.php
- Update README.md to reflect changes to language loading
- Correct language file string
- Correct lang() function calls to remove `$this->` from the call since we aren't overriding it anymore.
- Add code to correctly register namespace so that languages load.
- Fix plugin view render bug
Signed-off-by: objec <objecttothis@gmail.com>
- Fix bug causing all plugin views to be rendered on every page.
- Simplify code
- Refactor manage.php view to use bootstrap tables
Signed-off-by: objec <objecttothis@gmail.com>
- Move the PluginManager creation to a service.
- Move plugin discovery to creation.
- Create static discovery and namespaces variables in the PluginManager.php library
- Refactor persistent namespace declarations
- Refactor redundant code to private function.
- Remove whitespace
- Remove enable setting from MailchimpPlugin. That is handled by the PluginManager.php
- Update Events.php to call the pluginManager service
- Correct typo in enabled setting for BasePlugin to accurately reflect the database naming.
Signed-off-by: objec <objecttothis@gmail.com>
- Move Plugins controller and rename to reflect the rest of the code.
- Lazy load event registrations.
- Autoload classes so plugins are discovered.
- Remove TODO
- Remove unneeded use statement
- Correct typo in namespace of MailchimpConnector Library
- Add class names to autoload class map
- Move Plugin discovery to post_controller_constructor event
Signed-off-by: objec <objecttothis@gmail.com>
- Add plugin module to list of required admin modules.
- Don't trigger autoloader in plugin discovery.
- Delete plugins_config.php which is no longer needed for managing plugins.
- Remove references to plugins_configuration in config views.
- Correct the form submission URL path.
Signed-off-by: objec <objecttothis@gmail.com>
- Removed use statements
- Refactored key name
- Refactored MailchimpLibrary.php functions
- Removed
- Refactored function name for clarity
- Removed calls to model functions
- Corrected alignment of `=>` in language files
Signed-off-by: objec <objecttothis@gmail.com>