WHMCS Action Hooks Collection 2020 Updated Monthly

WHMCS Action Hooks Collection 2020 Updated Monthly

Back   Posted on 27 april 2020 / Updated on 26 may 2020
Reading time 13 minutes

Perfect your WHMCS

In this post we share our collection of action hooks that you can simply copy/paste on your WHMCS. You can also adapt them to your specific needs or use as inspiration for your projects. The post will be updated on monthly basis to include new action hooks and improvements.

Disclaimer

All action hooks have been created by us over the years. We published most of them on WHMCS forum. They are provided free of charge "as is" without warranty of any kind. You're not allowed to remove copyright notice.

Action Hooks Collection

Before you can start playing with hooks, first you should lear the fundamentals. As the name suggests, an action hook allows to execute your own code when specific events occur inside WHMCS. In essence, you can perform action X when event Y triggers.

Action hooks are written in PHP but their use is not limited to PHP and the web. You can execute javascript, jQuery, HTML, CSS, command line and other code. To some extent, other programming languages like Ruby, Python and PowerShell can be used but I digress.

To use any of the provided action hook, begin by creating a file named as you wish (it can be katamazeHooks.php) located within the includes/hooks directory then copy/paste your chosen hook inside this file. Keep in mind that the file can host multiple action hooks.

A complete listing of all available hook points can be found on Hook Index while below you can find useful resources to expand your knowledge of this panel:

Stronger Password Generator for auto-provisioning

The following hook point overrides the default password generated by WHMCS for the provisioning of services on third-party control panels like Plesk, cPanel, DirectAdmin and custom-made server modules. The code triggers a moment before WHMCS runs Module Create.

<? 

/**
 * Stronger Password Generator for WHMCS Provisioning
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use WHMCS\Database\Capsule;

add_hook('PreModuleCreate', 1, function($vars)
{
    $password = substr(str_shuffle('[email protected]#$%^&*()-=+?'), 0, $length = '10');
    Capsule::table('tblhosting')->where('id', $vars['params']['serviceid'])->update(['password' => Encrypt($password)]);
});

Below there's an improved version that makes sure that at least one special character is included in the password.

<?

/**
 * Stronger Password Generator for WHMCS Provisioning v2
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use WHMCS\Database\Capsule;

add_hook('PreModuleCreate', 1, function($vars)
{
    $chars              = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $specialChars       = '[email protected]#$%^&*?'; // Plesk does not consider (, ), -, = and + as special characters
    $password           = substr(str_shuffle($chars), 0, $length = '9');
    $randomPos          = rand(0, strlen($password) - 1);
    $randomSpecialChar  = $specialChars[rand(0,strlen($specialChars)-1)];
    $password           = substr($password, 0, $randomPos) . $randomSpecialChar . substr($password, $randomPos);

    Capsule::table('tblhosting')->where('id', $vars['params']['serviceid'])->update(['password' => Encrypt($password)]);
});

If you want an extremely strong password, use the third version. You can individually define the number of digits, lowercase, uppercase and special characters to use. Moreover the resulting password will not use the same character twice.

<?

/**
 * Stronger Password Generator for WHMCS Provisioning v3
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use WHMCS\Database\Capsule;

add_hook('PreModuleCreate', 1, function($vars)
{
    $length['digit']    = '3'; // Number of digits in password
    $length['lower']    = '4'; // Number of UNIQUE lowercase characters in password
    $length['upper']    = '4'; // Number of UNIQUE uppercase characters in password
    $length['special']  = '2'; // Number of special characters in password

    // The same character cannot be used more than once (case sensitive)
    if ($length['lower'] + $length['upper'] == '26')
    {
        $length['lower'] = '13';
        $length['upper'] = '13';
    }

    $digits             = '0123456789';
    $chars              = 'abcdefghijklmnopqrstuvwxyz';
    $special            = '[email protected]#$%^&*?'; // Plesk does not consider (, ), -, = and + as special characters
    $digits             = substr(str_shuffle($digits), 0, $length['digit']);
    $lower              = substr(str_shuffle($chars), 0, $length['lower']);
    $upper              = substr(str_shuffle(strtoupper( str_replace(str_split($lower), '', $chars) )), 0, $length['lower']);
    $special            = substr(str_shuffle($special), 0, $length['special']);
    $password           = str_shuffle($digits . $lower . $upper . $special);

    Capsule::table('tblhosting')->where('id', $vars['params']['serviceid'])->update(['password' => Encrypt($password)]);
});

New Clients as Affiliates

Automatically sets newly registered clients as affiliates so they don't need to join manually.

<?

/**
 * Set New Clients as Affiliates
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

add_hook('ClientAreaRegister', 1, function($vars)
{
    $adminUsername = 'ADMIN_USERNAME'; // Optional for WHMCS 7.2 and later
    $results = localAPI('AffiliateActivate', array('userid' => $vars['userid']), $adminUsername);
});

That said, as you probably already know the affiliate system of WHMCS is very basic. If you need something more complete you can take a look at Commission Manager.

Send Email & Add Reply on Ticket status change

When the status of a ticket changes, WHMCS doesn't send any notification however we can tweak this process with the following action hook that sends an email to customer via API and also add an automated reply to the ticket itself. This way you can guide customers through the resolving process letting them track the progress of their support tickets.

<?

/**
 * Send Email & Add Reply on Ticket status change
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use WHMCS\Database\Capsule;

add_hook('TicketStatusChange', 1, function($vars)
{
    $adminUsername = 'admin'; // The reply will be added by this Admin user. Set false to open the ticket using your own customer
    $userID = Capsule::table('tbltickets')->where('id', $vars['ticketid'])->first(['userid'])->userid;

    // Email notification
    $EmailData = array(
        'id' => $userID,
        'customtype' => 'general',
        'customsubject' => 'Thank you for contacting us',
        'custommessage' => 'Your ticket status has been changed to ' .$vars['status']
    );

    localAPI('SendEmail', $EmailData);

    // Ticket reply
    $TicketData = array(
        'ticketid' => $vars['ticketid'],
        'message' => 'Your ticket status has been changed to ' .$vars['status'],
        'clientid' => $userID,
        'adminusername' => $adminUsername,
    );

    localAPI('AddTicketReply', $TicketData);
});

Client to Group based on purchased Product/Service

Automatically assign a client to a specific client group based on the product/service he has just purchased. The script triggers as soon as the order is accepted both manually and automatically.

<?

/**
 * Assign Client to Group based on purchased Product/Service
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use WHMCS\Database\Capsule;

add_hook('AcceptOrder', 1, function($vars)
{
    $productID  = 12; // Replace with the ID of your Product/Service
    $groupID    = 1; // Replace with the ID of your Client Group
    $userID     = Capsule::table('tblorders')->leftJoin('tblhosting', 'tblorders.id', '=', 'tblhosting.orderid')->where([['tblorders.id', '=', $vars['orderid']], ['tblhosting.packageid', '=', $productID]])->first(['tblorders.userid']);

    if ($userID)
    {
        Capsule::table('tblclients')->where('id', $userID->userid)->update(['groupid' => $groupID]);
    }
});

Prevent changes to Client Custom Fields

WHMCS has an in-built function to lock client profile fields you want to prevent clients being able to edit from the client area (eg. company name, address). This feature however is not available for client custom fields. Even though you can make such fields "disabled" via HTML or jQuery, it is still possible to submit changes.

The following action hook makes grants that no customer can submit changes. If necessary this protection can be enabled also for administrators.

<?

/**
 * Prevent changes to Client Custom Fields
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use WHMCS\Database\Capsule;

add_hook('CustomFieldSave', 1, function($vars)
{
    $ReadOnlyFields = array('1', '2'); // IDs of Custom Fields that cannot be edited
    $DisallowAdmin = false; // true = Even Administrators are not allowed to edit | false = Administrators can freely update Custom Fields

    /* Do not edit below */
    $IsAdmin = (basename($_SERVER['PHP_SELF']) == 'clientsprofile.php' ? true : false);

    if (in_array($vars['fieldid'], $ReadOnlyFields) AND (($IsAdmin AND $DisallowAdmin) OR (!$IsAdmin)))
    {
        return array('value' => Capsule::table('tblcustomfieldsvalues')->where(['fieldid' => $vars['fieldid'], 'relid' => $vars['relid']])->first(['value'])->value);
    }
});

We take this opportunity to let you know that Billing Extension can bring your WHMCS to the next level with things like monthly invoicing, electronic invoicing, Facebook Pixel and much more.

Quote to Invoice conversion without redirect

When a quote is converted into an invoice, WHMCS automatically redirects you to the newly issued Invoice. The following action hook prevents WHMCS from performing the redirect this way you remain remain on the original quote.

<?

/**
 * Disable redirect when converting Quote to Invoice
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use Illuminate\Database\Capsule\Manager as Capsule;

add_hook('InvoiceCreation', 1, function($vars)
{
    $quoteID = Capsule::table('tblinvoices')->where('id', $vars['invoiceid'])->where('notes', 'like', 'Re Quote #%')->first(['notes']);
    header('Location: quotes.php?action=manage&id=' . explode('#', $quoteID->notes)[1]);
    die();
});

Restrict all pages to logged in users

Sometimes you want to create a closed off WHMCS where visitors cannot browse any page unless they are logged in. WHMCS has a feature that can get you almost there. Navigate to Setup > General Settings > Others > Default to Client Area. By enabling this option, visitors are forwarded directly to login page.

The feature we have just described however still allows the access to other pages (eg. cart, registration form, contact us, support tickets). Use this hook to restrict access even further. The variable $Exceptions can be used to "white-list" specific pages.

Remove/Hide Breadcrumb

WHMCS prepends Portal Home to breadcrumbs. There's nothing wrong with that but in case you want to remove it from all pages, use this action hook.

<? 

/**
 * Remove Portal Home breadcrumb
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

add_hook('ClientAreaPage', 1, function($vars)
{
	unset($vars['breadcrumb'][0]);
	return array('breadcrumb' => $vars['breadcrumb']);
});

Alternatively, you could simply hide it with few lines of CSS. This way search engines can still "see" the full breadcrumb.

.breadcrumb li:first-child {
    display:none;
}
.breadcrumb li:nth-child(2):before {
    content:" ";
}

Knowledgebase Last Updated Date

Let's say you want to customize Knowledgebase to display last updated date for your article. Unfortunately WHMCS doesn't store this value so I came up with the idea of retreiving it from Activity Log. It's not a stylish solution but it works.

The action hook provided below adds a Smarty variable to the existing $kbarticle array. You can use it by simply calling it from template file.

<?

/**
 * Knowledgebase Last Updated Date
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use WHMCS\Database\Capsule;

add_hook('ClientAreaPageKnowledgebase', 1, function($vars)
{
	if ($vars['kbarticle']['id'])
	{
		$LastUpdated = Capsule::table('tblactivitylog')->where('description', 'like', 'Modified Knowledgebase Article ID: %' . $vars['kbarticle']['id'])->orderby('id', 'desc')->first(['date'])->date;
		$output['kbarticle'] = $vars['kbarticle'];
		$output['kbarticle']['lastupdated']['date'] = date('Y-m-d', strtotime($LastUpdated));
		$output['kbarticle']['lastupdated']['time'] = date('H:i:s', strtotime($LastUpdated));

		return $output;
	}
});

If you need something more complete and up to date, learn how to benefit from WHMCS SEO.

Also keep in mind we developed a module to transform WHMCS into a complete CMS. It has nothing to envy from systems like WordPress, Joomla and Drupal.

Login as Client Language

Every time an administrator uses the Login as Client feature, WHMCS overrides the default language of the selected customer with the one used by the administrator for backend. This is bad because you're unknowingly changing the language for your customers. This is especially true when you are not allowing users to change the language of the system.

Let's say your clientarea is set in italian and you're using backend in english. When you perform the login as client WHMCS switches customer's language from italian to english and there's no way back. The customer in question is stucked with a language he cannot change. The following hook prevents that to happen.

<?

/**
 * Login as Client Language
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */
add_hook('AdminAreaHeaderOutput', 1, function($vars)
{
if ($vars['filename'] == 'clientssummary' AND $_GET['userid'])
{
$output = <<<HTML

HTML;

return $output;
}
});

Prevent emails to be sent based on client group

The following hook prevents WHMCS from sending General Messages email templates to specific client groups based on a sort of "blacklist".

<?

/**
 * Prevent emails to be send based on Client Group
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use WHMCS\Database\Capsule;

add_hook('EmailPreSend', 1, function($vars)
{
$disallowedGroupIDs = array('1', '2'); // Array of Client Group ID to block
$emailTemplates = array('Automated Password Reset', 'Password Reset Validation', 'Password Reset Confirmation'); // Email Templates to block (General Messages)

if (in_array($vars['messagename'], $emailTemplates))
{
if (!Capsule::select(Capsule::raw('SELECT id FROM tblclients WHERE id = "' . $vars['relid'] . '" AND groupid IN (\'' . implode('\',\'', $disallowedGroupIDs) . '\') LIMIT 1')))
{
$output['abortsend'] = true;
return $output;
}
}
});

Abort auto-provisioning when there's a note in the order

Let's suppose a customer orders a VPS and adds notes to request a particular configuration that requires your manual intervention. In case you're using auto-provisioning, there's no way to stop WHMCS from creating the VPS to let you intervene manually. This hook however can stop auto-provisioning when there's a note in the order.

<?

/** * Abort auto-provisioning when there's a note in the order * * @package WHMCS * @copyright Katamaze * @link https://katamaze.com * @author Davide Mantenuto <[email protected]> */

use
WHMCS\Database\Capsule; add_hook('PreModuleCreate', 1, function($vars) { $Data = Capsule::select(Capsule::raw('SELECT t1.notes FROM tblorders AS t1 LEFT JOIN tblhosting AS t2 ON t1.id = t2.orderid WHERE t2.id = "' . $vars['params']['serviceid'] . '" AND t2.orderid != "0"')); if ($Data[0]->notes) { return array('abortcmd' => true); } });

Simulate / Run WHMCS Daily Cron Job on demand

Being WHMCS developers, we often need to run the daily cron job whenever we want. This way we can quickly test new features and debug problems.

The problem is that, as the name suggests, the daily cron job runs only once a day. In order to run it multiple times, you have to trick WHMCS into thinking that the cron still didn't run.

You can achieve that by manually editing specific values in database but that's quite boring so we came up with the idea of integrating this feature in a button that shows at the very top of administration.

You can see the button in the screenshot above (the orange one). Please ignore Reinstall and Manage Demo buttons, they're used for Live Demo of our modules. Once you add the hook, you will be able to run the daily cron job multiple times by clicking on Run Daily Cronjob button.

<?

/**
 * Simulate / Run WHMCS Daily Cron Job on demand
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

use WHMCS\Database\Capsule;

add_hook('AdminAreaHeaderOutput', 1, function($vars)
{
// Simulazione Cron
if ($_GET['simulatecron'])
{
$SystemURL = Capsule::table('tblconfiguration')->where('setting', 'SystemURL')->first(['value'])->value;
Capsule::table('tblconfiguration')->where('setting', 'lastDailyCronInvocationTime')->update(['value' => '']);
Capsule::table('tblconfiguration')->where('setting', 'DailyCronExecutionHour')->update(['value' => date('H')]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $SystemURL . 'crons/cron.php');
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($ch);
curl_close($ch);
}
$output = <<<HTML


HTML;

return $output;
});

Add button next to module's functions

Sometimes you simply want to add a button next to the Create, Suspend, Unsuspend (...) functions in product/service view. This is how you can do that. Feel free to adapt the script to integrate your PHP/javascript function when the button is clicked.

<?

/**
 * Add button next to module's functions
 *
 * @package     WHMCS
 * @copyright   Katamaze
 * @link        https://katamaze.com
 * @author      Davide Mantenuto <[email protected]>
 */

add_hook
('AdminAreaHeaderOutput', 1, function($vars) { $output .= <<<HTML <script type="text/javascript"> $(document).ready(function(){ $("#modcmdbtns").append(' '); }); </script> HTML; return $output; });