[comment]: # aside:1

[comment]: # ({b311df1e-8fe7205c})
# 创建一个模块（教程）

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

[comment]: # ({98067368-5fb5b83a})
这是一个分步教程，演示如何create一个简单的Zabbix前端模块。  
您可以将该模块的所有文件下载为ZIP压缩包：[MyAddress.zip](../../../../assets/en/devel/modules/examples/MyAddress.zip)。

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

[comment]: # ({5fe8674d-5ce71b9d})
### 你将构建的内容

在本教程中，您将首先构建一个前端模块，该模块添加一个新的 *我的地址* 菜单部分，
然后将其转换为一个[更高级的](#part-iii---module-action)前端模块，该模块向 *https://api.seeip.org* 发起 HTTP 请求，
并显示响应结果（即您的计算机的 IP 地址）在新创建的 *我的地址* 菜单部分中的一个新页面上。
以下是完成后的模块外观：

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

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

[comment]: # ({a5c86d2c-8c2bcd9e})
### 第一部分 - 新的菜单部分

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

[comment]: # ({9c416cbb-1d4de634})
##### 向 Zabbix 前端添加空白模块

1. 在你的 Zabbix 前端安装的 *modules* 目录中（例如，*zabbix/ui/modules*）创建一个目录 *MyAddress*。

2. 创建一个包含基本模块元数据的 *manifest.json* file 文件（请参阅支持的 [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. 在 Zabbix 前端中，进入 *Administration → General → Modules* 部分，然后点击 *Scan directory* 按钮。

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

4. 在列表中找到新模块 *My IP Address*，然后点击 "Disabled" 超链接，将模块的状态从 "Disabled" 更改为 "Enabled"（如果未列出该模块，请参阅 [安装](/manual/extensions/frontendmodules#安装) 部分）。

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

现在该模块已在前端注册。但是，它还不可见，因为你仍需要定义模块的功能。  
向模块目录中添加内容后，在刷新页面后你将立即在 Zabbix 前端中看到更改。

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

[comment]: # ({64c4a481-d88b8114})
##### 创建菜单部分

1. 在 *MyAddress* 目录中创建一个 *Module.php* file。

该 file 实现了一个新的 *Module* 类，该类继承默认的 *CModule* 类。  
*Module* 类会将一个新的 *My Address* 菜单部分插入到主菜单中。

*setAction()* 方法用于指定点击菜单部分时执行的操作。  
初始情况下，您可以使用预定义操作 *userprofile.edit*，它将打开 *User profile* 页面。  
在本教程的[第三部分](#part-iii---module-action)中，您将学习如何 create 自定义操作。

**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
您可以将 `'userprofile.edit'` 替换为其他操作，例如，`'charts.view'`（打开自定义图表）、`'problems.view'`（打开 *Monitoring → Problems*）、或 `'report.status'`（打开 *System information* 报告）。
:::

3. 刷新 Zabbix 前端。现在在 Zabbix 主菜单底部有一个新的 *My Address* 部分。  
点击 *My Address* 打开 *User profile* 页面。

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

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

[comment]: # ({89dbfda8-5c3fc480})
### 第二部分 - 菜单部分位置更改

在本部分中，您将把 *My Address* 菜单部分移动到 *Monitoring* 菜单部分，然后向其添加一个嵌套菜单。
最终，用户将能够从 *Monitoring → My Address* 菜单部分访问两个子菜单页面。

1. 打开并编辑 *Module.php* 文件。

**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. 刷新 Zabbix 前端界面。展开 *Monitoring* 菜单部分，观察到 *My address* 部分现在位于 *Discovery* 部分下方。

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

3. 要向 *My Address* 菜单部分添加嵌套页面，请再次打开并编辑 *Module.php* file。

此步骤将创建两个子部分：

-   *External IP*，执行将在后续步骤中定义的新“my.address”动作；
-   *User profile*，执行预定义的“userprofile.edit”动作以打开 *User profile* 页面。

请注意，对于嵌套菜单，除了之前步骤中使用的类之外，还需要使用 *CMenu* 类。

**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. 刷新 Zabbix 前端界面。观察到 *My address* 菜单部分现在包含一个三级菜单，有两个页面：*External IP* 和 *User profile*。

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

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

[comment]: # ({6061bdad-8798eff6})
### 第三部分 - 模块动作

一个动作在两个文件中实现：*actions/MyAddress.php* 和 *views/my.address.php*。  
***actions/MyAddress.php*** file 负责业务逻辑的实现，而 ***views/my.address.php*** file 则负责视图部分。

1. 在 *MyAddress* 目录中创建一个 *actions* 目录。

2. 在 *actions* 目录中创建一个 *MyAddress.php* file 文件。

动作逻辑将在 *MyAddress* 类中定义。  
该动作类将实现四个函数：*init()*、*checkInput()*、*checkPermissions()* 和 *doAction()*。  
当请求该动作时，Zabbix 前端会调用 *doAction()* 函数。  
该函数负责模块的业务逻辑。

::: noteimportant
数据必须组织为一个关联 array（数组）。  
该 array 可以是多维的，并且可以包含视图所需的所有数据。
:::

**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. 在 *MyAddress* 目录中创建一个新的 *views* 目录。

4. 在 *views* 目录中创建 *my.address.php* file 文件，并定义模块视图。

请注意，变量 `$data` 在视图中是可用的，无需特别定义。  
框架会自动将关联 array 传递给视图。

**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. 模块动作必须在 *manifest.json* 文件中注册。打开 *manifest.json* 并添加一个新的 object `actions`，其中包含：

-   动作键，其值为小写字母（a-z）书写并用点分隔的名称（例如，`my.address`）；
-   动作类名（`MyAddress`），作为 `my.address` object 中 `class` 键的值；
-   动作视图名（`my.address`），作为 `my.address` object 中 `view` 键的值。

**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. 刷新 Zabbix 前端。点击 *My address → External IP* 以查看您计算机的 IP 地址。

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

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