diff --git a/LICENSE b/LICENSE index b06866a..bc3b5f2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2017 WHMCS, Limited +Copyright (c) 2017 WHMCS, Limited, 2023 Kumi Systems e.U. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0310f6d..1ad239f 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,14 @@ -# WHMCS Sample Addon Module # +# Account Selection Module for WHMCS -## Summary ## +## Summary -An addon module allows you to add additional functionality to WHMCS. It -can provide both client and admin facing user interfaces, as well as -utilise hook functionality within WHMCS. +This module allows you to select the account that you want a specific invoice item to be assigned to in accounting. -This sample file demonstrates how an addon module for WHMCS should be -structured and exercises all supported functionality. +This was a specific requirement for a client, but the concept of displaying a dynamic list of values for a custom invoice item field may be useful to others. -For more information, please refer to the online documentation at -https://developers.whmcs.com/addon-modules/ +This addon is a fork of the [WHMCS Sample Addon Module](https://github.com/WHMCS/sample-addon-module) and, as such, is licensed under the MIT License. -## Recommended Module Content ## - -The recommended structure of an addon module is as follows. - -``` - addonmodule/ - |- lang/ - |- lib/ - |- templates/ - | addonmodule.php - | hooks.php - | logo.png -``` - -## Minimum Requirements ## +## Minimum Requirements For the latest WHMCS minimum system requirements, please refer to http://docs.whmcs.com/System_Requirements @@ -34,14 +16,7 @@ http://docs.whmcs.com/System_Requirements We recommend your module follows the same minimum requirements wherever possible. -## Tests ## +## License -We strongly encourage you to write unit tests for your work. Within this SDK we -provide a sample unit test based upon the widely used PHPUnit. - -## Useful Resources -* [Developer Portal](https://developers.whmcs.com/) -* [Hook Documentation](https://developers.whmcs.com/hooks/) -* [API Documentation](https://developers.whmcs.com/api/) - -[WHMCS Limited](https://www.whmcs.com) +This module is licensed under the MIT License. Please see the LICENSE file for +more information. \ No newline at end of file diff --git a/composer.json b/composer.json deleted file mode 100644 index 7248aa8..0000000 --- a/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "whmcs/sample-addon-module", - "description": "Sample Addon Module for WHMCS Billing Automation Platform", - "keywords": [ - "whmcs", - "web host automation platform", - "addon module" - ], - "homepage": "http://www.whmcs.com/", - "license": "MIT", - "authors": [ - { - "name": "WHMCS Development Team", - "email": "development@whmcs.com", - "homepage": "http://www.whmcs.com/", - "role": "Developer" - } - ], - "support": { - "email": "support@whmcs.com", - "forum": "http://forums.whmcs.com/", - "wiki": "http://docs.whmcs.com/" - }, - "require-dev": { - "phpunit/phpunit": "@stable" - } -} diff --git a/modules/addons/accounting/accounting.php b/modules/addons/accounting/accounting.php new file mode 100644 index 0000000..f8e4a01 --- /dev/null +++ b/modules/addons/accounting/accounting.php @@ -0,0 +1,152 @@ + 'Account Selector', + // Description displayed within the admin interface + 'description' => 'This module allows you to select the account to be used for a given invoice item in accounting.', + // Module author name + 'author' => 'Kumi Systems e.U.', + // Default language + 'language' => 'english', + // Version number + 'version' => '1.0', + 'fields' => [ + 'Available Accounts' => [ + 'FriendlyName' => 'Available Accounts', + 'Type' => 'textarea', + 'Rows' => '20', + 'Cols' => '100', + 'Default' => '1234 - Example Account', + 'Description' => 'List of available accounts, one per line. Format: Account Number - Account Name', + ], + ] + ]; +} + +/** + * Called upon activation of the module for the first time. + * Used to create database tables required by the module. + * + * @see https://developers.whmcs.com/advanced/db-interaction/ + * + * @return array Optional success/failure message + */ +function accounting_activate() +{ + try { + Capsule::schema() + ->create( + 'mod_accounting', + function ($table) { + /** @var \Illuminate\Database\Schema\Blueprint $table */ + $table->increments('id'); + $table->integer('invoice'); + $table->integer('item'); + $table->text('account'); + } + ); + + return [ + 'status' => 'success', + 'description' => 'Successfully created mod_accounting table. Now, please set up your accounts in the module configuration.', + ]; + } catch (\Exception $e) { + return [ + 'status' => "error", + 'description' => 'Unable to create mod_accounting: ' . $e->getMessage(), + ]; + } +} + +/** + * Deactivate. + * + * Called upon deactivation of the module. + * Used to drop database tables created by the module. + * + * @return array Optional success/failure message + */ +function accounting_deactivate() +{ + try { + Capsule::schema() + ->dropIfExists('mod_accounting'); + + return [ + 'status' => 'success', + 'description' => 'Dropped mod_accounting table.', + ]; + } catch (\Exception $e) { + return [ + // Supported values here include: success, error or info + "status" => "error", + "description" => "Unable to drop mod_accounting: {$e->getMessage()}", + ]; + } +} + +/** + * Admin Area Output. + * + * Called when the addon module is accessed via the admin area. + * Should return HTML output for display to the admin user. + * + * This function is optional. + * + * @see AddonModule\Admin\Controller::index() + * + * @return string + */ +function accounting_output($vars) +{ + // Get common module parameters + $modulelink = $vars['modulelink']; + $version = $vars['version']; + $_lang = $vars['_lang']; // an array of the currently loaded language variables + + // Get module configuration parameters + $configTextareaField = $vars['Available Accounts']; + + // Dispatch and handle request here. What follows is a demonstration of one + // possible way of handling this using a very basic dispatcher implementation. + + $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; + + $dispatcher = new AdminDispatcher(); + $response = $dispatcher->dispatch($action, $vars); + echo $response; +} diff --git a/modules/addons/accounting/hooks.php b/modules/addons/accounting/hooks.php new file mode 100644 index 0000000..9e86cb4 --- /dev/null +++ b/modules/addons/accounting/hooks.php @@ -0,0 +1,56 @@ +where('invoiceid', $invoiceId) + ->get(); + // Get the available accounts from the module configuration. + $availableAccounts = explode("\n", $params['Available Accounts']); + // Create the drop-down field for each invoice item. + foreach ($invoiceItems as $invoiceItem) { + $accountSelector = ''; + // Add the drop-down field to the invoice item. + echo ''; + } + } catch (Exception $e) { + // Consider logging or reporting the error. + } +}); + +add_hook('ClientEdit', 1, function(array $params) { + try { + // Call the service's function, using the values provided by WHMCS in + // `$params`. + } catch (Exception $e) { + // Consider logging or reporting the error. + } +}); diff --git a/modules/addons/addonmodule/lang/english.php b/modules/addons/accounting/lang/english.php similarity index 100% rename from modules/addons/addonmodule/lang/english.php rename to modules/addons/accounting/lang/english.php diff --git a/modules/addons/addonmodule/lib/Admin/AdminDispatcher.php b/modules/addons/accounting/lib/Admin/AdminDispatcher.php similarity index 100% rename from modules/addons/addonmodule/lib/Admin/AdminDispatcher.php rename to modules/addons/accounting/lib/Admin/AdminDispatcher.php diff --git a/modules/addons/addonmodule/lib/Admin/Controller.php b/modules/addons/accounting/lib/Admin/Controller.php similarity index 68% rename from modules/addons/addonmodule/lib/Admin/Controller.php rename to modules/addons/accounting/lib/Admin/Controller.php index 0c13dcc..023c867 100644 --- a/modules/addons/addonmodule/lib/Admin/Controller.php +++ b/modules/addons/accounting/lib/Admin/Controller.php @@ -22,12 +22,7 @@ class Controller { $LANG = $vars['_lang']; // an array of the currently loaded language variables // Get module configuration parameters - $configTextField = $vars['Text Field Name']; - $configPasswordField = $vars['Password Field Name']; - $configCheckboxField = $vars['Checkbox Field Name']; - $configDropdownField = $vars['Dropdown Field Name']; - $configRadioField = $vars['Radio Field Name']; - $configTextareaField = $vars['Textarea Field Name']; + $configTextareaField = $vars['Available Accounts']; return <<Values of the configuration field are as follows:

- Text Field: {$configTextField}
- Password Field: {$configPasswordField}
- Checkbox Field: {$configCheckboxField}
- Dropdown Field: {$configDropdownField}
- Radio Field: {$configRadioField}
Textarea Field: {$configTextareaField}
@@ -77,12 +67,7 @@ EOF; $LANG = $vars['_lang']; // an array of the currently loaded language variables // Get module configuration parameters - $configTextField = $vars['Text Field Name']; - $configPasswordField = $vars['Password Field Name']; - $configCheckboxField = $vars['Checkbox Field Name']; - $configDropdownField = $vars['Dropdown Field Name']; - $configRadioField = $vars['Radio Field Name']; - $configTextareaField = $vars['Textarea Field Name']; + $configTextareaField = $vars['Available Accounts']; return << 'Addon Module Sample', - // Description displayed within the admin interface - 'description' => 'This module provides an example WHMCS Addon Module' - . ' which can be used as a basis for building a custom addon module.', - // Module author name - 'author' => 'Your name goes here', - // Default language - 'language' => 'english', - // Version number - 'version' => '1.0', - 'fields' => [ - // a text field type allows for single line text input - 'Text Field Name' => [ - 'FriendlyName' => 'Text Field Name', - 'Type' => 'text', - 'Size' => '25', - 'Default' => 'Default value', - 'Description' => 'Description goes here', - ], - // a password field type allows for masked text input - 'Password Field Name' => [ - 'FriendlyName' => 'Password Field Name', - 'Type' => 'password', - 'Size' => '25', - 'Default' => '', - 'Description' => 'Enter secret value here', - ], - // the yesno field type displays a single checkbox option - 'Checkbox Field Name' => [ - 'FriendlyName' => 'Checkbox Field Name', - 'Type' => 'yesno', - 'Description' => 'Tick to enable', - ], - // the dropdown field type renders a select menu of options - 'Dropdown Field Name' => [ - 'FriendlyName' => 'Dropdown Field Name', - 'Type' => 'dropdown', - 'Options' => [ - 'option1' => 'Display Value 1', - 'option2' => 'Second Option', - 'option3' => 'Another Option', - ], - 'Default' => 'option2', - 'Description' => 'Choose one', - ], - // the radio field type displays a series of radio button options - 'Radio Field Name' => [ - 'FriendlyName' => 'Radio Field Name', - 'Type' => 'radio', - 'Options' => 'First Option,Second Option,Third Option', - 'Default' => 'Third Option', - 'Description' => 'Choose your option!', - ], - // the textarea field type allows for multi-line text input - 'Textarea Field Name' => [ - 'FriendlyName' => 'Textarea Field Name', - 'Type' => 'textarea', - 'Rows' => '3', - 'Cols' => '60', - 'Default' => 'A default value goes here...', - 'Description' => 'Freeform multi-line text input field', - ], - ] - ]; -} - -/** - * Activate. - * - * Called upon activation of the module for the first time. - * Use this function to perform any database and schema modifications - * required by your module. - * - * This function is optional. - * - * @see https://developers.whmcs.com/advanced/db-interaction/ - * - * @return array Optional success/failure message - */ -function addonmodule_activate() -{ - // Create custom tables and schema required by your module - try { - Capsule::schema() - ->create( - 'mod_addonexample', - function ($table) { - /** @var \Illuminate\Database\Schema\Blueprint $table */ - $table->increments('id'); - $table->text('demo'); - } - ); - - return [ - // Supported values here include: success, error or info - 'status' => 'success', - 'description' => 'This is a demo module only. ' - . 'In a real module you might report a success or instruct a ' - . 'user how to get started with it here.', - ]; - } catch (\Exception $e) { - return [ - // Supported values here include: success, error or info - 'status' => "error", - 'description' => 'Unable to create mod_addonexample: ' . $e->getMessage(), - ]; - } -} - -/** - * Deactivate. - * - * Called upon deactivation of the module. - * Use this function to undo any database and schema modifications - * performed by your module. - * - * This function is optional. - * - * @see https://developers.whmcs.com/advanced/db-interaction/ - * - * @return array Optional success/failure message - */ -function addonmodule_deactivate() -{ - // Undo any database and schema modifications made by your module here - try { - Capsule::schema() - ->dropIfExists('mod_addonexample'); - - return [ - // Supported values here include: success, error or info - 'status' => 'success', - 'description' => 'This is a demo module only. ' - . 'In a real module you might report a success here.', - ]; - } catch (\Exception $e) { - return [ - // Supported values here include: success, error or info - "status" => "error", - "description" => "Unable to drop mod_addonexample: {$e->getMessage()}", - ]; - } -} - -/** - * Upgrade. - * - * Called the first time the module is accessed following an update. - * Use this function to perform any required database and schema modifications. - * - * This function is optional. - * - * @see https://laravel.com/docs/5.2/migrations - * - * @return void - */ -function addonmodule_upgrade($vars) -{ - $currentlyInstalledVersion = $vars['version']; - - /// Perform SQL schema changes required by the upgrade to version 1.1 of your module - if ($currentlyInstalledVersion < 1.1) { - $schema = Capsule::schema(); - // Alter the table and add a new text column called "demo2" - $schema->table('mod_addonexample', function($table) { - $table->text('demo2'); - }); - } - - /// Perform SQL schema changes required by the upgrade to version 1.2 of your module - if ($currentlyInstalledVersion < 1.2) { - $schema = Capsule::schema(); - // Alter the table and add a new text column called "demo3" - $schema->table('mod_addonexample', function($table) { - $table->text('demo3'); - }); - } -} - -/** - * Admin Area Output. - * - * Called when the addon module is accessed via the admin area. - * Should return HTML output for display to the admin user. - * - * This function is optional. - * - * @see AddonModule\Admin\Controller::index() - * - * @return string - */ -function addonmodule_output($vars) -{ - // Get common module parameters - $modulelink = $vars['modulelink']; // eg. addonmodules.php?module=addonmodule - $version = $vars['version']; // eg. 1.0 - $_lang = $vars['_lang']; // an array of the currently loaded language variables - - // Get module configuration parameters - $configTextField = $vars['Text Field Name']; - $configPasswordField = $vars['Password Field Name']; - $configCheckboxField = $vars['Checkbox Field Name']; - $configDropdownField = $vars['Dropdown Field Name']; - $configRadioField = $vars['Radio Field Name']; - $configTextareaField = $vars['Textarea Field Name']; - - // Dispatch and handle request here. What follows is a demonstration of one - // possible way of handling this using a very basic dispatcher implementation. - - $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; - - $dispatcher = new AdminDispatcher(); - $response = $dispatcher->dispatch($action, $vars); - echo $response; -} - -/** - * Admin Area Sidebar Output. - * - * Used to render output in the admin area sidebar. - * This function is optional. - * - * @param array $vars - * - * @return string - */ -function addonmodule_sidebar($vars) -{ - // Get common module parameters - $modulelink = $vars['modulelink']; - $version = $vars['version']; - $_lang = $vars['_lang']; - - // Get module configuration parameters - $configTextField = $vars['Text Field Name']; - $configPasswordField = $vars['Password Field Name']; - $configCheckboxField = $vars['Checkbox Field Name']; - $configDropdownField = $vars['Dropdown Field Name']; - $configRadioField = $vars['Radio Field Name']; - $configTextareaField = $vars['Textarea Field Name']; - - $sidebar = '

Sidebar output HTML goes here

'; - return $sidebar; -} - -/** - * Client Area Output. - * - * Called when the addon module is accessed via the client area. - * Should return an array of output parameters. - * - * This function is optional. - * - * @see AddonModule\Client\Controller::index() - * - * @return array - */ -function addonmodule_clientarea($vars) -{ - // Get common module parameters - $modulelink = $vars['modulelink']; // eg. index.php?m=addonmodule - $version = $vars['version']; // eg. 1.0 - $_lang = $vars['_lang']; // an array of the currently loaded language variables - - // Get module configuration parameters - $configTextField = $vars['Text Field Name']; - $configPasswordField = $vars['Password Field Name']; - $configCheckboxField = $vars['Checkbox Field Name']; - $configDropdownField = $vars['Dropdown Field Name']; - $configRadioField = $vars['Radio Field Name']; - $configTextareaField = $vars['Textarea Field Name']; - - /** - * Dispatch and handle request here. What follows is a demonstration of one - * possible way of handling this using a very basic dispatcher implementation. - */ - - $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; - - $dispatcher = new ClientDispatcher(); - return $dispatcher->dispatch($action, $vars); -} diff --git a/modules/addons/addonmodule/hooks.php b/modules/addons/addonmodule/hooks.php deleted file mode 100644 index d6924e8..0000000 --- a/modules/addons/addonmodule/hooks.php +++ /dev/null @@ -1,38 +0,0 @@ -$action($parameters); - } - } -} diff --git a/modules/addons/addonmodule/lib/Client/Controller.php b/modules/addons/addonmodule/lib/Client/Controller.php deleted file mode 100644 index 0cddf88..0000000 --- a/modules/addons/addonmodule/lib/Client/Controller.php +++ /dev/null @@ -1,86 +0,0 @@ - 'Sample Addon Module', - 'breadcrumb' => array( - 'index.php?m=addonmodule' => 'Sample Addon Module', - ), - 'templatefile' => 'publicpage', - 'requirelogin' => false, // Set true to restrict access to authenticated client users - 'forcessl' => false, // Deprecated as of Version 7.0. Requests will always use SSL if available. - 'vars' => array( - 'modulelink' => $modulelink, - 'configTextField' => $configTextField, - 'customVariable' => 'your own content goes here', - ), - ); - } - - /** - * Secret action. - * - * @param array $vars Module configuration parameters - * - * @return array - */ - public function secret($vars) - { - // Get common module parameters - $modulelink = $vars['modulelink']; // eg. addonmodules.php?module=addonmodule - $version = $vars['version']; // eg. 1.0 - $LANG = $vars['_lang']; // an array of the currently loaded language variables - - // Get module configuration parameters - $configTextField = $vars['Text Field Name']; - $configPasswordField = $vars['Password Field Name']; - $configCheckboxField = $vars['Checkbox Field Name']; - $configDropdownField = $vars['Dropdown Field Name']; - $configRadioField = $vars['Radio Field Name']; - $configTextareaField = $vars['Textarea Field Name']; - - return array( - 'pagetitle' => 'Sample Addon Module', - 'breadcrumb' => array( - 'index.php?m=addonmodule' => 'Sample Addon Module', - 'index.php?m=addonmodule&action=secret' => 'Secret Page', - ), - 'templatefile' => 'secretpage', - 'requirelogin' => true, // Set true to restrict access to authenticated client users - 'forcessl' => false, // Deprecated as of Version 7.0. Requests will always use SSL if available. - 'vars' => array( - 'modulelink' => $modulelink, - 'configTextField' => $configTextField, - 'customVariable' => 'your own content goes here', - ), - ); - } -} diff --git a/modules/addons/addonmodule/templates/js/sample.js b/modules/addons/addonmodule/templates/js/sample.js deleted file mode 100644 index e69de29..0000000 diff --git a/modules/addons/addonmodule/templates/publicpage.tpl b/modules/addons/addonmodule/templates/publicpage.tpl deleted file mode 100644 index 3ff831e..0000000 --- a/modules/addons/addonmodule/templates/publicpage.tpl +++ /dev/null @@ -1,43 +0,0 @@ -

Public Client Area Sample Page

- -

This is an example of a public client area page that does not require a login to view.

- -

All the template variables you define along with some additional standard template variables are available within this template.
You can use the Smarty {ldelim}debug{rdelim} function call to see a full list.

- -
- -
-
- Module Link -
-
- {$modulelink} -
-
- -
-
- Config Text Field Value -
-
- {$configTextField} -
-
- -
-
- Custom Variable -
-
- {$customVariable} -
-
- -
- -

- - - Go to page that requires authentication - -

diff --git a/modules/addons/addonmodule/templates/secretpage.tpl b/modules/addons/addonmodule/templates/secretpage.tpl deleted file mode 100644 index b527be7..0000000 --- a/modules/addons/addonmodule/templates/secretpage.tpl +++ /dev/null @@ -1,45 +0,0 @@ -

Secret Client Area Sample Page

- -

This is an example of a client area page that requires authentication to access.

- -

You will have either been prompted to login or already have an active login state to access this page.

- -

All the template variables you define along with some additional standard template variables are available within this template.
You can use the Smarty {ldelim}debug{rdelim} function call to see a full list.

- -
- -
-
- Module Link -
-
- {$modulelink} -
-
- -
-
- Config Text Field Value -
-
- {$configTextField} -
-
- -
-
- Custom Variable -
-
- {$customVariable} -
-
- -
- -

- - - Return to addon module default page - -

diff --git a/tests/WHMCSModuleTest.php b/tests/WHMCSModuleTest.php deleted file mode 100644 index 8145485..0000000 --- a/tests/WHMCSModuleTest.php +++ /dev/null @@ -1,44 +0,0 @@ -assertTrue(function_exists($this->moduleName . '_config')); - } - - /** - * Asserts the required config option array keys are present. - */ - public function testRequiredConfigOptionsParametersAreDefined() - { - $result = call_user_func($this->moduleName . '_config'); - - $this->assertArrayHasKey('name', $result); - $this->assertArrayHasKey('description', $result); - $this->assertArrayHasKey('author', $result); - $this->assertArrayHasKey('language', $result); - $this->assertArrayHasKey('version', $result); - $this->assertArrayHasKey('fields', $result); - } -} diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php deleted file mode 100644 index fa554ce..0000000 --- a/tests/_bootstrap.php +++ /dev/null @@ -1,10 +0,0 @@ -