diff --git a/app/Database/Migrations/20250531000000_PluginConfigTableCreate.php b/app/Database/Migrations/20250531000000_PluginConfigTableCreate.php index 578d7da4e..175f4cc5f 100644 --- a/app/Database/Migrations/20250531000000_PluginConfigTableCreate.php +++ b/app/Database/Migrations/20250531000000_PluginConfigTableCreate.php @@ -6,19 +6,13 @@ use CodeIgniter\Database\Migration; class PluginConfigTableCreate extends Migration { - /** - * Perform a migration step. - */ public function up(): void { - error_log('Migrating plugin_config table started'); + log_message('info', 'Migrating plugin_config table started'); execute_script(APPPATH . 'Database/Migrations/sqlscripts/3.4.1_PluginConfigTableCreate.sql'); } - /** - * Revert a migration step. - */ public function down(): void { } diff --git a/app/Database/Migrations/sqlscripts/3.4.1_PluginConfigTableCreate.sql b/app/Database/Migrations/sqlscripts/3.4.1_PluginConfigTableCreate.sql index f7a32c336..ad51f429f 100644 --- a/app/Database/Migrations/sqlscripts/3.4.1_PluginConfigTableCreate.sql +++ b/app/Database/Migrations/sqlscripts/3.4.1_PluginConfigTableCreate.sql @@ -1,8 +1,7 @@ -CREATE TABLE `ospos_plugin_config` ( +CREATE TABLE IF NOT EXISTS `ospos_plugin_config` ( `key` varchar(100) NOT NULL, `value` text NOT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), - `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -ALTER TABLE `ospos_plugin_config` ADD PRIMARY KEY (`key`); \ No newline at end of file + `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/app/Helpers/plugin_helper.php b/app/Helpers/plugin_helper.php index e241d2d79..12fba373c 100644 --- a/app/Helpers/plugin_helper.php +++ b/app/Helpers/plugin_helper.php @@ -2,14 +2,12 @@ use CodeIgniter\Events\Events; -if (!function_exists('plugin_content')) -{ +if (!function_exists('plugin_content')) { function plugin_content(string $section, array $data = []): string { $results = Events::trigger("view:{$section}", $data); - if (is_array($results)) - { + if (is_array($results)) { return implode('', array_filter($results, fn($r) => is_string($r))); } @@ -17,8 +15,7 @@ if (!function_exists('plugin_content')) } } -if (!function_exists('plugin_content_exists')) -{ +if (!function_exists('plugin_content_exists')) { function plugin_content_exists(string $section): bool { $observers = Events::listRegistered("view:{$section}"); diff --git a/app/Libraries/Plugins/BasePlugin.php b/app/Libraries/Plugins/BasePlugin.php index 87baf6978..188f6f425 100644 --- a/app/Libraries/Plugins/BasePlugin.php +++ b/app/Libraries/Plugins/BasePlugin.php @@ -4,12 +4,6 @@ namespace App\Libraries\Plugins; use App\Models\Plugin_config; -/** - * Base Plugin Class - * - * Abstract base class providing common plugin functionality. - * Plugins can extend this class to reduce boilerplate code. - */ abstract class BasePlugin implements PluginInterface { protected Plugin_config $configModel; @@ -19,45 +13,28 @@ abstract class BasePlugin implements PluginInterface $this->configModel = new Plugin_config(); } - /** - * Default install implementation. - * Override in subclass for custom installation logic. - */ public function install(): bool { return true; } - /** - * Default uninstall implementation. - * Override in subclass for custom uninstallation logic. - */ public function uninstall(): bool { return true; } - /** - * Check if the plugin is enabled. - */ public function isEnabled(): bool { $enabled = $this->configModel->get("{$this->getPluginId()}_enabled"); return $enabled === '1' || $enabled === 'true'; } - /** - * Get a plugin setting. - */ protected function getSetting(string $key, mixed $default = null): mixed { $value = $this->configModel->get("{$this->getPluginId()}_{$key}"); return $value ?? $default; } - /** - * Set a plugin setting. - */ protected function setSetting(string $key, mixed $value): bool { $stringValue = is_array($value) || is_object($value) @@ -67,17 +44,11 @@ abstract class BasePlugin implements PluginInterface return $this->configModel->set("{$this->getPluginId()}_{$key}", $stringValue); } - /** - * Get all plugin settings. - */ public function getSettings(): array { return $this->configModel->getPluginSettings($this->getPluginId()); } - /** - * Save plugin settings. - */ public function saveSettings(array $settings): bool { $prefixedSettings = []; @@ -88,9 +59,6 @@ abstract class BasePlugin implements PluginInterface return $this->configModel->batchSave($prefixedSettings); } - /** - * Log a plugin-specific message. - */ protected function log(string $level, string $message): void { log_message($level, "[Plugin:{$this->getPluginName()}] {$message}"); diff --git a/app/Libraries/Plugins/PluginInterface.php b/app/Libraries/Plugins/PluginInterface.php index df6a4485d..8a032c661 100644 --- a/app/Libraries/Plugins/PluginInterface.php +++ b/app/Libraries/Plugins/PluginInterface.php @@ -2,33 +2,14 @@ namespace App\Libraries\Plugins; -/** - * Plugin Interface - * - * All plugins must implement this interface to be discovered and loaded by the PluginManager. - * This ensures a standard contract for plugin lifecycle and event registration. - */ interface PluginInterface { - /** - * Get the unique identifier for this plugin. - * Should be lowercase with underscores, e.g., 'mailchimp_integration' - */ public function getPluginId(): string; - /** - * Get the human-readable name of the plugin. - */ public function getPluginName(): string; - /** - * Get the plugin description. - */ public function getPluginDescription(): string; - /** - * Get the plugin version. - */ public function getVersion(): string; /** @@ -46,29 +27,19 @@ interface PluginInterface /** * Install the plugin. * - * Called when the plugin is first enabled. Use this to: - * - Create database tables - * - Set default configuration values - * - Run any setup required - * - * @return bool True if installation succeeded + * Called when the plugin is first enabled. Use this to create database tables, + * set default configuration values, and run any setup required. */ public function install(): bool; /** * Uninstall the plugin. * - * Called when the plugin is being removed. Use this to: - * - Remove database tables - * - Clean up configuration - * - * @return bool True if uninstallation succeeded + * Called when the plugin is being removed. Use this to remove database tables, + * clean up configuration, etc. */ public function uninstall(): bool; - /** - * Check if the plugin is enabled. - */ public function isEnabled(): bool; /** @@ -79,18 +50,7 @@ interface PluginInterface */ public function getConfigView(): ?string; - /** - * Get plugin-specific settings for the configuration view. - * - * @return array Settings array to pass to the view - */ public function getSettings(): array; - /** - * Save plugin settings from configuration form. - * - * @param array $settings The settings to save - * @return bool True if settings were saved successfully - */ public function saveSettings(array $settings): bool; } \ No newline at end of file diff --git a/app/Plugins/README.md b/app/Plugins/README.md index 8180e2849..204765eb3 100644 --- a/app/Plugins/README.md +++ b/app/Plugins/README.md @@ -492,6 +492,187 @@ echo form_submit('submit', 'Save'); echo form_close(); ``` +## Internationalization (Language Files) + +Plugins can include their own language files, making them completely self-contained. This allows plugins to provide translations without modifying core language files. + +### Plugin Language Directory Structure + +``` +app/Plugins/ +└── CasposPlugin/ + ├── CasposPlugin.php + ├── Language/ + │ ├── en/ + │ │ └── CasposPlugin.php # English translations + │ ├── es-ES/ + │ │ └── CasposPlugin.php # Spanish translations + │ └── de-DE/ + │ └── CasposPlugin.php # German translations + └── Views/ + └── config.php +``` + +### Language File Format + +Each language file returns an array of translation strings: + +```php + 'CASPOS Integration', + 'caspos_plugin_desc' => 'Azerbaijan government cash register integration', + 'caspos_print_receipt' => 'Print Fiscal Receipt', + 'caspos_fiscal_number' => 'Fiscal Number', + 'caspos_api_url' => 'API URL', + 'caspos_api_key' => 'API Key', + 'caspos_configuration' => 'CASPOS Configuration', + 'caspos_sync_success' => 'Sale synchronized successfully', + 'caspos_sync_failed' => 'Failed to synchronize sale', +]; +``` + +### Loading Language Strings in Plugins + +The `BasePlugin` class can provide a helper method to load plugin-specific language strings: + +```php +lang('caspos_plugin_name'); + } + + public function getPluginDescription(): string + { + return $this->lang('caspos_plugin_desc'); + } + + public function onItemSale(array $saleData): void + { + if (!$this->isEnabled()) { + return; + } + + $result = $this->sendToApi($saleData); + + if ($result['success']) { + $this->log('info', $this->lang('caspos_sync_success')); + } else { + $this->log('error', $this->lang('caspos_sync_failed') . ': ' . $result['error']); + } + } + + protected function lang(string $key, array $data = []): string + { + $language = \Config\Services::language(); + $language->addLanguagePath(APPPATH . 'Plugins/CasposPlugin/Language/'); + return $language->getLine($key, $data); + } +} +``` + +### Using Language Strings in Plugin Views + +```php +addLanguagePath(APPPATH . 'Plugins/CasposPlugin/Language/'); +?> + +