-
-
Notifications
You must be signed in to change notification settings - Fork 20
Plugins
Panopticon has a plugin system since version 1.1.0. This allows you to run custom code which hooks into events, providing additional features.
Each plugin can also have its own language files, allowing it to be translated separately from the core application.
🤵🏽 Pro Tip: Most customisations you will want to do are possible using plugins, template overrides, and custom CSS files (as opposed to overriding entire classes or, worse, modifying core code).
Core plugins are placed under src/Plugin
and cannot be disabled; they are forcibly loaded, always. You MUST NOT put your plugins there. This is a reserved area for core-provided plugins. You can, however, create a same-named plugin in your user_code
folder, shading (overriding) the core plugin completely.
User plugins are placed under the folder user_core/Plugin
and belong to the PHP namespace Akeeba\Panopticon\Plugin
. Every plugin is in its own directory. There MUST be a class named Plugin in the PSR-4 namespace corresponding to the plugin's folder which extends the \Akeeba\Panopticon\Library\Plugin\PanopticonPlugin
class; this is what is being loaded by Panopticon.
For example, a plugin named Example
would be in the folder user_core/Plugin/Example
. It needs a file user_core/Plugin/Example/Plugin.php
which holds the class Akeeba\Panopticon\Plugin\Example\Plugin
extending \Akeeba\Panopticon\Library\Plugin\PanopticonPlugin.
Plugins are discovered and loaded automatically.
By default, Panopticon uses PHP's Reflection to identify public methods whose name begins with on
, registering them as event handlers. This is slow. It is best to override the method getObservableEvents
, returning an array with your event handlers yourself. For example:
public function getObservableEvents(): array
{
return [
'onExample',
];
}
public function onExample()
{
// Handles the `onExample` event
}
If your plugin needs to load its own language files remember to override the constructor, setting the loadLanguage
property to true:
public function __construct(Observable &$subject, Container $container)
{
$this->loadLanguage = true;
parent::__construct($subject, $container);
}
The base PanopticonPlugin
class implements the \Awf\Container\ContainerAwareInterface
interface. You have access to the application's container using $this->getContainer()
. By default, this returns an \Akeeba\Panopticon\Container
object.
Currently, there is no per-plugin configuration.
Remember that the plugins load very early into the boot process of the application. Not just the web application, but also the CLI application. You need to be mindful of the implications so as not to break, or unnecessarily slow down your Panopticon installation.
You MUST NOT put any initialisation code of your plugin in the constructor! Instead, you should initialise your plugin as-needed, i.e. the first time one its event handlers is called.
There's a very simple coding pattern for this. First, create an initialisation method like so:
private function initialiseAsNeeded()
{
static $isInitialised = false;
if ($isInitialised)
{
return;
}
$isInitialised = true;
// Your initialisation code here
}
Then just remember to call $this->initialiseAsNeeded()
as the very first line of your event handler methods (the public methods whose names start with on
).
Depending on which Panopticon application you are running under, a different constant is defined:
-
AKEEBA_WEB
when you are under the interactive web application. -
AKEEBA_CLI
when you are under the CLI application.
If your plugin behaves differently across applications, or only supports one or the other application, you MUST perform feature detection using these constants.
For example, if your plugin only supports the web application you should override the getObservableEvents
method like so:
public function getObservableEvents(): array
{
if (!defined('AKEEBA_WEB'))
{
return [];
}
return [
'onExample',
];
}
DO NOT ASSUME that the above list of constants is exhaustive. In the future, for example, we might add an API application with its own constant, e.g. AKEEBA_API
. Therefore, the following code is WRONG and MUST NOT be used:
// DO NOT USE! THIS IS WRONG!
public function getObservableEvents(): array
{
// THIS DOES NOT DO WHAT YOU THINK IT DOES!
if (defined('AKEEBA_WEB'))
{
return [];
}
// Stuff for the CLI. OOPS! WRONG! THIS DOES NOT MEAN WHAT YOU THINK IT MEANS!
return [
'onExample',
];
}
The above example assumes that if it's not the web application then it has to be the CLI application. This is WRONG. It only means that it's not the web application. It might be the CLI application or any other application added in a future version of Panopticon. So, at some point, this code may run in an application context you did not expect. Upsy-daisy!
When you need to check the user input, or the server environment, DO NOT use the PHP superglobals, such as $_GET
, $_POST
, or $_SERVER
. Instead, you should always go through the application input object. For example, to get the PATH environment variable:
$path = $this->getContainer()->input->server->getRaw('PATH');
Remember that the input object offers methods which apply input filtering. This should always be your first line of defense when reading data coming from external sources.
Never trust blindly any data which is not hard-coded. This applies to the user input, but also the server environment and the application configuration.
When using the database, always use the quote
method to escape the data you are inserting or querying against. Again, it does not matter where this data comes from.
When dealing with files, use the constants with the APATH_
prefix as your ground truth. You should not allow files to be read from or written to folders above APATH_ROOT
, as they're not guaranteed to belong to the Panopticon installation.
If you are writing files with sensitive information always place them in a directory which has adequate access control (e.g. a .htaccess
file preventing direct access), or use .php
files whose first line is <?php die() ?>
.
Be vigilant. When it comes to information security you're never paranoid; everyone is out to get you.
By default, Panopticon uses PHP's DirectoryIterator
against the core and user plugin directories to list their subdirectories, and use this information to create a list of potential plugin classes to load. This is slow.
You can instead create a file called plugins.php
in your plugins directory (i.e. user_code/Plugin/plugins.php
) which returns an array of the plugin classes to load. For example:
<?php
defined('AKEEBA') || die;
return [
\Akeeba\Panopticon\Plugin\Uptime\Example::class
];
This is what we use for the core plugins, allowing them to load fast.
🤵🏽 Pro Tip: You can use the
plugins.php
file to tell Panopticon to load plugins from a namespace other than\Akeeba\Panopticon\Plugin
. However, you will find that loading languages does not work. Fixing it is simple! Override the protected methodgetPluginPath
to return the path where your plugin's language files are located in.
Documentation Copyright ©2023–2024 Akeeba Ltd.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
You can also obtain a copy of the GNU Free Documentation License from the Free Software Foundation
- Overview pages
- Working with sites
- Site Overview
- Backup Management with Akeeba Backup Pro
- Security Management with Admin Tools Pro
- Scheduled Update Summary
- Scheduled Action Summary
- Backup Tasks
- Scanner Tasks
- System Configuration
- Managing Sites
- Mail templates
- Users and Groups
- Tasks
- Log files
- Update Panopticon
- Database Backups
- Fixing your session save path
- The .htaccess file
- Advanced Customisation (user code)
- Plugins
- Custom CSS
- Custom Templates
- Advanced Permissions
- .env For Configuration