mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-31 07:12:23 -07:00
DOCS: Plugins docs refactor
This commit is contained in:
642
docs/PLUGINS_DEV_UI_COMPONENTS.md
Normal file
642
docs/PLUGINS_DEV_UI_COMPONENTS.md
Normal file
@@ -0,0 +1,642 @@
|
||||
# Plugin UI Components
|
||||
|
||||
Configure how your plugin's data is displayed in the NetAlertX web interface.
|
||||
|
||||
## Overview
|
||||
|
||||
Plugin results are displayed in the UI via the **Plugins page** and **Device details tabs**. You control the appearance and functionality of these displays by defining `database_column_definitions` in your plugin's `config.json`.
|
||||
|
||||
Each column definition specifies:
|
||||
- Which data field to display
|
||||
- How to render it (label, link, color-coded badge, etc.)
|
||||
- What CSS classes to apply
|
||||
- What transformations to apply (regex, string replacement, etc.)
|
||||
|
||||
## Column Definition Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Object_PrimaryID",
|
||||
"mapped_to_column": "devMac",
|
||||
"mapped_to_column_data": null,
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "device_mac",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"options_params": [],
|
||||
"localized": ["name"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "MAC Address"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
|----------|------|----------|-------------|
|
||||
| `column` | string | **YES** | Source column name from data contract (e.g., `Object_PrimaryID`, `Watched_Value1`) |
|
||||
| `mapped_to_column` | string | no | Target database column if mapping to a table like `CurrentScan` |
|
||||
| `mapped_to_column_data` | object | no | Static value to map instead of using column data |
|
||||
| `css_classes` | string | no | Bootstrap CSS classes for width/spacing (e.g., `"col-sm-2"`, `"col-sm-6"`) |
|
||||
| `show` | boolean | **YES** | Whether to display in UI (must be `true` to appear) |
|
||||
| `type` | string | **YES** | How to render the value (see [Render Types](#render-types)) |
|
||||
| `default_value` | varies | **YES** | Default if column is empty |
|
||||
| `options` | array | no | Options for `select`/`threshold`/`replace`/`regex` types |
|
||||
| `options_params` | array | no | Dynamic options from SQL or settings |
|
||||
| `localized` | array | **YES** | Which properties need translations (e.g., `["name", "description"]`) |
|
||||
| `name` | array | **YES** | Display name in UI (localized strings) |
|
||||
| `description` | array | no | Help text in UI (localized strings) |
|
||||
| `maxLength` | number | no | Character limit for input fields |
|
||||
|
||||
## Render Types
|
||||
|
||||
### Display-Only Types
|
||||
|
||||
These render as read-only display elements:
|
||||
|
||||
#### `label`
|
||||
Plain text display (read-only).
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Status"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Output:** `online`
|
||||
|
||||
---
|
||||
|
||||
#### `device_mac`
|
||||
Renders as a clickable link to the device with the given MAC address.
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "ForeignKey",
|
||||
"show": true,
|
||||
"type": "device_mac",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Device"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Input:** `aa:bb:cc:dd:ee:ff`
|
||||
**Output:** Clickable link to device details page
|
||||
|
||||
---
|
||||
|
||||
#### `device_ip`
|
||||
Resolves an IP address to a MAC address and creates a device link.
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Object_SecondaryID",
|
||||
"show": true,
|
||||
"type": "device_ip",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Host"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Input:** `192.168.1.100`
|
||||
**Output:** Link to device with that IP (if known)
|
||||
|
||||
---
|
||||
|
||||
#### `device_name_mac`
|
||||
Creates a device link with the target device's name as the link label.
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Object_PrimaryID",
|
||||
"show": true,
|
||||
"type": "device_name_mac",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Device Name"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Input:** `aa:bb:cc:dd:ee:ff`
|
||||
**Output:** Device name (clickable link to device)
|
||||
|
||||
---
|
||||
|
||||
#### `url`
|
||||
Renders as a clickable HTTP/HTTPS link.
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"show": true,
|
||||
"type": "url",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Endpoint"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Input:** `https://example.com/api`
|
||||
**Output:** Clickable link
|
||||
|
||||
---
|
||||
|
||||
#### `url_http_https`
|
||||
Creates two links (HTTP and HTTPS) as lock icons for the given IP/hostname.
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Object_SecondaryID",
|
||||
"show": true,
|
||||
"type": "url_http_https",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Web Links"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Input:** `192.168.1.50`
|
||||
**Output:** 🔓 HTTP link | 🔒 HTTPS link
|
||||
|
||||
---
|
||||
|
||||
#### `textarea_readonly`
|
||||
Multi-line read-only display with newlines preserved.
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Extra",
|
||||
"show": true,
|
||||
"type": "textarea_readonly",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Details"}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Interactive Types
|
||||
|
||||
#### `textbox_save`
|
||||
User-editable text box that persists changes to the database (typically `UserData` column).
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "UserData",
|
||||
"show": true,
|
||||
"type": "textbox_save",
|
||||
"default_value": "",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Notes"}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Styled/Transformed Types
|
||||
|
||||
#### `label` with `threshold`
|
||||
Color-codes values based on ranges. Useful for status codes, latency, capacity percentages.
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"show": true,
|
||||
"type": "threshold",
|
||||
"options": [
|
||||
{
|
||||
"maximum": 199,
|
||||
"hexColor": "#792D86" // Purple for <199
|
||||
},
|
||||
{
|
||||
"maximum": 299,
|
||||
"hexColor": "#5B862D" // Green for 200-299
|
||||
},
|
||||
{
|
||||
"maximum": 399,
|
||||
"hexColor": "#7D862D" // Orange for 300-399
|
||||
},
|
||||
{
|
||||
"maximum": 499,
|
||||
"hexColor": "#BF6440" // Red-orange for 400-499
|
||||
},
|
||||
{
|
||||
"maximum": 999,
|
||||
"hexColor": "#D33115" // Dark red for 500+
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "HTTP Status"}]
|
||||
}
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- Value `150` → Purple (≤199)
|
||||
- Value `250` → Green (≤299)
|
||||
- Value `350` → Orange (≤399)
|
||||
- Value `450` → Red-orange (≤499)
|
||||
- Value `550` → Dark red (>500)
|
||||
|
||||
---
|
||||
|
||||
#### `replace`
|
||||
Replaces specific values with display strings or HTML.
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value2",
|
||||
"show": true,
|
||||
"type": "replace",
|
||||
"options": [
|
||||
{
|
||||
"equals": "online",
|
||||
"replacement": "<i class='fa-solid fa-circle' style='color: green;'></i> Online"
|
||||
},
|
||||
{
|
||||
"equals": "offline",
|
||||
"replacement": "<i class='fa-solid fa-circle' style='color: red;'></i> Offline"
|
||||
},
|
||||
{
|
||||
"equals": "idle",
|
||||
"replacement": "<i class='fa-solid fa-circle' style='color: yellow;'></i> Idle"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Status"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Output Examples:**
|
||||
- `"online"` → 🟢 Online
|
||||
- `"offline"` → 🔴 Offline
|
||||
- `"idle"` → 🟡 Idle
|
||||
|
||||
---
|
||||
|
||||
#### `regex`
|
||||
Applies a regular expression to extract/transform values.
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"show": true,
|
||||
"type": "regex",
|
||||
"options": [
|
||||
{
|
||||
"type": "regex",
|
||||
"param": "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "IP Address"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Input:** `Host: 192.168.1.100 Port: 8080`
|
||||
**Output:** `192.168.1.100`
|
||||
|
||||
---
|
||||
|
||||
#### `eval`
|
||||
Evaluates JavaScript code with access to the column value (use `${value}` or `{value}`).
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"show": true,
|
||||
"type": "eval",
|
||||
"default_value": "",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Formatted Value"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Example with custom formatting:**
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"show": true,
|
||||
"type": "eval",
|
||||
"options": [
|
||||
{
|
||||
"type": "eval",
|
||||
"param": "`<b>${value}</b> units`"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Value with Units"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Input:** `42`
|
||||
**Output:** **42** units
|
||||
|
||||
---
|
||||
|
||||
### Chaining Types
|
||||
|
||||
You can chain multiple transformations with dot notation:
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value3",
|
||||
"show": true,
|
||||
"type": "regex.url_http_https",
|
||||
"options": [
|
||||
{
|
||||
"type": "regex",
|
||||
"param": "([\\d.:]+)" // Extract IP/host
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "HTTP/S Links"}]
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. Apply regex to extract `192.168.1.50` from input
|
||||
2. Create HTTP/HTTPS links for that host
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Options
|
||||
|
||||
### SQL-Driven Select
|
||||
|
||||
Use SQL query results to populate dropdown options:
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value2",
|
||||
"show": true,
|
||||
"type": "select",
|
||||
"options": ["{value}"],
|
||||
"options_params": [
|
||||
{
|
||||
"name": "value",
|
||||
"type": "sql",
|
||||
"value": "SELECT devType as id, devType as name FROM Devices UNION SELECT 'Unknown' as id, 'Unknown' as name ORDER BY id"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Device Type"}]
|
||||
}
|
||||
```
|
||||
|
||||
The SQL query must return exactly **2 columns:**
|
||||
- **First column (id):** Option value
|
||||
- **Second column (name):** Display label
|
||||
|
||||
---
|
||||
|
||||
### Setting-Driven Select
|
||||
|
||||
Use plugin settings to populate options:
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"show": true,
|
||||
"type": "select",
|
||||
"options": ["{value}"],
|
||||
"options_params": [
|
||||
{
|
||||
"name": "value",
|
||||
"type": "setting",
|
||||
"value": "MYPLN_AVAILABLE_STATUSES"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Status"}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mapping to Database Tables
|
||||
|
||||
### Mapping to `CurrentScan`
|
||||
|
||||
To import plugin data into the device scan pipeline (for notifications, heuristics, etc.):
|
||||
|
||||
1. Add `"mapped_to_table": "CurrentScan"` at the root level of `config.json`
|
||||
2. Add `"mapped_to_column"` property to each column definition
|
||||
|
||||
```json
|
||||
{
|
||||
"code_name": "my_device_scanner",
|
||||
"unique_prefix": "MYSCAN",
|
||||
"mapped_to_table": "CurrentScan",
|
||||
"database_column_definitions": [
|
||||
{
|
||||
"column": "Object_PrimaryID",
|
||||
"mapped_to_column": "cur_MAC",
|
||||
"show": true,
|
||||
"type": "device_mac",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "MAC Address"}]
|
||||
},
|
||||
{
|
||||
"column": "Object_SecondaryID",
|
||||
"mapped_to_column": "cur_IP",
|
||||
"show": true,
|
||||
"type": "device_ip",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "IP Address"}]
|
||||
},
|
||||
{
|
||||
"column": "NameDoesntMatter",
|
||||
"mapped_to_column": "cur_ScanMethod",
|
||||
"mapped_to_column_data": {
|
||||
"value": "MYSCAN"
|
||||
},
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Scan Method"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Using Static Values
|
||||
|
||||
Use `mapped_to_column_data` to map a static value instead of reading from a column:
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "NameDoesntMatter",
|
||||
"mapped_to_column": "cur_ScanMethod",
|
||||
"mapped_to_column_data": {
|
||||
"value": "MYSCAN"
|
||||
},
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Discovery Method"}]
|
||||
}
|
||||
```
|
||||
|
||||
This always sets `cur_ScanMethod` to `"MYSCAN"` regardless of column data.
|
||||
|
||||
---
|
||||
|
||||
## Filters
|
||||
|
||||
Control which rows are displayed based on filter conditions. Filters are applied on the client-side in JavaScript.
|
||||
|
||||
```json
|
||||
{
|
||||
"data_filters": [
|
||||
{
|
||||
"compare_column": "Object_PrimaryID",
|
||||
"compare_operator": "==",
|
||||
"compare_field_id": "txtMacFilter",
|
||||
"compare_js_template": "'{value}'.toString()",
|
||||
"compare_use_quotes": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `compare_column` | The column from plugin results to compare (left side) |
|
||||
| `compare_operator` | JavaScript operator: `==`, `!=`, `<`, `>`, `<=`, `>=`, `includes`, `startsWith` |
|
||||
| `compare_field_id` | HTML input field ID containing the filter value (right side) |
|
||||
| `compare_js_template` | JavaScript template to transform values. Use `{value}` placeholder |
|
||||
| `compare_use_quotes` | If `true`, wrap result in quotes for string comparison |
|
||||
|
||||
**Example: Filter by MAC address**
|
||||
|
||||
```json
|
||||
{
|
||||
"data_filters": [
|
||||
{
|
||||
"compare_column": "ForeignKey",
|
||||
"compare_operator": "==",
|
||||
"compare_field_id": "txtMacFilter",
|
||||
"compare_js_template": "'{value}'.toString()",
|
||||
"compare_use_quotes": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When viewing a device detail page, the `txtMacFilter` field is populated with that device's MAC, and only rows where `ForeignKey == MAC` are shown.
|
||||
|
||||
---
|
||||
|
||||
## Example: Complete Column Definitions
|
||||
|
||||
```json
|
||||
{
|
||||
"database_column_definitions": [
|
||||
{
|
||||
"column": "Object_PrimaryID",
|
||||
"mapped_to_column": "cur_MAC",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "device_mac",
|
||||
"default_value": "",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "MAC Address"}]
|
||||
},
|
||||
{
|
||||
"column": "Object_SecondaryID",
|
||||
"mapped_to_column": "cur_IP",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "device_ip",
|
||||
"default_value": "unknown",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "IP Address"}]
|
||||
},
|
||||
{
|
||||
"column": "DateTime",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Last Seen"}]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "threshold",
|
||||
"options": [
|
||||
{"maximum": 199, "hexColor": "#792D86"},
|
||||
{"maximum": 299, "hexColor": "#5B862D"},
|
||||
{"maximum": 399, "hexColor": "#7D862D"},
|
||||
{"maximum": 499, "hexColor": "#BF6440"},
|
||||
{"maximum": 999, "hexColor": "#D33115"}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "HTTP Status"}]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value2",
|
||||
"css_classes": "col-sm-1",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value": "",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Response Time"}]
|
||||
},
|
||||
{
|
||||
"column": "Extra",
|
||||
"css_classes": "col-sm-3",
|
||||
"show": true,
|
||||
"type": "textarea_readonly",
|
||||
"default_value": "",
|
||||
"localized": ["name"],
|
||||
"name": [{"language_code": "en_us", "string": "Additional Info"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CSS Classes
|
||||
|
||||
Use Bootstrap grid classes to control column widths in tables:
|
||||
|
||||
| Class | Width | Usage |
|
||||
|-------|-------|-------|
|
||||
| `col-sm-1` | ~8% | Very narrow (icons, status) |
|
||||
| `col-sm-2` | ~16% | Narrow (MACs, IPs) |
|
||||
| `col-sm-3` | ~25% | Medium (names, URLs) |
|
||||
| `col-sm-4` | ~33% | Medium-wide (descriptions) |
|
||||
| `col-sm-6` | ~50% | Wide (large content) |
|
||||
|
||||
---
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
- [ ] All columns have `"show": true` or `false`
|
||||
- [ ] Display columns with `"type"` specified from supported types
|
||||
- [ ] Localized strings include at least `en_us`
|
||||
- [ ] `mapped_to_column` matches target table schema (if using mapping)
|
||||
- [ ] Options/thresholds have correct structure
|
||||
- [ ] CSS classes are valid Bootstrap grid classes
|
||||
- [ ] Chaining types (e.g., `regex.url_http_https`) are supported combinations
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Plugin Data Contract](PLUGINS_DEV_DATA_CONTRACT.md) - What data fields are available
|
||||
- [Plugin Settings System](PLUGINS_DEV_SETTINGS.md) - Configure user input
|
||||
- [Database Mapping](PLUGINS_DEV.md#-mapping-the-plugin-results-into-a-database-table) - Map data to core tables
|
||||
- [Debugging Plugins](DEBUG_PLUGINS.md) - Troubleshoot display issues
|
||||
Reference in New Issue
Block a user