Utwórz widżet (samouczek)

To jest samouczek pokazujący krok po kroku, jak utworzyć prosty widget pulpitu nawigacyjnego. Możesz pobrać wszystkie pliki tego widgetu w postaci archiwum ZIP: lesson_gauge_chart.zip.

Co zbudujesz

Podczas tego samouczka najpierw zbudujesz podstawowy widżet „Hello, world!”, a następnie przekonwertujesz go na bardziej zaawansowany widżet, który wyświetla wartość pozycji jako wykres wskaźnikowy. Oto jak będzie wyglądał gotowy widżet:

Część I - "Witaj, świecie!"

W tej sekcji dowiesz się, jak utworzyć minimalną wymaganą liczbę elementów widgetu i dodać nowy widget do frontendu Zabbix.

Dodawanie pustego widgetu do frontend Zabbix

  1. Utwórz katalog lesson_gauge_chart w katalogu modules swojej instalacji frontend Zabbix (na przykład zabbix/ui/modules).

Wszystkie niestandardowe widgety są traktowane jako moduły zewnętrzne i muszą być dodawane do katalogu modules instalacji frontend Zabbix (na przykład zabbix/ui/modules). Katalog zabbix/ui/widgets jest zarezerwowany dla wbudowanych widgetów Zabbix i jest aktualizowany razem z interfejsem Zabbix UI.

  1. Utwórz plik manifest.json z podstawowymi metadanymi widgetu (zobacz opis obsługiwanych parametrów).

ui/modules/lesson_gauge_chart/manifest.json

{
    "manifest_version": 2.0,
    "id": "lesson_gauge_chart",
    "type": "widget",
    "name": "Gauge chart",
    "namespace": "LessonGaugeChart",
    "version": "1.1",
    "author": "Zabbix"
}
  1. W frontend Zabbix przejdź do sekcji Administration → General → Modules i kliknij przycisk Scan directory.

  1. Odszukaj na liście nowy moduł Gauge chart i kliknij hiperłącze "Disabled", aby zmienić status modułu z "Disabled" na "Enabled" (jeśli moduł nie jest widoczny na liście, zobacz sekcję troubleshooting).

  1. Otwórz dashboard, przełącz go w tryb edycji i dodaj nowy widget. W polu Type wybierz "Gauge chart".

  1. Na tym etapie konfiguracja widgetu Gauge chart zawiera tylko wspólne pola widgetu Name i Refresh interval. Kliknij Add, aby dodać widget do dashboardu.

  1. Na dashboardzie powinien pojawić się pusty widget. Kliknij Save changes w prawym górnym rogu, aby zapisać dashboard.

Dodawanie widoku widgetu

Plik widoku widgetu powinien znajdować się w katalogu views (w tym samouczku: ui/modules/lesson_gauge_chart/views/). Jeśli plik ma domyślną nazwę widget.view.php, nie trzeba go rejestrować w pliku manifest.json. Jeśli plik ma inną nazwę, należy ją określić w sekcji actions/widget.lesson_gauge_chart.view pliku manifest.json.

  1. Utwórz katalog views w katalogu lesson_gauge_chart.

  2. Utwórz plik widget.view.php w katalogu views.

ui/modules/lesson_gauge_chart/views/widget.view.php

<?php

/**
 * Widok widgetu wykresu wskaźnikowego.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetView($data))
    ->addItem(
        new CTag('h1', true, 'Hello, world!')
    )
    ->show();
  1. Odśwież pulpit. Widget Gauge chart wyświetla teraz „Hello, world!”.

Część II - Wykres wskaźnikowy

Dodawanie ustawień do widoku konfiguracji i używanie ich w widoku widgetu

W tej sekcji dowiesz się, jak dodać pole konfiguracji widgetu i wyświetlić wprowadzaną wartość w widoku widgetu jako tekst.

Konfiguracja widgetu składa się z formularza (Zabbix\Widgets\CWidgetForm) oraz widoku formularza widgetu (widget.edit.php). Aby dodać pola (Zabbix\Widgets\CWidgetField), należy utworzyć klasę WidgetForm, która będzie rozszerzać Zabbix\Widgets\CWidgetForm.

Formularz zawiera zestaw pól (Zabbix\Widgets\CWidgetField) różnych typów, które są używane do walidacji wartości wprowadzanych przez użytkownika. Pole formularza (Zabbix\Widgets\CWidgetField) dla każdego typu elementu wejściowego konwertuje wartość do jednolitego formatu, aby można ją było przechowywać w bazie danych.

Plik form widgetu powinien znajdować się w katalogu includes (w tym samouczku: ui/modules/lesson_gauge_chart/includes/). Jeśli plik ma domyślną nazwę WidgetForm.php, nie trzeba go rejestrować w pliku manifest.json. Jeśli plik ma inną nazwę, należy ją określić w sekcji widget/form_class pliku manifest.json.

  1. Utwórz nowy katalog includes w katalogu lesson_gauge_chart.

  2. Utwórz plik WidgetForm.php w katalogu includes.

ui/modules/lesson_gauge_chart/includes/WidgetForm.php

<?php

namespace Modules\LessonGaugeChart\Includes;

use Zabbix\Widgets\CWidgetForm;

class WidgetForm extends CWidgetForm {
}
  1. Dodaj pole Description do formularza konfiguracji widgetu. Jest to zwykłe pole tekstowe, w którym użytkownik może wprowadzić dowolny zestaw znaków. Możesz do tego użyć klasy CWidgetFieldTextBox.

ui/modules/lesson_gauge_chart/includes/WidgetForm.php

<?php

namespace Modules\LessonGaugeChart\Includes;

use Zabbix\Widgets\CWidgetForm;

use Zabbix\Widgets\Fields\CWidgetFieldTextBox;

class WidgetForm extends CWidgetForm {

    public function addFields(): self {
        return $this
            ->addField(
               new CWidgetFieldTextBox('description', _('Description'))
            );
   }
}
  1. W katalogu views utwórz plik widoku konfiguracji widgetu widget.edit.php i dodaj widok dla nowego pola Description. Dla klasy pola CWidgetFieldTextBox widokiem jest CWidgetFieldTextBoxView.

ui/modules/lesson_gauge_chart/views/widget.edit.php

<?php

/**
 * Gauge chart widget form view.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetFormView($data))
    ->addField(
        new CWidgetFieldTextBoxView($data['fields']['description'])
    )
    ->show();
  1. Przejdź do pulpitu i kliknij ikonę koła zębatego w widżecie, aby otworzyć formularz konfiguracji widgetu.

  2. Formularz konfiguracji widgetu zawiera teraz nowe pole tekstowe Description. Wprowadź dowolną wartość, na przykład Gauge chart description.

  1. Kliknij Apply w formularzu konfiguracji widgetu. Następnie kliknij Save changes w prawym górnym rogu, aby zapisać pulpit. Zwróć uwagę, że nowy opis nie jest nigdzie widoczny, a widget nadal wyświetla „Hello, world!”.

Aby nowy opis pojawił się w widżecie, wartość pola Description musi zostać pobrana z bazy danych i przekazana do widoku widgetu. W tym celu należy utworzyć klasę akcji.

  1. Utwórz nowy katalog actions w katalogu lesson_gauge_chart.

  2. Utwórz plik WidgetView.php w katalogu actions. Klasa akcji WidgetView będzie rozszerzać klasę CControllerDashboardWidgetView.

Wartości pól konfiguracji widgetu są przechowywane we właściwości $fields_values klasy akcji.

ui/modules/lesson_gauge_chart/actions/WidgetView.php

<?php

namespace Modules\LessonGaugeChart\Actions;

use CControllerDashboardWidgetView,
    CControllerResponseData;

class WidgetView extends CControllerDashboardWidgetView {

    protected function doAction(): void {
        $this->setResponse(new CControllerResponseData([
            'name' => $this->getInput('name', $this->widget->getName()),
            'description' => $this->fields_values['description'],
            'user' => [
                'debug_mode' => $this->getDebugMode()
            ]
        ]));
    }
}
  1. Otwórz plik manifest.json i zarejestruj WidgetView jako klasę akcji w sekcji actions/widget.lesson_gauge_chart.view.

ui/modules/lesson_gauge_chart/manifest.json

{
    "manifest_version": 2.0,
    "id": "lesson_gauge_chart",
    "type": "widget",
    "name": "Gauge chart",
    "namespace": "LessonGaugeChart",
    "version": "1.0",
    "author": "Zabbix",
    "actions": {
        "widget.lesson_gauge_chart.view": {
            "class": "WidgetView"
        }
    }
}
  1. Teraz możesz użyć wartości pola opisu, zawartej w $data['description'], w widoku widgetu. Otwórz plik views/widget.view.php i zastąp statyczny tekst „Hello, world!” wartością $data['description'].

ui/modules/lesson_gauge_chart/views/widget.view.php

<?php

/**
 * Gauge chart widget view.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetView($data))
    ->addItem(
        new CTag('h1', true, $data['description'])
    )
    ->show();
  1. Odśwież stronę pulpitu. Powinien teraz być widoczny tekst opisu widgetu zamiast „Hello, world!”.

Pobieranie wartości pozycji przez API

Widget powinien wyświetlać ostatnią wartość pozycji wybranej przez użytkownika. W tym celu należy dodać możliwość wyboru pozycji w konfiguracji widgetu.

W tej sekcji dowiesz się, jak dodać pole wyboru pozycji do formularza widgetu oraz jak dodać wizualną część tego pola do widoku konfiguracji. Następnie kontroler widgetu będzie mógł pobrać dane pozycji i jej wartość za pomocą żądania API. Po odebraniu wartość będzie mogła zostać wyświetlona w widoku widgetu.

  1. Otwórz plik includes/WidgetForm.php i dodaj pole CWidgetFieldMultiSelectItem. Pozwoli to na wybór pozycji w formularzu konfiguracji.

ui/modules/lesson_gauge_chart/includes/WidgetForm.php

<?php

namespace Modules\LessonGaugeChart\Includes;

use Zabbix\Widgets\{
    CWidgetField,
    CWidgetForm
};

use Zabbix\Widgets\Fields\{
    CWidgetFieldMultiSelectItem,
    CWidgetFieldTextBox
};

/**
 * Formularz widgetu wykresu wskaźnikowego.
 */
class WidgetForm extends CWidgetForm {

    public function addFields(): self {
        return $this
            ->addField(
                (new CWidgetFieldMultiSelectItem('itemid', _('Item')))
                    ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
                    ->setMultiple(false)
            )
            ->addField(
                new CWidgetFieldTextBox('description', _('Description'))
            );
    }
}
  1. Otwórz plik views/widget.edit.php i dodaj wizualny komponent pola do widoku konfiguracji.

ui/modules/lesson_gauge_chart/views/widget.edit.php

<?php

/**
 * Widok formularza widgetu wykresu wskaźnikowego.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetFormView($data))
    ->addField(
        new CWidgetFieldMultiSelectItemView($data['fields']['itemid'])
    )
    ->addField(
        new CWidgetFieldTextBoxView($data['fields']['description'])
    )
    ->show();
  1. Wróć do pulpitu i kliknij ikonę koła zębatego w widżecie, aby otworzyć formularz konfiguracji widgetu.

  2. Formularz konfiguracji widgetu zawiera teraz nowe pole wejściowe Item. Wybierz host "Zabbix server" oraz pozycję "Load average (1m avg)".

  1. Kliknij Apply w formularzu konfiguracji widgetu. Następnie kliknij Save changes w prawym górnym rogu, aby zapisać pulpit.

  2. Otwórz i zmodyfikuj plik actions/WidgetView.php.

Od tej chwili ID pozycji będzie dostępne w kontrolerze widgetu w $this->fields_values['itemid']. Metoda kontrolera doAction() pobiera dane pozycji (nazwę, typ wartości, jednostki) za pomocą metody API item.get oraz ostatnią wartość pozycji za pomocą metody API history.get.

ui/modules/lesson_gauge_chart/actions/WidgetView.php

<?php

namespace Modules\LessonGaugeChart\Actions;

use API,
    CControllerDashboardWidgetView,
    CControllerResponseData;

class WidgetView extends CControllerDashboardWidgetView {

    protected function doAction(): void {
        $db_items = API::Item()->get([
            'output' => ['itemid', 'value_type', 'name', 'units'],
            'itemids' => $this->fields_values['itemid'],
            'webitems' => true,
            'filter' => [
                'value_type' => [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT]
            ]
        ]);

        $value = null;

        if ($db_items) {
            $item = $db_items[0];

            $history = API::History()->get([
                'output' => API_OUTPUT_EXTEND,
                'itemids' => $item['itemid'],
                'history' => $item['value_type'],
                'sortfield' => 'clock',
                'sortorder' => ZBX_SORT_DOWN,
                'limit' => 1
            ]);

            if ($history) {
                $value = convertUnitsRaw([
                    'value' => $history[0]['value'],
                    'units' => $item['units']
                ]);
            }
        }

        $this->setResponse(new CControllerResponseData([
            'name' => $this->getInput('name', $this->widget->getName()),
            'value' => $value,
            'description' => $this->fields_values['description'],
            'user' => [
                'debug_mode' => $this->getDebugMode()
            ]
        ]));
    }
}
  1. Otwórz plik views/widget.view.php i dodaj wartość pozycji do widoku widgetu.

ui/modules/lesson_gauge_chart/views/widget.view.php

<?php

/**
 * Widok widgetu wykresu wskaźnikowego.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetView($data))
    ->addItem([
        new CTag('h1', true, $data['description']),
        new CDiv($data['value'] !== null ? $data['value']['value'] : _('No data'))
    ])
    ->show();
  1. Odśwież stronę pulpitu. Widget wyświetli najnowszą wartość pozycji.

Dodawanie zaawansowanych ustawień konfiguracji do widoku konfiguracji

W tej sekcji dowiesz się, jak dodać rozwijaną/zwijaną sekcję Zaawansowana konfiguracja z opcjonalnymi parametrami, takimi jak kolor, wartości minimalna i maksymalna, jednostki oraz pole Opis utworzone wcześniej.

  1. Utwórz plik Widget.php w głównym katalogu widgetu lesson_gauge_chart, aby utworzyć nową klasę Widget.

Klasa Widget będzie rozszerzać klasę bazową CWidget, aby dodać/zastąpić domyślne ustawienia widgetu (w tym przypadku — tłumaczenia). JavaScript podany poniżej wyświetla ciąg „Brak danych” w przypadku braku danych. Ciąg „Brak danych” jest obecny w plikach tłumaczeń interfejsu Zabbix.

Jeśli istnieją jakiekolwiek stałe widgetu, zaleca się również określenie ich w klasie Widget.

ui/modules/lesson_gauge_chart/Widget.php

<?php

namespace Modules\LessonGaugeChart;

use Zabbix\Core\CWidget;

class Widget extends CWidget {

    public const UNIT_AUTO = 0;
    public const UNIT_STATIC = 1;

    public function getTranslationStrings(): array {
        return [
            'class.widget.js' => [
                'No data' => _('No data')
            ]
        ];
    }
}
  1. Otwórz includes/WidgetForm.php i dodaj nowe pola Kolor (selektor koloru), Min (pole numeryczne), Max (pole numeryczne) oraz Jednostki (lista wyboru), a także zdefiniuj domyślną paletę kolorów dla selektora koloru, aby można było jej użyć w kolejnych krokach.

ui/modules/lesson_gauge_chart/includes/WidgetForm.php

<?php

namespace Modules\LessonGaugeChart\Includes;

use Modules\LessonGaugeChart\Widget;

use Zabbix\Widgets\{
    CWidgetField,
    CWidgetForm
};

use Zabbix\Widgets\Fields\{
    CWidgetFieldColor,
    CWidgetFieldMultiSelectItem,
    CWidgetFieldNumericBox,
    CWidgetFieldSelect,
    CWidgetFieldTextBox
};

/**
 * Formularz widgetu wykresu wskaźnikowego.
 */
class WidgetForm extends CWidgetForm {

    public const DEFAULT_COLOR_PALETTE = [
        'FF465C', 'B0AF07', '0EC9AC', '524BBC', 'ED1248', 'D1E754', '2AB5FF', '385CC7', 'EC1594', 'BAE37D',
        '6AC8FF', 'EE2B29', '3CA20D', '6F4BBC', '00A1FF', 'F3601B', '1CAE59', '45CFDB', '894BBC', '6D6D6D'
    ];

    public function addFields(): self {
        return $this
            ->addField(
                (new CWidgetFieldMultiSelectItem('itemid', _('Item')))
                    ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
                    ->setMultiple(false)
            )
            ->addField(
                (new CWidgetFieldColor('chart_color', _('Color')))->setDefault('FF0000')
            )
            ->addField(
                (new CWidgetFieldNumericBox('value_min', _('Min')))
                    ->setDefault(0)
                    ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
            )
            ->addField(
                (new CWidgetFieldNumericBox('value_max', _('Max')))
                    ->setDefault(100)
                    ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
            )
            ->addField(
                (new CWidgetFieldSelect('value_units', _('Units'), [
                    Widget::UNIT_AUTO => _x('Auto', 'history source selection method'),
                    Widget::UNIT_STATIC => _x('Static', 'history source selection method')
                ]))->setDefault(Widget::UNIT_AUTO)
            )
            ->addField(
                (new CWidgetFieldTextBox('value_static_units'))
            )
            ->addField(
                new CWidgetFieldTextBox('description', _('Description'))
            );
    }
}
  1. Otwórz views/widget.edit.php i dodaj wizualne komponenty pól do widoku konfiguracji.

ui/modules/lesson_gauge_chart/views/widget.edit.php

<?php

/**
 * Widok formularza widgetu wykresu wskaźnikowego.
 *
 * @var CView $this
 * @var array $data
 */

$lefty_units = new CWidgetFieldSelectView($data['fields']['value_units']);
$lefty_static_units = (new CWidgetFieldTextBoxView($data['fields']['value_static_units']))
    ->setPlaceholder(_('value'))
    ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);

(new CWidgetFormView($data))
    ->addField(
        (new CWidgetFieldMultiSelectItemView($data['fields']['itemid']))
            ->setPopupParameter('numeric', true)
    )
    ->addFieldset(
        (new CWidgetFormFieldsetCollapsibleView(_('Advanced configuration')))
            ->addField(
                new CWidgetFieldColorView($data['fields']['chart_color'])
            )
            ->addField(
                new CWidgetFieldNumericBoxView($data['fields']['value_min'])
            )
            ->addField(
                new CWidgetFieldNumericBoxView($data['fields']['value_max'])
            )
            ->addItem([
                $lefty_units->getLabel(),
                (new CFormField([
                    $lefty_units->getView()->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
                    $lefty_static_units->getView()
                ]))
            ])
            ->addField(
                new CWidgetFieldTextBoxView($data['fields']['description'])
            )
    )
    ->show();

Metoda addField() klasy CWidgetFormView przyjmuje ciąg klasy CSS jako drugi parametr.

  1. Wróć do pulpitu, przełącz się w tryb edycji i kliknij ikonę koła zębatego w widżecie, aby otworzyć formularz konfiguracji widgetu. Formularz konfiguracji widgetu zawiera teraz nową rozwijaną/zwijaną sekcję Zaawansowana konfiguracja.

  1. Rozwiń sekcję Zaawansowana konfiguracja, aby zobaczyć dodatkowe pola konfiguracji widgetu. Zwróć uwagę, że pole Kolor nie ma jeszcze selektora koloru. Dzieje się tak dlatego, że selektor koloru musi zostać zainicjowany za pomocą JavaScript, który zostanie dodany w następnej sekcji — Dodawanie JavaScript do widgetu.

Dodawanie JavaScript do widgetu

W tej sekcji dowiesz się, jak dodać wykres wskaźnikowy (gauge chart) — utworzony przy użyciu JavaScript — który pokazuje, czy najnowsza wartość jest prawidłowa, czy zbyt wysoka/zbyt niska.

  1. Utwórz plik widget.edit.js.php w katalogu views.

JavaScript będzie odpowiadał za inicjalizację selektora kolorów w widoku konfiguracji.

ui/modules/lesson_gauge_chart/views/widget.edit.js.php

<?php

use Modules\LessonGaugeChart\Widget;

?>

window.widget_lesson_gauge_chart_form = new class {

    init({color_palette}) {
        this._unit_select = document.getElementById('value_units');
        this._unit_value = document.getElementById('value_static_units');

        this._unit_select.addEventListener('change', () => this.updateForm());

        colorPalette.setThemeColors(color_palette);

        for (const colorpicker of jQuery('.<?= ZBX_STYLE_COLOR_PICKER ?> input')) {
            jQuery(colorpicker).colorpicker();
        }

        const overlay = overlays_stack.getById('widget_properties');

        for (const event of ['overlay.reload', 'overlay.close']) {
            overlay.$dialogue[0].addEventListener(event, () => { jQuery.colorpicker('hide'); });
        }

        this.updateForm();
    }

    updateForm() {
        this._unit_value.disabled = this._unit_select.value == <?= Widget::UNIT_AUTO ?>;
    }
};
  1. Otwórz views/widget.edit.php i dodaj plik widget.edit.js.php z kodem JavaScript do widoku konfiguracji. Aby to zrobić, użyj metody includeJsFile(). Aby dodać osadzony kod JavaScript, użyj metody addJavaScript().

ui/modules/lesson_gauge_chart/views/widget.edit.php

<?php

/**
 * Widok formularza widgetu wykresu wskaźnikowego.
 *
 * @var CView $this
 * @var array $data
 */

use Modules\LessonGaugeChart\Includes\WidgetForm;

$lefty_units = new CWidgetFieldSelectView($data['fields']['value_units']);
$lefty_static_units = (new CWidgetFieldTextBoxView($data['fields']['value_static_units']))
    ->setPlaceholder(_('value'))
    ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);

(new CWidgetFormView($data))
    ->addField(
        (new CWidgetFieldMultiSelectItemView($data['fields']['itemid']))
            ->setPopupParameter('numeric', true)
    )
    ->addFieldset(
        (new CWidgetFormFieldsetCollapsibleView(_('Advanced configuration')))
            ->addField(
                new CWidgetFieldColorView($data['fields']['chart_color'])
            )
            ->addField(
                new CWidgetFieldNumericBoxView($data['fields']['value_min'])
            )
            ->addField(
                new CWidgetFieldNumericBoxView($data['fields']['value_max'])
            )
            ->addItem([
                $lefty_units->getLabel(),
                (new CFormField([
                    $lefty_units->getView()->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
                    $lefty_static_units->getView()
                ]))
            ])
            ->addField(
                new CWidgetFieldTextBoxView($data['fields']['description'])
            )
    )
    ->includeJsFile('widget.edit.js.php')
    ->addJavaScript('widget_lesson_gauge_chart_form.init('.json_encode([
        'color_palette' => WidgetForm::DEFAULT_COLOR_PALETTE
    ], JSON_THROW_ON_ERROR).');')
    ->show();
  1. Wróć do pulpitu, kliknij ikonę koła zębatego w widżecie, aby otworzyć formularz konfiguracji widgetu. Następnie rozwiń sekcję Advanced configuration, aby zobaczyć zainicjalizowany selektor kolorów. Wypełnij pola wartościami i wybierz kolor dla wykresu wskaźnikowego.

  1. Kliknij Apply w formularzu konfiguracji widgetu. Następnie kliknij Save changes w prawym górnym rogu, aby zapisać pulpit.

  2. Otwórz actions/WidgetView.php i zaktualizuj kontroler.

Właściwość $this->fields_values zawiera teraz wartości wszystkich pól z sekcji Advanced configuration. Uzupełnij kontroler, aby umożliwić przekazywanie konfiguracji i wartości wybranej pozycji do widoku widgetu.

ui/modules/lesson_gauge_chart/actions/WidgetView.php

<?php

namespace Modules\LessonGaugeChart\Actions;

use API,
    CControllerDashboardWidgetView,
    CControllerResponseData;

class WidgetView extends CControllerDashboardWidgetView {

    protected function doAction(): void {
        $db_items = API::Item()->get([
            'output' => ['itemid', 'value_type', 'name', 'units'],
            'itemids' => $this->fields_values['itemid'],
            'webitems' => true,
            'filter' => [
                'value_type' => [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT]
            ]
        ]);

        $history_value = null;

        if ($db_items) {
            $item = $db_items[0];

            $history = API::History()->get([
                'output' => API_OUTPUT_EXTEND,
                'itemids' => $item['itemid'],
                'history' => $item['value_type'],
                'sortfield' => 'clock',
                'sortorder' => ZBX_SORT_DOWN,
                'limit' => 1
            ]);

            if ($history) {
                $history_value = convertUnitsRaw([
                    'value' => $history[0]['value'],
                    'units' => $item['units']
                ]);
            }
        }

        $this->setResponse(new CControllerResponseData([
            'name' => $this->getInput('name', $this->widget->getName()),
            'history' => $history_value,
            'fields_values' => $this->fields_values,
            'user' => [
                'debug_mode' => $this->getDebugMode()
            ]
        ]));
    }
}
  1. Otwórz i zmodyfikuj views/widget.view.php.

Musisz utworzyć kontener dla wykresu wskaźnikowego, który narysujesz w kolejnych krokach, oraz kontener dla opisu.

Aby przekazać wartości do JavaScript jako obiekt JSON, użyj metody setVar().

ui/modules/lesson_gauge_chart/views/widget.view.php

<?php

/**
 * Widok widgetu wykresu wskaźnikowego.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetView($data))
    ->addItem([
        (new CDiv())->addClass('chart'),
        $data['fields_values']['description']
            ? (new CDiv($data['fields_values']['description']))->addClass('description')
            : null
    ])
    ->setVar('history', $data['history'])
    ->setVar('fields_values', $data['fields_values'])
    ->show();
  1. Utwórz nowy katalog assets w katalogu lesson_gauge_chart. Ten katalog będzie używany do przechowywania JavaScript, CSS oraz potencjalnie innych zasobów, takich jak czcionki lub obrazy.

  2. Dla JavaScript widoku widgetu utwórz katalog js w katalogu assets.

  3. Utwórz plik class.widget.js w katalogu assets/js.

Ta klasa JavaScript widgetu będzie rozszerzać bazową klasę JavaScript wszystkich widgetów pulpitu — CWidget.

Pulpit opiera się na poprawnej implementacji widgetu i przekazuje do niego wszystkie istotne informacje poprzez wywoływanie odpowiednich metod JavaScript. Pulpit oczekuje również, że widget będzie generował zdarzenia, gdy wystąpi jakaś interakcja. Dlatego klasa CWidget zawiera zestaw metod z domyślną implementacją zachowania widgetu, które można dostosować przez rozszerzenie tej klasy.

W tym przypadku konieczne jest pewne dostosowanie, dlatego zostanie zaimplementowana własna logika dla następujących aspektów działania widgetu:

  • inicjalizacja widgetu, która odpowiada za zdefiniowanie początkowego stanu widgetu (zobacz metodę onInitialize());
  • wyświetlanie zawartości widgetu (to znaczy rysowanie wykresu wskaźnikowego), jeśli proces aktualizacji widgetu zakończył się powodzeniem i bez błędów (zobacz metodę processUpdateResponse(response) oraz powiązane metody _resizeChart() i _updatedChart())
  • zmiana rozmiaru widgetu (zobacz metodę onResize() oraz powiązaną metodę _resizeChart())

W przypadku innych aspektów widgetu wykresu wskaźnikowego zostanie użyta domyślna implementacja zachowania widgetu. Aby dowiedzieć się więcej o metodach JavaScript klasy CWidget, zobacz: JavaScript.

Ponieważ ten kod JavaScript jest wymagany dla widoku widgetu, powinien być ładowany wraz ze stroną pulpitu. Aby włączyć ładowanie JavaScript, musisz zaktualizować parametry assets/js oraz js_class w pliku manifest.json, jak pokazano w kroku 10.

ui/modules/lesson_gauge_chart/assets/js/class.widget.js

class WidgetLessonGaugeChart extends CWidget {

    static UNIT_AUTO = 0;
    static UNIT_STATIC = 1;

    onInitialize() {
        super.onInitialize();

        this._refresh_frame = null;
        this._chart_container = null;
        this._canvas = null;
        this._chart_color = null;
        this._min = null;
        this._max = null;
        this._value = null;
        this._last_value = null;
        this._units = '';
    }

    processUpdateResponse(response) {
        if (response.history === null) {
            this._value = null;
            this._units = '';
        }
        else {
            this._value = Number(response.history.value);
            this._units = response.fields_values.value_units == WidgetLessonGaugeChart.UNIT_AUTO
                ? response.history.units
                : response.fields_values.value_static_units;
        }

        this._chart_color = response.fields_values.chart_color;
        this._min = Number(response.fields_values.value_min);
        this._max = Number(response.fields_values.value_max);

        super.processUpdateResponse(response);
    }

    setContents(response) {
        if (this._canvas === null) {
            super.setContents(response);

            this._chart_container = this._body.querySelector('.chart');
            this._chart_container.style.height =
                `${this._getContentsSize().height - this._body.querySelector('.description').clientHeight}px`;
            this._canvas = document.createElement('canvas');

            this._chart_container.appendChild(this._canvas);

            this._resizeChart();
        }

        this._updatedChart();
    }

    onResize() {
        super.onResize();

        if (this._state === WIDGET_STATE_ACTIVE) {
            this._resizeChart();
        }
    }

    _resizeChart() {
        const ctx = this._canvas.getContext('2d');
        const dpr = window.devicePixelRatio;

        this._canvas.style.display = 'none';
        const size = Math.min(this._chart_container.offsetWidth, this._chart_container.offsetHeight);
        this._canvas.style.display = '';

        this._canvas.width = size * dpr;
        this._canvas.height = size * dpr;

        ctx.scale(dpr, dpr);

        this._canvas.style.width = `${size}px`;
        this._canvas.style.height = `${size}px`;

        this._refresh_frame = null;

        this._updatedChart();
    }

    _updatedChart() {
        if (this._last_value === null) {
            this._last_value = this._min;
        }

        const start_time = Date.now();
        const end_time = start_time + 400;

        const animate = () => {
            const time = Date.now();

            if (time <= end_time) {
                const progress = (time - start_time) / (end_time - start_time);
                const smooth_progress = 0.5 + Math.sin(Math.PI * (progress - 0.5)) / 2;
                let value = this._value !== null ? this._value : this._min;
                value = (this._last_value + (value - this._last_value) * smooth_progress - this._min) / (this._max - this._min);

                const ctx = this._canvas.getContext('2d');
                const size = this._canvas.width;
                const char_weight = size / 12;
                const char_shadow = 3;
                const char_x = size / 2;
                const char_y = size / 2;
                const char_radius = (size - char_weight) / 2 - char_shadow;

                const font_ratio = 32 / 100;

                ctx.clearRect(0, 0, size, size);

                ctx.beginPath();
                ctx.shadowBlur = char_shadow;
                ctx.shadowColor = '#bbb';
                ctx.strokeStyle = '#eee';
                ctx.lineWidth = char_weight;
                ctx.lineCap = 'round';
                ctx.arc(char_x, char_y, char_radius, Math.PI * 0.749, Math.PI * 2.251, false);
                ctx.stroke();

                ctx.beginPath();
                ctx.strokeStyle = `#${this._chart_color}`;
                ctx.lineWidth = char_weight - 2;
                ctx.lineCap = 'round';
                ctx.arc(char_x, char_y, char_radius, Math.PI * 0.75,
                    Math.PI * (0.75 + (1.5 * Math.min(1, Math.max(0, value)))), false
                    );
                ctx.stroke();

                ctx.shadowBlur = 2;
                ctx.fillStyle = '#1f2c33';
                ctx.font = `${(char_radius * font_ratio)|0}px Arial`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText(`${this._value !== null ? this._value : t('No data')}${this._units}`,
                    char_x, char_y, size - char_shadow * 4 - char_weight * 2
                );

                ctx.fillStyle = '#768d99';
                ctx.font = `${(char_radius * font_ratio * .5)|0}px Arial`;
                ctx.textBaseline = 'top';

                ctx.textAlign = 'left';
                ctx.fillText(`${this._min}${this._min != '' ? this._units : ''}`,
                    char_weight * .75, size - char_weight * 1.25, size / 2 - char_weight
                );

                ctx.textAlign = 'right';
                ctx.fillText(`${this._max}${this._max != '' ? this._units : ''}`,
                    size - char_weight * .75, size - char_weight * 1.25, size / 2 - char_weight
                );

                requestAnimationFrame(animate);
            }
            else {
                this._last_value = this._value;
            }
        };

        requestAnimationFrame(animate);
    }
}
  1. Otwórz manifest.json i dodaj:
  • nazwę pliku (class.widget.js) do tablicy w sekcji assets/js;
  • nazwę klasy (WidgetLessonGaugeChart) do parametru js_class w sekcji widget.

Klasa WidgetLessonGaugeChart będzie teraz automatycznie ładowana wraz z pulpitem.

ui/modules/lesson_gauge_chart/manifest.json

{
    "manifest_version": 2.0,
    "id": "lesson_gauge_chart",
    "type": "widget",
    "name": "Gauge chart",
    "namespace": "LessonGaugeChart",
    "version": "1.0",
    "author": "Zabbix",
    "actions": {
        "widget.lesson_gauge_chart.view": {
            "class": "WidgetView"
        }
    },
    "widget": {
        "js_class": "WidgetLessonGaugeChart"
    },
    "assets": {
        "js": ["class.widget.js"]
    }
}

Dodawanie stylów CSS do widgetu

W tej sekcji dowiesz się, jak dodać niestandardowe style CSS, aby widget wyglądał bardziej atrakcyjnie.

  1. Dla stylów widgetu utwórz nowy katalog css w katalogu assets.

  2. Utwórz plik widget.css w katalogu assets/css. Aby nadać styl elementom widgetu, użyj selektora div.dashboard-widget-{widget id}. Aby skonfigurować CSS dla całego widgetu, użyj selektora form.dashboard-widget-{widget id}

ui/modules/lesson_gauge_chart/assets/css/widget.css

div.dashboard-widget-lesson_gauge_chart {
    display: grid;
    grid-template-rows: 1fr;
    padding: 0;
}

div.dashboard-widget-lesson_gauge_chart .chart {
    display: grid;
    align-items: center;
    justify-items: center;
}

div.dashboard-widget-lesson_gauge_chart .chart canvas {
    background: white;
}

div.dashboard-widget-lesson_gauge_chart .description {
    padding-bottom: 8px;
    font-size: 1.750em;
    line-height: 1.2;
    text-align: center;
}

.dashboard-grid-widget-hidden-header div.dashboard-widget-lesson_gauge_chart .chart {
    margin-top: 8px;
}
  1. Otwórz plik manifest.json i dodaj nazwę pliku CSS (widget.css) do tablicy w sekcji assets/css. Umożliwi to wczytanie stylów CSS zdefiniowanych w pliku widget.css wraz ze stroną pulpitu.

ui/modules/lesson_gauge_chart/manifest.json

{
    "manifest_version": 2.0,
    "id": "lesson_gauge_chart",
    "type": "widget",
    "name": "Gauge chart",
    "namespace": "LessonGaugeChart",
    "version": "1.0",
    "author": "Zabbix",
    "actions": {
        "widget.lesson_gauge_chart.view": {
            "class": "WidgetView"
        }
    },
    "widget": {
        "js_class": "WidgetLessonGaugeChart"
    },
    "assets": {
        "css": ["widget.css"],
        "js": ["class.widget.js"]
    }
}
  1. Odśwież stronę pulpitu, aby zobaczyć gotową wersję widgetu.