WebMail Pro 7 documentation

Creating your own plugin

Introduction

This documentation page provides guidelines on creating custom plugins for WebMail Pro, with a couple of existing plugins taken as a basis. Typical workflow of a plugin, supplying plugin configuration values, deriving from an existing plugin are explained.

In most cases, you'll need to use one or several plugin hooks, that aspect is covered in detail as well.

Requirements

If you create a plugin for WebMail Pro, it needs to meet the following requirements:

  • Plugin names are unique, all-lowercase, no spaces, words are separated with dashes ("-"). Other characters aside from alphanumeric ones and dashes are not allowed
  • Plugin class name is constructed by "C" prepended to plugin name converted to camelcase with dashes stripped out
    (plugin name my-custom-plugin => plugin class CMyCustomPlugin)
  • Plugin needs to be placed in a directory, its name must match the plugin name
  • Plugin needs to contain at least one primary file called index.php
  • Plugins can only reside under data/plugins/plugin-name-goes-here filesystem path

When placing plugin into a ZIP archive, it's important to make sure you're putting the plugin directory into it, not just PHP file itself.

Plugin may contain additional subdirectories under main plugin directory.

PHP API of WebMail Pro is usually utilized within a plugin to provide custom functionality. If your plugin needs to modify client-side interface aside from backend code, that can be achieved by using JavaScript API.

Analyzing existing plugin

Below, we will assume that you'd like to adapt some existing plugin obtained from Plugin repository - should be easier compared to writing new plugin from scratch, yet would allow you to understand the plugin architecture.

As an example, we'll use Password change via POPPASSD plugin. The idea behind the plugin is to let users change their account password, technically that's achieved by sending password update request to specific backend service, POPPASSD in this case.

The plugin itself holds only one index.php file. As a ground, it uses core plugin for password change found in libraries/afterlogic/common/plugins/change-password.php file:

CApi::Inc('common.plugins.change-password');

class CPoppassdChangePasswordPlugin extends AApiChangePasswordPlugin
{

Note that this isn't really a typical case. The majority of plugins wouldn't use any core plugins, and plugin class would extend primary plugin class instead:

class CArbitraryPlugin extends AApiPlugin

The plugin class contains a number of methods:

1) constructor which is used to reset class properties:

protected $oPopPassD;
protected $oDefaultDomain;

public function __construct(CApiPluginManager $oPluginManager)
{
	parent::__construct('1.0', $oPluginManager);

	$this->oPopPassD = null;
	$this->oDefaultDomain = null;
}

2) auxiliary method which determines whether this specific user account is allowed to change password:

protected function validateIfAccountCanChangePassword($oAccount)
{
	if (null === $this->oDefaultDomain)
	{
		$oApiDomainsManager = CApi::Manager('domains');
		if ($oApiDomainsManager)
		{
			$this->oDefaultDomain = $oApiDomainsManager->GetDefaultDomain();
		}
	}

	return (($oAccount instanceof CAccount) && $this->oDefaultDomain &&
		$this->oDefaultDomain->IncomingMailServer === $oAccount->IncomingMailServer);
}

Typically, you'd want password change to be enabled for local accounts only, i.e. those hosted by the same server WebMail Pro is installed on, and not for external accounts like Gmail. In this case, we assume it's a local account if its IMAP hostname matches IMAP hostname specified for default domain.

In some other plugins, the check is explicitly performed against a set of local IMAP hostnames, for example:

return \in_array(\strtolower(\trim($oAccount->IncomingMailServer)), array(
   'localhost', '127.0.0.1', '::1', '::1/128', '0:0:0:0:0:0:0:1'
  ));

3) primary method which is responsible for password change:

public function ChangePasswordProcess($oAccount)
{
	if (0 < strlen($oAccount->PreviousMailPassword) &&
		$oAccount->PreviousMailPassword !== $oAccount->IncomingMailPassword)
	{
	...

As you can see, password change is only performed if user enters current password which matches one currently stored for this account.

Subsequently in the code, plugin needs to communicate with POPPASSD backend, and for that purpose, a number of configuration parameters are loaded:

$this->oPopPassD = new CApiPoppassdProtocol(
	CApi::GetConf('plugins.poppassd-change-password.config.host', '127.0.0.1'),
	CApi::GetConf('plugins.poppassd-change-password.config.port', 106)
);

The default values will be assigned, unless they were supplied in data/settings/config.php file:

'plugins.poppassd-change-password.config.host' => "192.168.0.100",
'plugins.poppassd-change-password.config.port' => 107,

The rest of this method code authenticates on POPPASSD and sends password change request, handling all the errors that might occur along the way.

Creating modification of a plugin

Now, let's assume we need to implement password change functionality which works similarly, but mail server uses other ways of storing user account information. For example, it could use MySQL database, with some known connection details and password encryption format used.

The first thing to do is to create a copy of the original plugin, and provide a different name for it - for example, database-change-password. It will reside under data/plugins/database-change-password filesystem path, and primary class of the plugin should be renamed accordingly:

class CDatabaseChangePasswordPlugin extends AApiChangePasswordPlugin

And similarly to POPPASSD password change plugin, our new one assumes that configuration parameters are supplied in data/settings/config.php file, to denote password storage access details. If those were not provided, default values are used:

$aDBconfig = [
    "dbhost" => CApi::GetConf('plugins.database-change-password.config.dbhost', '127.0.0.1'),
    "dbuser" => CApi::GetConf('plugins.database-change-password.config.dbuser', 'root'),
    "dbpass" => CApi::GetConf('plugins.database-change-password.config.dbpass', ''),
    "dbname" => CApi::GetConf('plugins.database-change-password.config.dbname', 'afterlogic')
];
$mysqldbn=mysqli_connect($aDBconfig['dbhost'], $aDBconfig['dbuser'], $aDBconfig['dbpass'], $aDBconfig['dbname']);

Now you have all the information you need to perform the password change, which includes email address, current and new password, so you can check whether the old password is supplied correctly, and store new password in the database.

This approach is implemented in several plugins, for example, Password change for ISPconfig.

Using plugin hooks

Let's take a look at another plugin - Change email-to-username mapping on login.

The plugin is very basic but it provides a demonstration of using plugin hooks, a cornerstone of plugins functionality in WebMail Pro. The idea of using hooks assumes providing a custom handler for specific operation which is performed in WebMail Pro, intercepting and modifying data the way you require.

WebMail Pro provides a vast list of hooks for various operations. In this case, we're using api-integrator-login-to-account hook. In order to use it, we invoke $this->AddHook method call, with names of hook and custom function as parameters:

$this->AddHook('api-integrator-login-to-account', 'PluginIntegratorLoginToAccount');

Then we define PluginIntegratorLoginToAccount method, with list of parameters identical to hook's list of those. In this method, user account credentials are modified prior to submitting those for authentication:

public function PluginIntegratorLoginToAccount(&$sEmail, &$sPassword, &$sLogin, &$sLanguage, &$bAuthResult)
{
	if (empty($sLogin))
	{
		$sLogin = str_replace('@', '.', $sEmail);
	}
	else
	{
		$sLogin = str_replace('@', '.', $sLogin);
	}
}