Plugins

Main Goal

Plugins are designed to extend/change the functionality of the project without making changes to the main code. You can easily create your own plugin. Within the plugin, you can:

  • use hooks and filters to extend and modify the project's logic
  • create and include PHP templates, JS/CSS/image files
  • add/override email templates, SEO templates, routes, cron tasks
  • enhance/override any module class (models or components) as well as core classes
  • modify module files using special instructions  

Create a Plugin

To create the framework of a plugin, follow these steps:

  • Log in to the control panel.
  • Go to Developer mode (in the top right corner of the user menu).
  • Go to "Add-Ons / Plugins" and click on the "Create Plugin" link in the top right corner.
  • Specify the name of your new plugin, for example, "My Plugin".
  • Specify the name of the plugin in Latin characters, for example, "example" (allowed characters a-z, 0-9, _).
  • Click "Create".
  • The system will then create a plugin directory /plugins/example/, as well as an index.php file in it.
  • The plugin you created will appear in the control panel in the "Add-Ons / Plugins" section.

Enable a Plugin

Each plugin must be enabled to work in the project.

  • The first available action with the plugin is its installation, during which all necessary preliminary actions provided by the plugin will be performed: copying static files, making changes to the database structure (migrations), and other actions.
  • If the installation is successful, it becomes possible to enable the plugin, after which the plugin starts working in the project.
  • If necessary, it can also be disabled and re-enabled at any stage.
  • In addition, the plugin can be uninstalled - an action that cancels its installation, but not its physical removal. To completely remove it, you need to delete the plugin directory in the /plugins/ directory.

Plugin Structure

Plugins are located in the directory /plugins/. The plugin directory must contain at least the index.php file. Let's consider an example of its content using a newly created plugin called 'example':

<?php

class Plugin_Example extends Plugin
{
    public function init()
    {
        parent::init();

        $this->setSettings(array(
            'plugin_title'   => 'Plugin Title',
            'plugin_version' => '1.0.0',
            'extension_id'   => '1234567890123456789012345678901234567890', // unique add-on identifier
        ));

        $this->configSettings(array(
            // ...
        ));
        
        $this->blockAdd('block_key', function ($context){
            // ... 
        });
    }

    protected function start()
    {
        // plugin code, hook declarations ...
    }
}

Let's describe it in more detail:

  • The name of the plugin class always starts with the prefix Plugin_, and the class itself inherits the base class Plugin.
  • The mandatory init function specifies the name of the plugin, its version, and declares the settings. Blocks can also be declared here.
  • All other plugin code should be placed in the start function, as well as in the functions of this class. Additional PHP files should also be included in the start function or just before use.

Example of including other PHP files located in the plugin directory:

require $this->path('file.php');
require $this->path('dir/file.php');

Static Files

Plugins can have their own js, css, and image files, which should be located in the /static/ directory within the plugin's directory. When the plugin is installed/enabled, these files will be copied to the directory: /public_html/plugins/plugin_name/

Example of including js/css files within the plugin's code:

$this->js('file.js');
$this->css('file.css');

They will have the following full path:

/public_html/plugins/plugin_name/file.js
/public_html/plugins/plugin_name/file.css

There is also a helper method for generating a url with the same name. For example, if you need to specify a link to a plugin image file in a PHP template.

During plugin development, it may be inconvenient to enable/disable the plugin to apply changes to static files. For this purpose, there is a debug mode, which, when enabled, automatically updates static files. The procedure for activating it is described in this article. Don't forget to disable it once development is complete, as this setting creates additional overhead.

Plugin Settings

Plugin settings are declared in the init method. You can find more information on how to do this in the Settings article.

Blocks

Plugin blocks are declared in the init method and allow you to integrate addon HTML blocks into theme or module template files. You can learn more about them in the Blocks article.

Events

For each of the described plugin events, there is a corresponding function:

  • install - installation
  • uninstall - removal
  • enable - enable
  • disable - disable
  • update - update

If additional actions need to be performed, you can extend it as follows:

protected function install()
{
    if ( ! parent::install()) return false;
    // additional actions
    return true;
}
  • The example shows an extension of the plugin installation function.
  • First, the base function is called and a return is made in case the result is negative.
  • Then, you can add your own code, for example, if you need to execute an SQL file, the installSqlFile method is available. The recommended way to do this is through database migrations.
  • In the end, the function should return true if the actions were performed successfully or false if there were errors during the process.
  • The same rules apply to the other mentioned functions.

Routing

The plugin allows you to add or modify application routes, both general and specific to a particular module. To do this, use the routeAdd function.

For example, let's add a route that allows accessing all public methods of the plugin. Add the following code to the start method of your plugin:

$this->routeAdd('plugin1/routes', array(
    'pattern' => 'plugin1/(.*)',
    'callback' => $this->routeAction('$1')
));

After that, when the following JavaScript code is called, an AJAX request will be made to the plugin's public method named test:

bff.ajax('/plugin1/test', {}, function(response){
    /* handle the response */
});

The code for this plugin method could be as follows:

public function test()
{
     $response = array();
     // arbitrary code
     $this->ajaxResponse($response);
}

Replace the string plugin1 with a unique name, such as the name of your plugin in Latin letters. For more information on how to format the response, refer to the "Application Response" article here.

Admin Panel

To determine the context of a request to the admin panel, use the bff::adminPanel() method:

protected function start()
{
    if (bff::adminPanel())
    {
        // connect css/js files for the admin panel
        $this->cssAdmin('css/admin.css');
        $this->jsAdmin('js/admin.js');
    } else {
        // connect css/js files for the frontend
        $this->css('css/frontend.css');
        $this->js('js/frontend.js');
    }
}

Modules

The plugin has some similar capabilities to a module, namely:

  • cronSettings - declaration of running plugin cron tasks
  • sendmailTemplates - declaration of plugin email templates
  • seoTemplates - declaration of plugin SEO templates

If necessary, each of these functions should be declared in the plugin in the same way as in the module.

Also, the plugin can contain a whole module or several modules, similar in structure to those located in the project directory /modules/, and register them with the following code example in the plugin's start function:

bff::i()->moduleRegister('module name', $this->path('module_dir'));

Here, 'module_dir' is the name of the module directory in the plugin directory, and 'module name' is the name of the module specified when created in Latin letters. After that, at the start of the plugin (calling the start function), this module will be registered in the system and will start working like a regular module from the /modules/ directory.

The creation of a module framework is also available through the console command, an example and description of which you can find here.

Interface Localization

In projects with multiple languages, it may be necessary to translate text that is visible to the user, such as the name of a button or a message displayed for a specific action. To do this, all phrases in the plugin's templates should be specified using the following construction:

<?= $this->lang('Phrase to be translated'); ?>

These phrases will be available for translation in the plugin settings in the "Localization" section. Translation files are stored at the following path: /plugins/plugin name/lang/{xx}.php and have the following structure:

<?php 
return array (
  'Original phrase1' => 'Translation1',
  'Original phrase2' => 'Translation2',
);

When the translation is saved by the user (in the extension settings), the translation files are saved in the /files/extensions/plugin name/lang/{xx}.php directory. The {xx} macro represents a two-letter language code, which can be found in the /bff/languages.php file.

readme.md Instructions File

In the plugin settings, there is an option to enable the "Instructions" section with a description of how to install and configure it. To do this, add the readme.md file to the root of the plugin directory. The file supports Markdown markup, which means that the text in it can be formatted using special constructions, such as bold/italic, as well as insert links and images. It also supports localization of this file. For example, to create an English version of the file, name it readme-en.md, where you can see the language key en.

This file also supports some macros:
{plugin:title} - the plugin name
{plugin:url} - URL path to the plugin's static file directory
{plugin:path} - relative path to the plugin directory in the project (inclusive)
{theme}text{/theme} - the text will be displayed if a theme is activated in the project,
inside this block, the following macros are available:
{theme:title} - the name of the current theme
{theme:url} - URL path to the theme's static file directory
{theme:path} - relative path to the theme directory in the project (inclusive)

In the case where a theme is used in the project and some plugin code is based on modifiers, there is often a situation where they do not work correctly due to the inability to find the source line, which is present in the source template file but is missing in the theme template file.
In this case, to ensure the correct operation of the plugin, you can describe to the user a way to manually embed parts of the plugin code into the templates of the theme they are using. Let's consider an example:

We have the source file: /modules/bbs/tpl/def/item.view.owner.php
In the installed theme theme1, there is a similar file but with modified content: /themes/theme1/modules/bbs/tpl/def/item.view.owner.php
The plugin modifier is "hooked" to a certain line that is missing in the version of the theme file, for example, it should display an additional block on the page, but this does not happen. We will write an instruction in the readme.md file that will be displayed only in case the modifier fails (incorrect operation). To do this, enclose the text of this part of the description with the following code:

{theme:file:/modules/bbs/tpl/def/item.view.owner.php}
Description of what needs to be done by the user with the version of this file in the theme. For example:
Add the following code to the template file **{theme:file:path}**:
`<?php echo $code; ?>`
The recommended location is right after the line: `<div class="v-author__contact_write">`
{/theme:file}

In addition to macros {theme:file:file path} and {/theme:file}, you can also see the contextual macro {theme:file:path} that substitutes the path to the file being referred to.

Migrations

The plugin can make changes to the database structure by either adding to the structure of existing tables in the project or creating new tables. For more information on working with migrations, read the article "Database / Migrations"

Errors

During the operation of the plugin, errors may occur, which the plugin developer can record in the error log file. For more information on working with errors, read the article "Errors"