Initial commit

This commit is contained in:
Matt Pugh 2017-01-03 09:08:45 +00:00
commit 7afe14b8ac
18 changed files with 848 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# PHPUnit
phpunit.xml
# Composer
composer.phar
composer.lock
vendor/*

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2017 WHMCS, Limited
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

47
README.md Normal file
View file

@ -0,0 +1,47 @@
# WHMCS Sample Addon Module #
## 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 sample file demonstrates how an addon module for WHMCS should be
structured and exercises all supported functionality.
For more information, please refer to the online documentation at
https://developers.whmcs.com/addon-modules/
## 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 ##
For the latest WHMCS minimum system requirements, please refer to
http://docs.whmcs.com/System_Requirements
We recommend your module follows the same minimum requirements wherever
possible.
## Tests ##
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)

27
composer.json Normal file
View file

@ -0,0 +1,27 @@
{
"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"
}
}

View file

@ -0,0 +1,290 @@
<?php
/**
* WHMCS SDK Sample Addon Module
*
* 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 sample file demonstrates how an addon module for WHMCS should be
* structured and exercises all supported functionality.
*
* Addon Modules are stored in the /modules/addons/ directory. The module
* name you choose must be unique, and should be all lowercase, containing
* only letters & numbers, always starting with a letter.
*
* Within the module itself, all functions must be prefixed with the module
* filename, followed by an underscore, and then the function name. For this
* example file, the filename is "addonmodule" and therefore all functions
* begin "addonmodule_".
*
* For more information, please refer to the online documentation.
*
* @see https://developers.whmcs.com/addon-modules/
*
* @copyright Copyright (c) WHMCS Limited 2017
* @license http://www.whmcs.com/license/ WHMCS Eula
*/
if (!defined("WHMCS")) {
die("This file cannot be accessed directly");
}
// Require any libraries needed for the module to function.
// require_once __DIR__ . '/path/to/library/loader.php';
//
// Also, perform any initialization required by the service's library.
use WHMCS\Module\Addon\AddonModule\Admin\AdminDispatcher;
use WHMCS\Module\Addon\AddonModule\Client\ClientDispatcher;
/**
* Define addon module configuration parameters.
*
* Includes a number of required system fields including name, description,
* author, language and version.
*
* Also allows you to define any configuration parameters that should be
* presented to the user when activating and configuring the module. These
* values are then made available in all module function calls.
*
* Examples of each and their possible configuration parameters are provided in
* the fields parameter below.
*
* @return array
*/
function addonmodule_config()
{
return array(
'name' => 'Addon Module Sample', // Display name for your module
'description' => 'This module provides an example WHMCS Addon Module which can be used as a basis for building a custom addon module.', // Description displayed within the admin interface
'author' => 'Your name goes here', // Module author name
'language' => 'english', // Default language
'version' => '1.0', // Version number
'fields' => array(
// a text field type allows for single line text input
'Text Field Name' => array(
'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' => array(
'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' => array(
'FriendlyName' => 'Checkbox Field Name',
'Type' => 'yesno',
'Description' => 'Tick to enable',
),
// the dropdown field type renders a select menu of options
'Dropdown Field Name' => array(
'FriendlyName' => 'Dropdown Field Name',
'Type' => 'dropdown',
'Options' => array(
'option1' => 'Display Value 1',
'option2' => 'Second Option',
'option3' => 'Another Option',
),
'Description' => 'Choose one',
),
// the radio field type displays a series of radio button options
'Radio Field Name' => array(
'FriendlyName' => 'Radio Field Name',
'Type' => 'radio',
'Options' => 'First Option,Second Option,Third Option',
'Description' => 'Choose your option!',
),
// the textarea field type allows for multi-line text input
'Textarea Field Name' => array(
'FriendlyName' => 'Textarea Field Name',
'Type' => 'textarea',
'Rows' => '3',
'Cols' => '60',
'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.
*
* @return array Optional success/failure message
*/
function addonmodule_activate()
{
// Create custom tables and schema required by your module
$query = "CREATE TABLE `mod_addonexample` (`id` INT( 1 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,`demo` TEXT NOT NULL )";
full_query($query);
return array(
'status' => 'success', // Supported values here include: success, error or info
'description' => 'This is a demo module only. In a real module you might report an error/failure or instruct a user how to get started with it here.',
);
}
/**
* 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.
*
* @return array Optional success/failure message
*/
function addonmodule_deactivate()
{
// Undo any database and schema modifications made by your module here
$query = "DROP TABLE `mod_addonexample`";
full_query($query);
return array(
'status' => 'success', // Supported values here include: success, error or info
'description' => 'This is a demo module only. In a real module you might report an error/failure here.',
);
}
/**
* 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.
*
* @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) {
$query = "ALTER `mod_addonexample` ADD `demo2` TEXT NOT NULL ";
full_query($query);
}
/// Perform SQL schema changes required by the upgrade to version 1.2 of your module
if ($currentlyInstalledVersion < 1.2) {
$query = "ALTER `mod_addonexample` ADD `demo3` TEXT NOT NULL ";
full_query($query);
}
}
/**
* 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 = '<p>Sidebar output HTML goes here</p>';
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);
}

View file

@ -0,0 +1,38 @@
<?php
/**
* WHMCS SDK Sample Addon Module Hooks File
*
* Hooks allow you to tie into events that occur within the WHMCS application.
*
* This allows you to execute your own code in addition to, or sometimes even
* instead of that which WHMCS executes by default.
*
* @see https://developers.whmcs.com/hooks/
*
* @copyright Copyright (c) WHMCS Limited 2017
* @license http://www.whmcs.com/license/ WHMCS Eula
*/
// Require any libraries needed for the module to function.
// require_once __DIR__ . '/path/to/library/loader.php';
//
// Also, perform any initialization required by the service's library.
/**
* Register a hook with WHMCS.
*
* This sample demonstrates triggering a service call when a change is made to
* a client profile within WHMCS.
*
* For more information, please refer to https://developers.whmcs.com/hooks/
*
* add_hook(string $hookPointName, int $priority, string|array|Closure $function)
*/
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.
}
});

View file

@ -0,0 +1,6 @@
<?php
/**
* Sample addon module language file.
* Language: English
*/
$_ADDONLANG['variable_name'] = "Translated language string.";

View file

@ -0,0 +1,34 @@
<?php
namespace WHMCS\Module\Addon\AddonModule\Admin;
/**
* Sample Admin Area Dispatch Handler
*/
class AdminDispatcher {
/**
* Dispatch request.
*
* @param string $action
* @param array $parameters
*
* @return string
*/
public function dispatch($action, $parameters)
{
if (!$action) {
// Default to index if no action specified
$action = 'index';
}
$controller = new Controller();
// Verify requested action is valid and callable
if (is_callable(array($controller, $action))) {
return $controller->$action($parameters);
}
return '<p>Invalid action requested. Please go back and try again.</p>';
}
}

View file

@ -0,0 +1,104 @@
<?php
namespace WHMCS\Module\Addon\AddonModule\Admin;
/**
* Sample Admin Area Controller
*/
class Controller {
/**
* Index action.
*
* @param array $vars Module configuration parameters
*
* @return string
*/
public function index($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 <<<EOF
<h2>Index</h2>
<p>This is the <em>index</em> action output of the sample addon module.</p>
<p>The currently installed version is: <strong>{$version}</strong></p>
<p>Values of the configuration field are as follows:</p>
<blockquote>
Text Field: {$configTextField}<br>
Password Field: {$configPasswordField}<br>
Checkbox Field: {$configCheckboxField}<br>
Dropdown Field: {$configDropdownField}<br>
Radio Field: {$configRadioField}<br>
Textarea Field: {$configTextareaField}
</blockquote>
<p>
<a href="{$modulelink}&action=show" class="btn btn-success">
<i class="fa fa-check"></i>
Visit valid action link
</a>
<a href="{$modulelink}&action=invalid" class="btn btn-default">
<i class="fa fa-times"></i>
Visit invalid action link
</a>
</p>
EOF;
}
/**
* Show action.
*
* @param array $vars Module configuration parameters
*
* @return string
*/
public function show($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 <<<EOF
<h2>Show</h2>
<p>This is the <em>show</em> action output of the sample addon module.</p>
<p>The currently installed version is: <strong>{$version}</strong></p>
<p>
<a href="{$modulelink}" class="btn btn-info">
<i class="fa fa-arrow-left"></i>
Back to home
</a>
</p>
EOF;
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace WHMCS\Module\Addon\AddonModule\Client;
/**
* Sample Client Area Dispatch Handler
*/
class ClientDispatcher {
/**
* Dispatch request.
*
* @param string $action
* @param array $parameters
*
* @return array
*/
public function dispatch($action, $parameters)
{
if (!$action) {
// Default to index if no action specified
$action = 'index';
}
$controller = new Controller();
// Verify requested action is valid and callable
if (is_callable(array($controller, $action))) {
return $controller->$action($parameters);
}
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace WHMCS\Module\Addon\AddonModule\Client;
/**
* Sample Client Area Controller
*/
class Controller {
/**
* Index action.
*
* @param array $vars Module configuration parameters
*
* @return array
*/
public function index($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',
),
'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',
),
);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,43 @@
<h2>Public Client Area Sample Page</h2>
<p>This is an example of a public client area page that does not require a login to view.</p>
<p>All the template variables you define along with some <a href="https://developers.whmcs.com/themes/variables/" target="_blank">additional standard template variables</a> are available within this template.<br>You can use the Smarty <em>{ldelim}debug{rdelim}</em> function call to see a full list.</p>
<hr>
<div class="row">
<div class="col-sm-3">
Module Link
</div>
<div class="col-sm-7">
{$modulelink}
</div>
</div>
<div class="row">
<div class="col-sm-3">
Config Text Field Value
</div>
<div class="col-sm-7">
{$configTextField}
</div>
</div>
<div class="row">
<div class="col-sm-3">
Custom Variable
</div>
<div class="col-sm-7">
{$customVariable}
</div>
</div>
<hr>
<p>
<a href="{$modulelink}&action=secret" class="btn btn-default">
<i class="fa fa-lock"></i>
Go to page that requires authentication
</a>
</p>

View file

@ -0,0 +1,45 @@
<h2>Secret Client Area Sample Page</h2>
<p>This is an example of a client area page that requires authentication to access.</p>
<p>You will have either been prompted to login or already have an active login state to access this page.</p>
<p>All the template variables you define along with some <a href="https://developers.whmcs.com/themes/variables/" target="_blank">additional standard template variables</a> are available within this template.<br>You can use the Smarty <em>{ldelim}debug{rdelim}</em> function call to see a full list.</p>
<hr>
<div class="row">
<div class="col-sm-3">
Module Link
</div>
<div class="col-sm-7">
{$modulelink}
</div>
</div>
<div class="row">
<div class="col-sm-3">
Config Text Field Value
</div>
<div class="col-sm-7">
{$configTextField}
</div>
</div>
<div class="row">
<div class="col-sm-3">
Custom Variable
</div>
<div class="col-sm-7">
{$customVariable}
</div>
</div>
<hr>
<p>
<a href="{$modulelink}" class="btn btn-default">
<i class="fa fa-arrow-left"></i>
Return to addon module default page
</a>
</p>

13
phpunit.xml.dist Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.6/phpunit.xsd"
bootstrap="tests/_bootstrap.php"
cacheTokens="false"
>
<testsuites>
<testsuite name="WHMCS Sample Addon Module Tests">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

44
tests/WHMCSModuleTest.php Normal file
View file

@ -0,0 +1,44 @@
<?php
/**
* WHMCS Sample Addon Module Test
*
* Sample PHPUnit test that asserts the fundamental requirements of a WHMCS
* module, ensuring that the required config function is defined and contains
* the required array keys.
*
* This is by no means intended to be a complete test, and does not exercise any
* of the actual functionality of the functions within the module. We strongly
* recommend you implement further tests as appropriate for your module use
* case.
*
* @copyright Copyright (c) WHMCS Limited 2017
* @license http://www.whmcs.com/license/ WHMCS Eula
*/
class WHMCSModuleTest extends PHPUnit_Framework_TestCase
{
/** @var string $moduleName */
protected $moduleName = 'addonmodule';
/**
* Asserts the required config options function is defined.
*/
public function testRequiredConfigOptionsFunctionExists()
{
$this->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);
}
}

10
tests/_bootstrap.php Normal file
View file

@ -0,0 +1,10 @@
<?php
// This is the bootstrap for PHPUnit testing.
if (!defined('WHMCS')) {
define('WHMCS', true);
}
// Include the WHMCS module.
require_once __DIR__ . '/../modules/addons/addonmodule/addonmodule.php';