# Custom UAPI Modules ## Overview You can add custom modules to cPanel & WHM and call them with UAPI. div Warning: Make certain that you thoroughly test custom modules before you attempt to add them to production servers. cPanel & WHM does not support custom modules for the WHM API 1 system. ## Steps to create a custom module To create a custom module and add it to cPanel & WHM's UAPI, perform the following steps: 1. Create a Perl module. Save your custom module as the `/usr/local/cpanel/Cpanel/API/Module.pm` file, where `Module` is the custom module's name. div Warning: UAPI modules reside in the `Cpanel::API` namespace. cPanel & WHM treats all of the modules in the `/usr/local/cpanel/Cpanel/API/` directory as UAPI modules. We recommend that you only save module files (`.pm`) to this location. The presence of other files may cause problems, even if the files relate to your custom modules. 1. Add functions to your module. * A single module generally contains many functions. * You can cause functions to only work with certain feature lists. 1. Thoroughly test your module on a non-production server. ### Additional resources For more information about how to write Perl modules, we recommend the following resources: * [The perlmonks.org Simple Module Tutorial.](http://www.perlmonks.org/?node_id=102347) * [Perl.org's perlmodstyle](http://perldoc.perl.org/perlmodstyle.html) documentation. * [Perl.org's perlnewmod](http://perldoc.perl.org/perlnewmod.html#Step-by-step%3A-Making-the-module) documentation. ## Create your module To create a custom module, you must first create the `Module.pm` file. div Warning: Make certain that you thoroughly test custom modules before you install them to production servers. ### The Module.pm file Save your custom module as the `/usr/local/cpanel/Cpanel/API/Module.pm` file, where `Module` represents the module's name. div Note: * We recommend that you use a CamelCase module name. * UAPI modules **cannot** use the same name as an existing UAPI module. * UAPI module names should **not** begin with an underscore ( `_` ). We recommend that you begin with the following file template: ```perl package Cpanel::API::Module; use strict; our $VERSION = '1.0'; # Your comments about this custom module. # Cpanel Dependencies use Cpanel (); use Cpanel::API (); use Cpanel::Locale (); use Cpanel::Logger (); # Other dependencies go here. # Defaults go here. # Constants go here. # Globals my $logger; my $locale; # Caches go here. # Functions go here. 1; ``` #### Package the module ```perl package Cpanel::API::Module; ``` This declaration instructs Perl to treat all of the file's functions as a part of the `Cpanel::API::Module` namespace. For more information, read [perldoc.perl.org's package](http://perldoc.perl.org/functions/package.html) documentation. #### Set the strict pragma ```perl use strict; ``` This declaration instructs Perl to return errors if the file contains potentially unsafe code. For more information, read [perldoc.perl.org's strict](https://perldoc.perl.org/strict) documentation. #### Declare a module version ```perl our $VERSION = '1.0'; ``` This declaration creates the variable `$VERSION` and sets it to `1.0`. This allows you to differentiate between this and future versions of your module. #### cPanel dependencies ```perl # Cpanel Dependencies use Cpanel (); use Cpanel::API (); use Cpanel::Locale (); use Cpanel::Logger (); ``` While UAPI calls do not require that you include these dependencies, we recommend that you include them. For more information, read [perldoc.perl.org's use](http://perldoc.perl.org/functions/use.html) documentation. div Note: If you include the `use Cpanel::API` statement, your functions can call functions from any other custom or cPanel-provided UAPI module. Remember that those UAPI functions may have additional feature list requirements. #### Additional dependencies, defaults, and constants ```perl # Other dependencies go here. # Defaults go here. # Constants go here. # Globals my $logger; my $locale; # Caches go here. ``` Use the lines below the `Cpanel::` dependencies to declare dependencies on additional Perl modules, declare default values for variables, set constant values, and set caches. UAPI modules that use the `Cpanel::Logger` and `Cpanel::Locale` objects should also declare the `$logger` and `$locale` global variables. #### Functions ```perl # Functions go here. ``` Module files **must** contain at least one function. For more information, read our [Custom Function Basics](#customfunctionbasics) documentation. #### End the module file End your module file with the following line: ```perl 1; ``` div Note: Perl requires that module files return a `true` value in order to function. This line fulfills that requirement. ## Custom Function Basics Custom modules contain one or more individual functions. div Warning: Make certain that you thoroughly test custom modules before you install them to production servers. ### Functions div Note: Write all of the functions for a custom module in the same `Module.pm` file. The examples below use the following variables: * `Module` — The custom module's name. * `function` — The name of a function in the custom module. For each function that you wish to include in your module, we recommend the following format and elements: ```perl #------------------------------------------------------------------------------------------------- # Name: # function - Choose a function name that describes the function's action. # Desc: # A description of the function function's action. # Arguments: # $arg1 - string - A description of the $arg1 parameter. # Returns: # $result1 - string - A description of the $result1 parameter. #------------------------------------------------------------------------------------------------- sub function { my ( $args, $result ) = @_; my ( $arg1, $arg2 ) = $args->get( 'arg1', 'arg2' ); # (Optional) Set a feature that the cPanel user must have in order to access the function. my $feature = 'featurename'; if ( !main::hasfeature($feature) ) { $result->error( '_ERROR_FEATURE', $feature ); return; } # Make the function unusable if the cPanel account is in demo mode. if ( $Cpanel::CPDATA{'DEMO'} ) { $result->error( '_ERROR_DEMO_MODE', $feature ); return; } # Validate the required parameters. # Sanitize the arguments. # Perform the function's action(s). # Build the results. if ($success) { $result->data($successful_data); return 1; } else { return 0; } } ``` #### Function information ```perl #------------------------------------------------------------------------------------------------- # Name: # function - Choose a function name that describes the function's action. # Desc: # A description of the function function's action. # Arguments: # $arg1 - string - A description of the $arg1 parameter. # Returns: # $result1 - string - A description of the $result1 parameter. #------------------------------------------------------------------------------------------------- ``` We **strongly** encourage you to include a summary of each function in the form of comments. This practice is beneficial both to your future self, and to other developers who may use your code. ### The function subroutine ```perl sub function { ``` Write each function in a UAPI module as a subroutine. div Note: * We recommend that you use lowercase function names. * If your function name includes multiple words, separate each word with an underscore ( `_` ) for readability. * UAPI considers functions with names that begin with an underscore ( `_` ) to be private functions. * You **cannot** call these functions through UAPI. * These functions will **not** appear in cPanel's [*API Shell*](https://docs.cpanel.net/cpanel/advanced/api-shell-for-cpanel/) interface (*cPanel >> Home >> Advanced >> API Shell*). For more information about subroutines in Perl, read [perldoc.perl.org's perlsub](http://perldoc.perl.org/perlsub.html) documentation. #### Get arguments from @_ div Important: We **strongly** recommend that all custom functions begin with this line of code. Functions that do not perform this action **will** experience errors. ```perl my ( $args, $result ) = @_; ``` This code declares the $args and $result variables, and assigns values from @_. * The `Cpanel::Args` object allows UAPI calls to receive named parameters. * The `Cpanel::Result` object allows UAPI calls to return function data. #### Use the get() method to assign variables ```perl my ( $arg1, $arg2 ) = $args->get( 'arg1', 'arg2' ); ``` This code assumes that the `arg1` and `arg2` input parameters exist. It uses the `Cpanel::Args` object's `get()` method to assign these parameters' values to the `$arg1` and `$arg2` variables. #### Require a feature You can choose to require specific features in order to call the function. Replace `featurename` with the name of the feature to require. ```perl # (Optional) Set a feature that the cPanel user must have in order to access the function. my $feature = 'featurename'; if ( !main::hasfeature($feature) ) { $result->error( '_ERROR_FEATURE', $feature ); return; } ``` This [if statement](http://perldoc.perl.org/perlsyn.html#Compound-Statements) uses built-in cPanel & WHM functionality to check whether the account has access to the specified feature. If the cPanel user does not have the access to the required feature, the function will [return](http://perldoc.perl.org/functions/return.html) an error message and will not perform any other actions. For example, the following code requires that a cPanel user has the `filemanager` feature in order to call the function: ```perl my $feature = 'filemanager'; if ( !main::hasfeature($feature) ) { $result->error( '_ERROR_FEATURE', $feature ); return; } ``` #### Disallow demo mode In most cases, you should not allow your custom module to function on cPanel accounts that are in [demo mode](https://docs.cpanel.net/whm/account-functions/manage-demo-mode/). ```perl # Make the function unusable if the cPanel account is in demo mode. if ( $Cpanel::CPDATA{'DEMO'} ) { $result->error( '_ERROR_DEMO_MODE', $feature ); return; } ``` This [if statement](http://perldoc.perl.org/perlsyn.html#Compound-Statements) uses built-in cPanel & WHM functionality to check whether the cPanel account is a demo account. If a demo cPanel account attempts to call your function, the function call with [return](http://perldoc.perl.org/functions/return.html) an error message and will **not** perform any other actions. #### Perform the desired actions ```perl # Validate the required parameters. # Sanitize the arguments. # Perform the function's action(s). ``` This section of the subroutine is where your function performs its own unique actions. These actions could combine any of Perl's many functions. Often, this includes the following actions: * Validate the data that you passed into variables in line 13. * Sanitize this data (for example, use the [chomp](http://perldoc.perl.org/functions/chomp.html) function to remove trailing newlines). * [Check data in other files](http://perldoc.perl.org/functions/open.html). * [Write data to other files](http://perldoc.perl.org/functions/write.html). #### Result data and returns At the end of your function, you can use the `Cpanel::Result` object's `data()` method to [assign values to a hash](http://perlmaven.com/perl-hash) or array of hashes of named output parameters. * Output in an array of hashes allows you to use UAPI's filter, sort, and pagination options. * The only meaningful return values in Perl are `1` (for success) and `0` (for failure). * While Perl allows bare `return` values, we do not recommend that you use them in UAPI functions. In some circumstances, the system may misinterpret bare `return` values as true. ```perl # Build the results. if ($success) { $result->data($successful_data); return 1; } else { return 0; } } ``` This example code assumes that, at some point during the function's actions, the subroutine initialized and assigned values to the `$success` and `$successful_data` values. div Note: In line 35, this example feeds the `$successful_data` value into the data hash, to return output parameters. The API **also** returns the standard UAPI metadata. If your [return](http://perldoc.perl.org/functions/return.html) data contains any strings with Unicode characters that are binary encoded, and that display on a cPanel interface, you will receive a *Wide Character* warning. To resolve this, use the following code to encode your strings: ```perl use Encode qw(encode); $result->data( encode( 'utf-8', $successful_data ) ); ``` br ## The cPanel Args object UAPI modules use the `Cpanel::Args` object to receive arguments (input parameters). When you write a custom UAPI module, you **must** use this object for your functions to operate correctly. For more information about methods in Perl, read [perl.org's perlobj documentation](http://perldoc.perl.org/perlobj.html). ### Required $args declaration Before you can use `Cpanel::Args` methods, you **must** declare the `$args` variable, as in the following example: ```perl sub custom_function { my ( $args, $result ) = @_; } ``` This example declares the `$args` and `$result` variables, and assigns values from the input parameters that the function received. We strongly recommend that you begin **all** custom functions with the `my ( $args, $result ) = @_;` declaration. Functions that **do not** perform this action will experience errors. For more information about the `Cpanel::Result` object, read the `Cpanel::Result` Object section below. ### Methods The `Cpanel::Args` object includes methods that you can use to incorporate input parameter values into your function call. #### $args->exists() This method checks whether a named parameter exists. Use this method, for example, to ensure that a value exists for an input parameter before you attempt to perform the function's action. ```perl # Check whether the param1 input parameter exists. $args->exists('param1'); ``` In this example, the `exists` method checks whether a value exists for the `param1` input parameter. * If the `param1` parameter exists, the method returns true (`1`). * If the `param1` parameter does not exist, the method returns false (`0`). div Note: This method returns `true` if the parameter exists with a null or blank value. #### $args->get() This method retrieves one or more named parameters. We recommend that you use this method to assign values from optional input parameters. ```perl # Assign param1's value to $arg1 and param2's value to $arg2. # The param1 and param2 input parameters are optional. my($arg1, $arg2) = $args->get('param1', 'param2'); ``` This example assigns the `param1` parameter's value to the `$arg1` variable, and the `param2` parameter's value to the `$arg2` variable. div Note: This method does not return runtime errors if the method attempts to assign a value from a nonexistent parameter. * For this reason, we recommend that you do **not** use this method with required parameters. * To check whether a parameter exists and then return the value or an error message, use the `get_required()` method. #### $args->get_required() This method checks whether a named parameter exists. * If the parameter exists, the method returns that parameter and its value. * If it does not exist, the method returns a predetermined error message. We recommend that you use this method to assign values from required input parameters that could have a blank value. ```perl # Assign param1's value to $variable. # Returns an error if param1 does not exist. # No error if param1's value is blank. my ($variable) = $args->get_required('param1'); ``` This example uses the `get_required` method to check whether the `param1` parameter exists, and then assigns the parameter and its value to the `$variable` variable. div Note: This method does **not** return an error if the parameter exists with a null or blank value. #### $args->get_length_required() This method checks whether a named parameter exists, and ensures that it is not a blank or null value. * If the parameter exists and has a value, the method returns that parameter and its value. * If it does not exist, has a blank value, or has a null value, the method returns a predetermined error message. We recommend that you use this method to assign values from required input parameters that must not be empty. ```perl # Assign param1's value to $variable. # Returns an error if param1 does not have a value or is empty. my ($variable) = $args->get_length_required('param1'); ``` This example uses the `get_length_required` method to check whether the `param1` parameter exists and has a valid value, and then assigns the parameter and its value to the `$variable` variable. #### $args->add() This method adds a `key=value` pair to the currently available named parameters. Use this method, for example, to introduce additional parameters from another subroutine. ```perl $args->add('parameter_name','value'); ``` In this example, the `add` method adds the `parameter_name` parameter, and assigns it a value of `value`. #### $args->keys() This method retrieves all of the current named parameters. Use this method to assign all of the named input parameters to an array, without the need to explicitly name each parameter. ```perl my $value = $args->keys(); ``` In this example, the `keys` method assigns all of the available named parameters and their values to the `$value` variable. ## The cPanel Result objects UAPI modules use the `Cpanel::Result` object to return messages and data. When you write a custom UAPI module, you **must** use this object for your functions to operate correctly. For more information about methods in Perl, read [perl.org's perlobj](http://perldoc.perl.org/perlobj.html) documentation. ### Required $result declaration Before you can use `Cpanel::Result` methods, you **must** declare the `$result` variable, as in the following example: ```perl sub custom_function { my ( $args, $result ) = @_; } ``` This example declares the `$args` and `$result` variables, and assigns values from the input parameters that the function received. div Important: We strongly recommend that you begin all custom functions with the `my ( $args, $result ) = @_;` declaration. Functions that **do not** perform this action will experience errors. div Note: If your return data contains any strings with Unicode characters that are binary encoded, and that display on a cPanel interface, you will receive a *Wide Character* warning. To resolve this, use the following code to encode your strings: ```perl use Encode qw(encode); $result->data( encode( 'utf-8', $successful_data ) ); ``` br For more information about the `Cpanel::Args` object, read the `Cpanel::Args Object` documentation above. ### Methods The `Cpanel::Result` object includes methods that you can use to return messages and data through UAPI. #### $result->data() This method stores data to include in the function's return data. Use this method to return the data hash for custom functions. You can use the `data()` method to store individual values. However, we recommend that you instead use this method to store hashes of named parameters. ```perl $result->data(@array_of_data); ``` In this example, the data method stores an array of function data. This data will appear in the data hash in the function's metadata. div Important: Do **not** use this method more than once in each function. If a function uses the `data()` method a second time, it will overwrite all of the data that it previously stored. #### $result->error() This method stores an error message to include in the function's return data. Use this method to return error messages in custom functions. If you call this method multiple times in the same function, it appends the new error messages to the list that it previously stored. ```perl $result->error('This is an error message about the function.'); ``` In this example, the `error` method stores an error message. This message will appear in the `errors` hash in the function's metadata. div Warning: * This method attempts to localize any message that a function passes to it. * For this reason, **do not** attempt to interpolate data into a string that you pass to this method. * The strings that you use with this method **must** be exact string literals. * To include interpolated data in an error message, use the `raw_error()` method. #### $result->raw_error() This method stores an error message to include in the function's return data. Use this method to return error messages in custom functions. If you call this method multiple times in the same function, it appends the new error messages to the list that it previously stored. ```perl $result->raw_error('This is an error message about the [_1].', '$function'); ``` In this example, the `raw_error` method stores an error message. This message will appear in the `errors` hash in the function's metadata. div Note: This method does **not** attempt to localize strings, and therefore is safe to use with interpolated data. #### $result->message() This method stores a message to include in the function's return data. Use this method to return messages of success, or additional function information. If you call this method multiple times in the same function, it appends the new messages to the list that it previously stored. ```perl $result->message('This is a message of success about the function.'); ``` In this example, the `message` method stores a success message. This message will appear in the `messages` hash in the function's metadata. div Warning: * This method attempts to localize any message that a function passes to it. * For this reason, **do not** attempt to interpolate data into a string that you pass to this method. * The strings that you use with this method must be exact string literals. * To include interpolated data in an error message, use the `raw_message()` method. #### $result->raw_message() This method stores a message to include in the function's return data. Use this method to return messages of success, or [the function's metadata](#the-cpanel-result-objects) If you call this method multiple times in the same function, it appends the new messages to the list that it previously stored. ```perl $result->raw_message('This is a message of success about the function.'); ``` In this example, the `raw_message` method stores a success message. This message will appear in the `messages` hash in [the function's metadata](#the-cpanel-result-objects). div Note: This method does **not** attempt to localize strings, and therefore is safe to use with interpolated data. #### $result->metadata() This method stores metadata to include in the function's return data. ```perl $result->metadata('metadata_var', '1'); ``` This example adds the `metadata_var` parameter to the hash of metadata, with a value of `1`. This message will appear in the `metadata` hash in [the function's metadata](#the-cpanel-result-objects). div Note: In most instances, custom modules will not need to use this method.