diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php index 87c815f8f..14e19d979 100644 --- a/app/Config/Autoload.php +++ b/app/Config/Autoload.php @@ -205,6 +205,7 @@ class Autoload extends AutoloadConfig 'cookie', 'tabular', 'locale', - 'security' + 'security', + 'plugin' ]; } diff --git a/app/Helpers/plugin_helper.php b/app/Helpers/plugin_helper.php new file mode 100644 index 000000000..e241d2d79 --- /dev/null +++ b/app/Helpers/plugin_helper.php @@ -0,0 +1,27 @@ + is_string($r))); + } + + return is_string($results) ? $results : ''; + } +} + +if (!function_exists('plugin_content_exists')) +{ + function plugin_content_exists(string $section): bool + { + $observers = Events::listRegistered("view:{$section}"); + return !empty($observers); + } +} \ No newline at end of file diff --git a/app/Plugins/CasposPlugin/CasposPlugin.php b/app/Plugins/CasposPlugin/CasposPlugin.php new file mode 100644 index 000000000..c95b6c526 --- /dev/null +++ b/app/Plugins/CasposPlugin/CasposPlugin.php @@ -0,0 +1,127 @@ +log('debug', 'CASPOS plugin events registered'); + } + + public function install(): bool + { + $this->log('info', 'Installing CASPOS plugin'); + + $this->setSetting('api_url', ''); + $this->setSetting('api_key', ''); + $this->setSetting('merchant_id', ''); + $this->setSetting('show_receipt_button', '1'); + + return true; + } + + public function uninstall(): bool + { + $this->log('info', 'Uninstalling CASPOS plugin'); + return true; + } + + public function getConfigView(): ?string + { + return 'Plugins/CasposPlugin/Views/config'; + } + + public function getSettings(): array + { + return [ + 'api_url' => $this->getSetting('api_url', ''), + 'api_key' => $this->getSetting('api_key', ''), + 'merchant_id' => $this->getSetting('merchant_id', ''), + 'show_receipt_button' => $this->getSetting('show_receipt_button', '1'), + ]; + } + + public function saveSettings(array $settings): bool + { + if (isset($settings['api_url'])) + { + $this->setSetting('api_url', $settings['api_url']); + } + + if (isset($settings['api_key'])) + { + $this->setSetting('api_key', $settings['api_key']); + } + + if (isset($settings['merchant_id'])) + { + $this->setSetting('merchant_id', $settings['merchant_id']); + } + + if (isset($settings['show_receipt_button'])) + { + $this->setSetting('show_receipt_button', $settings['show_receipt_button'] ? '1' : '0'); + } + + return true; + } + + public function onItemSale(array $saleData): void + { + if (!$this->isEnabled()) + { + return; + } + + $this->log('info', "CASPOS: Processing sale {$saleData['sale_id_num']}"); + } + + public function injectReceiptButton(array $data): string + { + if (!$this->isEnabled() || $this->getSetting('show_receipt_button', '1') !== '1') + { + return ''; + } + + return view('Plugins/CasposPlugin/Views/receipt_button', $data); + } + + public function injectCustomerTab(array $data): string + { + if (!$this->isEnabled()) + { + return ''; + } + + return view('Plugins/CasposPlugin/Views/customer_tab', $data); + } +} \ No newline at end of file diff --git a/app/Plugins/CasposPlugin/Views/config.php b/app/Plugins/CasposPlugin/Views/config.php new file mode 100644 index 000000000..fb88a7b28 --- /dev/null +++ b/app/Plugins/CasposPlugin/Views/config.php @@ -0,0 +1,35 @@ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/app/Plugins/CasposPlugin/Views/customer_tab.php b/app/Plugins/CasposPlugin/Views/customer_tab.php new file mode 100644 index 000000000..862828b12 --- /dev/null +++ b/app/Plugins/CasposPlugin/Views/customer_tab.php @@ -0,0 +1,6 @@ +
  • + +   + + +
  • \ No newline at end of file diff --git a/app/Plugins/CasposPlugin/Views/receipt_button.php b/app/Plugins/CasposPlugin/Views/receipt_button.php new file mode 100644 index 000000000..a59bff4cc --- /dev/null +++ b/app/Plugins/CasposPlugin/Views/receipt_button.php @@ -0,0 +1,4 @@ + +   + + \ No newline at end of file diff --git a/app/Plugins/README.md b/app/Plugins/README.md index 0c4137154..1cad1a60d 100644 --- a/app/Plugins/README.md +++ b/app/Plugins/README.md @@ -100,6 +100,123 @@ OSPOS fires these events that plugins can listen to: | `items_csv_import` | `array $importData` | Fired after items CSV import | | `customers_csv_import` | `array $importData` | Fired after customers CSV import | +## View Hooks (Injecting Plugin Content into Views) + +Plugins can inject UI elements into core views using the event-based view hook system. This allows plugins to add buttons, tabs, or other content without modifying core view files. + +### How It Works + +1. **Core views define hook points** using the `plugin_content()` helper +2. **Plugins register listeners** for these view hooks in `registerEvents()` +3. **Content is conditionally rendered** only if the plugin is enabled + +### Step 1: Adding Hook Points in Core Views + +In your core view files, use the `plugin_content()` helper to define injection points: + +```php +// In app/Views/sales/receipt.php +
    + + $sale]) ?> +
    + +// In app/Views/customers/form.php + +``` + +### Step 2: Plugin Registers View Hook + +In your plugin class, register a listener that returns HTML content: + +```php +class CasposPlugin extends BasePlugin +{ + public function registerEvents(): void + { + Events::on('item_sale', [$this, 'onItemSale']); + + // View hooks - inject content into core views + Events::on('view:receipt_actions', [$this, 'injectReceiptButton']); + Events::on('view:customer_tabs', [$this, 'injectCustomerTab']); + } + + public function injectReceiptButton(array $data): string + { + if (!$this->isEnabled()) { + return ''; + } + + // Return HTML from plugin's own view file + return view('Plugins/CasposPlugin/Views/receipt_button', $data); + } + + public function injectCustomerTab(array $data): string + { + if (!$this->isEnabled()) { + return ''; + } + + return view('Plugins/CasposPlugin/Views/customer_tab', $data); + } +} +``` + +### Plugin View Files + +The plugin's view files are self-contained within the plugin directory: + +```php +// app/Plugins/CasposPlugin/Views/receipt_button.php + +   + Print Fiscal Receipt + + +// app/Plugins/CasposPlugin/Views/customer_tab.php +
  • + +   + Fiscal Records + +
  • +``` + +### Helper Functions + +The `plugin_helper.php` provides two functions: + +```php +// Render plugin content for a hook point +plugin_content(string $section, array $data = []): string + +// Check if any plugin has registered for a hook (for conditional rendering) +plugin_content_exists(string $section): bool +``` + +### Standard Hook Points + +Core views should define these standard hook points: + +| Hook Name | Location | Usage | +|-----------|----------|-------| +| `view:receipt_actions` | Receipt view action buttons | Add receipt-related buttons | +| `view:customer_tabs` | Customer form tabs | Add customer-related tabs | +| `view:item_form_buttons` | Item form action buttons | Add item-related buttons | +| `view:sales_complete` | Sale complete screen | Post-sale integration UI | +| `view:reports_menu` | Reports menu | Add custom report links | + +### Benefits + +- **Self-Contained**: Plugin UI stays in plugin directory +- **Conditional**: Only renders when plugin is enabled +- **Data Access**: Pass context (sale, customer, etc.) to plugin views +- **Multiple Plugins**: Multiple plugins can hook the same location +- **Clean Separation**: Core views remain unmodified + ## Creating a Plugin ### Simple Plugin (Single File) @@ -485,4 +602,72 @@ CasposPlugin-1.0.0.zip └── README.md # Plugin documentation ``` -Users extract the zip to `app/Plugins/` and the plugin is ready to use. \ No newline at end of file +Users extract the zip to `app/Plugins/` and the plugin is ready to use. + +## Example: Plugin with View Hooks + +Here's a complete example of a plugin that injects UI elements into core views: + +``` +app/Plugins/CasposPlugin/ +├── CasposPlugin.php # Main class with view hook registration +└── Views/ + ├── receipt_button.php # Button for receipt view + ├── customer_tab.php # Tab for customer form + └── config.php # Plugin configuration +``` + +**Main Plugin Class:** + +```php +isEnabled()) { + return ''; + } + return view('Plugins/CasposPlugin/Views/receipt_button', $data); + } + + public function injectCustomerTab(array $data): string + { + if (!$this->isEnabled()) { + return ''; + } + return view('Plugins/CasposPlugin/Views/customer_tab', $data); + } + + // ... rest of implementation +} +``` + +Core views that want to support plugin hooks: + +```php +// app/Views/sales/receipt.php +
    + + $sale]) ?> +
    +``` \ No newline at end of file