[comment]: # aside:1

[comment]: # ({b311df1e-8fe7205c})
# Create a module (tutorial)

[comment]: # ({/b311df1e-8fe7205c})

[comment]: # ({98067368-5fb5b83a})
This is a step-by-step tutorial that shows how to create a simple Zabbix frontend module.
You can download all files of this module as a ZIP archive: [MyAddress.zip](../../../../assets/en/devel/modules/examples/MyAddress.zip).

[comment]: # ({/98067368-5fb5b83a})

[comment]: # ({5fe8674d-5ce71b9d})
### What you'll build

During this tutorial, you will first build a frontend module that adds a new *My Address* menu section
and then convert it into a [more advanced](#part-iii---module-action) frontend module that makes an HTTP request to *https://api.seeip.org*
and displays the response - the IP address of your computer - on a new page in the newly created *My Address* menu section.
Here's how the finished module will look like:

![](../../../../assets/en/devel/modules/tutorials/module/my_address_finished.png){width="600"}

[comment]: # ({/5fe8674d-5ce71b9d})

[comment]: # ({a5c86d2c-8c2bcd9e})
### Part I - New menu section

[comment]: # ({/a5c86d2c-8c2bcd9e})

[comment]: # ({9c416cbb-1d4de634})
##### Add a blank module to Zabbix frontend

1. Create a directory *MyAddress* in the *modules* directory of your Zabbix frontend installation (for example, *zabbix/ui/modules*).

2. Create a *manifest.json* file with basic module metadata (see the description of supported [parameters](../file_structure/manifest)).

**ui/modules/MyAddress/manifest.json**

```json
{
    "manifest_version": 2.0,
    "id": "my-address",
    "name": "My IP Address",
    "version": "1.0",
    "namespace": "MyAddress",
    "description": "My External IP Address."
}
```

3. In Zabbix frontend, go to *Administration → General → Modules* section and click on the *Scan directory* button.

![](../../../../assets/en/devel/modules/tutorials/widget/scan_dir.png)

4. Locate the new module *My IP Address* in the list and click on the "Disabled" hyperlink to change the module's status from "Disabled" to "Enabled"  (if the module is not listed, see [troubleshooting](/manual/extensions/frontendmodules#installation) section).

![](../../../../assets/en/devel/modules/tutorials/module/module_register.png){width="600"}

The module is now registered in the frontend. However, it is not visible yet, because you still need to define the module functionality.
Once you add content to the module directory, you will immediately see the changes in Zabbix frontend upon refreshing the page.

[comment]: # ({/9c416cbb-1d4de634})

[comment]: # ({64c4a481-d88b8114})
##### Create a menu section

1. Create a *Module.php* file in the *MyAddress* directory.

This file implements a new *Module* class that extends the default *CModule* class.
The *Module* class will insert a new *My Address* menu section into the main menu.

The *setAction()* method specifies an action to be executed upon clicking on the menu section.
To start with, you can use the predefined action *userprofile.edit*, which will open the *User profile* page.
In [part III](#part-iii---module-action) of this tutorial, you will learn how to create a custom action.

**ui/modules/MyAddress/Module.php**

```php
<?php

namespace Modules\MyAddress;

use Zabbix\Core\CModule,
    APP,
    CMenuItem;

class Module extends CModule {

    public function init(): void {
        APP::Component()->get('menu.main')
            ->add((new CMenuItem(_('My Address')))
            ->setAction('userprofile.edit'));
    }
}
```

:::noteclassic
You can replace `'userprofile.edit'` with other actions, for example, `'charts.view'` (opens custom graphs), `'problems.view'` (opens *Monitoring → Problems*), or `'report.status'` (opens *System information* report).
:::

3. Refresh Zabbix frontend. There is now a new *My Address* section at the bottom of the Zabbix main menu.
Click on *My Address* to open the *User profile* page.

![](../../../../assets/en/devel/modules/tutorials/module/my_address_top_menu.png){width="600"}

[comment]: # ({/64c4a481-d88b8114})

[comment]: # ({89dbfda8-5c3fc480})
### Part II - Menu section location change

In this part, you will move the *My Address* menu section to the *Monitoring* section and then add a nested menu to it.
As a result, users will be able to access two sub-menu pages from the *Monitoring → My Address* menu section.

1. Open and edit the *Module.php* file.

**ui/modules/MyAddress/Module.php**

```php
<?php

namespace Modules\MyAddress;

use Zabbix\Core\CModule,
    APP,
    CMenuItem;

class Module extends CModule {

    public function init(): void {
        APP::Component()->get('menu.main')
            ->findOrAdd(_('Monitoring'))
            ->getSubmenu()
            ->insertAfter(_('Discovery'),
                (new CMenuItem(_('My Address')))->setAction('userprofile.edit')
            );
    }
}
```

2. Refresh Zabbix frontend. Expand the *Monitoring* menu section and observe that the *My address* section is now located below the *Discovery* section.

![](../../../../assets/en/devel/modules/tutorials/module/my_address_monitoring.png){width="600"}

3. To add nested pages to the *My Address* menu section, open and edit the *Module.php* file again.

This step creates two subsections:

-   *External IP* that executes a new 'my.address' action that will be defined in the next steps;
-   *User profile* that executes the predefined 'userprofile.edit' action to open the *User profile* page.

Note that for the nested menu, you need to use the *CMenu* class in addition to the classes used in the previous steps.

**ui/modules/MyAddress/Module.php**

```php
<?php

namespace Modules\MyAddress;

use Zabbix\Core\CModule,
    APP,
    CMenu,
    CMenuItem;

class Module extends CModule {

    public function init(): void {
        APP::Component()->get('menu.main')
            ->findOrAdd(_('Monitoring'))
            ->getSubmenu()
            ->insertAfter(_('Discovery'),
                (new CMenuItem(_('My Address')))->setSubMenu(
                    new CMenu([
                        (new CMenuItem(_('External IP')))->setAction('my.address'),
                        (new CMenuItem(_('User profile')))->setAction('userprofile.edit')
                    ])
                )
            );
    }
}
```

4. Refresh Zabbix frontend. Observe that the *My address* menu section now contains a third-level menu with two pages - *External IP* and *User profile*.

![](../../../../assets/en/devel/modules/tutorials/module/my_address_nested.png){width="600"}

[comment]: # ({/89dbfda8-5c3fc480})

[comment]: # ({6061bdad-8798eff6})
### Part III - Module action

An action is implemented in two files - *actions/MyAddress.php* and *views/my.address.php*.
The ***actions/MyAddress.php*** file takes care of the business logic implementation, while the ***views/my.address.php*** file is responsible for the view.

1. Create a directory *actions* in the *MyAddress* directory.

2. Create a *MyAddress.php* file in the *actions* directory.

The action logic will be defined in the *MyAddress* class.
This action class will implement four functions: *init()*, *checkInput()*, *checkPermissions()*, and *doAction()*.
Zabbix frontend calls the *doAction()* function when the action is requested.
This function is responsible for the business logic of the module.

::: noteimportant
The data must be organized as an associative array.
The array can be multidimensional and may contain any data expected by the view.
:::

**ui/modules/MyAddress/actions/MyAddress.php**

```php
<?php

namespace Modules\MyAddress\Actions;

use CController,
    CControllerResponseData;

class MyAddress extends CController {

    public function init(): void {
        $this->disableCsrfValidation();
    }

    protected function checkInput(): bool {
        return true;
    }

    protected function checkPermissions(): bool {
        return true;
    }

    protected function doAction(): void {
        $data = ['my-ip' => file_get_contents("https://api.seeip.org")];
        $response = new CControllerResponseData($data);
        $this->setResponse($response);
    }
}
```

3. Create a new directory *views* in the *MyAddress* directory.

4. Create a *my.address.php* file in the *views* directory and define the module view.

Note that the variable `$data` is available in the view without specifically defining it.
The framework automatically passes the associative array to the view.

**ui/modules/MyAddress/views/my.address.php**

```php
<?php

(new CHtmlPage())
    ->setTitle(_('The HTML Title of My Address Page'))
    ->addItem(new CDiv($data['my-ip']))
    ->show();
```

5. The module action has to be registered in the *manifest.json* file. Open *manifest.json* and add a new object `actions` that contains:

-   the action key with the action name written in lowercase (a-z) and with words separated by dots (for example, `my.address`);
-   the action class name (`MyAddress`) as a value for the `class` key of the `my.address` object;
-   the action view name (`my.address`) as a value for the `view` key of the `my.address` object.

**ui/modules/MyAddress/manifest.json**

```json
{
    "manifest_version": 2.0,
    "id": "my-address",
    "name": "My IP Address",
    "version": "1.0",
    "namespace": "MyAddress",
    "description": "My External IP Address.",
    "actions": {
        "my.address": {
            "class": "MyAddress",
            "view": "my.address"
        }
    }
}
```

6. Refresh Zabbix frontend. Click on *My address → External IP* to see the IP address of your computer.

![](../../../../assets/en/devel/modules/tutorials/module/my_address_finished.png){width="600"}

[comment]: # ({/6061bdad-8798eff6})
