Filter plugin prep
This commit is contained in:
@@ -13,7 +13,7 @@
|
|||||||
| Devices | The main devices database that also contains the Network tree mappings. If `ScanCycle` is set to `0` device is not scanned. | ![Screen2][screen2] |
|
| Devices | The main devices database that also contains the Network tree mappings. If `ScanCycle` is set to `0` device is not scanned. | ![Screen2][screen2] |
|
||||||
| DHCP_Leases | Used for importing devices from DHCP_Leases files. Also leveraged by some plugins. | ![Screen3][screen3] |
|
| DHCP_Leases | Used for importing devices from DHCP_Leases files. Also leveraged by some plugins. | ![Screen3][screen3] |
|
||||||
| Events | Used to collect connection/disconnection events. | ![Screen4][screen4] |
|
| Events | Used to collect connection/disconnection events. | ![Screen4][screen4] |
|
||||||
| Nmap_Scan | Contains results of the scheduled Nmap scan, taht is also displayed in the Nmap tab on each device. | ![Screen5][screen5] |
|
| Nmap_Scan | Contains results of the scheduled Nmap scan, that is also displayed in the Nmap tab on each device. | ![Screen5][screen5] |
|
||||||
| Online_History | Used to display the `Device presence over time` chart | ![Screen6][screen6] |
|
| Online_History | Used to display the `Device presence over time` chart | ![Screen6][screen6] |
|
||||||
| Parameters | Used to pass values between the frontend and backend. | ![Screen7][screen7] |
|
| Parameters | Used to pass values between the frontend and backend. | ![Screen7][screen7] |
|
||||||
| Pholus_Scan | Scan results of the Pholus python network penetration script. | ![Screen8][screen8] |
|
| Pholus_Scan | Scan results of the Pholus python network penetration script. | ![Screen8][screen8] |
|
||||||
|
|||||||
@@ -852,6 +852,7 @@ function main () {
|
|||||||
var urlParams = new URLSearchParams(window.location.search);
|
var urlParams = new URLSearchParams(window.location.search);
|
||||||
if (urlParams.has ('mac') == true) {
|
if (urlParams.has ('mac') == true) {
|
||||||
mac = urlParams.get ('mac');
|
mac = urlParams.get ('mac');
|
||||||
|
setCache("piaDeviceDetailsMac", mac); // set cookie
|
||||||
} else {
|
} else {
|
||||||
$('#pageTitle').html ('Device not found');
|
$('#pageTitle').html ('Device not found');
|
||||||
}
|
}
|
||||||
@@ -1544,7 +1545,10 @@ function performSwitch(direction)
|
|||||||
|
|
||||||
// get new mac from the devicesList. Don't change to the commented out line below, the mac query string in the URL isn't updated yet!
|
// get new mac from the devicesList. Don't change to the commented out line below, the mac query string in the URL isn't updated yet!
|
||||||
// mac = params.mac;
|
// mac = params.mac;
|
||||||
|
|
||||||
mac = devicesList[pos].mac.toString();
|
mac = devicesList[pos].mac.toString();
|
||||||
|
|
||||||
|
setCache("piaDeviceDetailsMac", mac);
|
||||||
|
|
||||||
getDeviceData (true);
|
getDeviceData (true);
|
||||||
|
|
||||||
|
|||||||
@@ -2,25 +2,54 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Main content ---------------------------------------------------------- -->
|
<!-- Main content ---------------------------------------------------------- -->
|
||||||
<section class="content">
|
<section class="content">
|
||||||
<div class="nav-tabs-custom plugin-content" style="margin-bottom: 0px;">
|
<div>
|
||||||
<ul id="tabs-location" class="nav nav-tabs">
|
<div class="input-group">
|
||||||
<!-- PLACEHOLDER -->
|
<input class="form-control" id="txtMacFilter" type="text" value="--">
|
||||||
</ul>
|
</div>
|
||||||
<div id="tabs-content-location" class="tab-content">
|
</div>
|
||||||
<!-- PLACEHOLDER -->
|
<div class="nav-tabs-custom plugin-content" style="margin-bottom: 0px;">
|
||||||
</div>
|
<ul id="tabs-location" class="nav nav-tabs">
|
||||||
|
<!-- PLACEHOLDER -->
|
||||||
</section>
|
</ul>
|
||||||
|
<div id="tabs-content-location" class="tab-content">
|
||||||
|
<!-- PLACEHOLDER -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<script defer>
|
<script defer>
|
||||||
|
|
||||||
var urlParams = new URLSearchParams(window.location.search);
|
// -----------------------------------------------------------------------------
|
||||||
mac = urlParams.get ('mac');
|
// Initializes fields based on current MAC
|
||||||
|
function initFields() {
|
||||||
|
|
||||||
console.log(mac)
|
var urlParams = new URLSearchParams(window.location.search);
|
||||||
|
mac = urlParams.get ('mac');
|
||||||
|
|
||||||
|
// if the current mac has changed, reinitialize the data
|
||||||
|
if(mac != undefined && $("#txtMacFilter").val() != mac)
|
||||||
|
{
|
||||||
|
$("#txtMacFilter").val(mac);
|
||||||
|
console.log("UPDATE");
|
||||||
|
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Checking if current MAC has changed and triggering an updated if needed
|
||||||
|
function updater() {
|
||||||
|
|
||||||
|
initFields()
|
||||||
|
|
||||||
|
// loop
|
||||||
|
setTimeout(function() {
|
||||||
|
updater();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Get form control according to the column definition from config.json > database_column_definitions
|
// Get form control according to the column definition from config.json > database_column_definitions
|
||||||
@@ -80,7 +109,7 @@ function getFormControl(dbColumnDef, value, index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Update the coresponding DB column and entry
|
// Update the corresponding DB column and entry
|
||||||
function saveData (id) {
|
function saveData (id) {
|
||||||
columnName = $(`#${id}`).attr('data-my-column')
|
columnName = $(`#${id}`).attr('data-my-column')
|
||||||
index = $(`#${id}`).attr('data-my-index')
|
index = $(`#${id}`).attr('data-my-index')
|
||||||
@@ -175,23 +204,31 @@ function generateTabs()
|
|||||||
{
|
{
|
||||||
activetab = 'active'
|
activetab = 'active'
|
||||||
|
|
||||||
$.each(pluginDefinitions, function(index, obj) {
|
// clear previous headers data
|
||||||
|
$('#tabs-location').html("");
|
||||||
|
// clear previous content data
|
||||||
|
$('#tabs-content-location').html("");
|
||||||
|
|
||||||
// console.log(obj)
|
$.each(pluginDefinitions, function(index, pluginObj) {
|
||||||
|
|
||||||
$('#tabs-location').append(
|
// console.log(pluginObj)
|
||||||
`<li class=" ${activetab}">
|
|
||||||
<a href="#${obj.unique_prefix}" data-plugin-prefix="${obj.unique_prefix}" id="${obj.unique_prefix}_id" data-toggle="tab" >
|
if(pluginObj.data_source != "template") // hiding template-based plugins as they don't produce any output
|
||||||
${localize(obj, 'icon')} ${localize(obj, 'display_name')}
|
{
|
||||||
</a>
|
$('#tabs-location').append(
|
||||||
</li>`
|
`<li class=" ${activetab}">
|
||||||
);
|
<a href="#${pluginObj.unique_prefix}" data-plugin-prefix="${pluginObj.unique_prefix}" id="${pluginObj.unique_prefix}_id" data-toggle="tab" >
|
||||||
activetab = '' // only first tab is active
|
${localize(pluginObj, 'icon')} ${localize(pluginObj, 'display_name')}
|
||||||
|
</a>
|
||||||
|
</li>`
|
||||||
|
);
|
||||||
|
activetab = '' // only first tab is active
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
activetab = 'active'
|
activetab = 'active'
|
||||||
|
|
||||||
$.each(pluginDefinitions, function(index, obj) {
|
$.each(pluginDefinitions, function(index, pluginObj) {
|
||||||
|
|
||||||
headersHtml = ""
|
headersHtml = ""
|
||||||
colDefinitions = []
|
colDefinitions = []
|
||||||
@@ -200,7 +237,7 @@ function generateTabs()
|
|||||||
hiRows = ""
|
hiRows = ""
|
||||||
|
|
||||||
// Generate the header
|
// Generate the header
|
||||||
$.each(obj["database_column_definitions"], function(index, colDef){
|
$.each(pluginObj["database_column_definitions"], function(index, colDef){
|
||||||
if(colDef.show == true) // select only the ones to show
|
if(colDef.show == true) // select only the ones to show
|
||||||
{
|
{
|
||||||
colDefinitions.push(colDef)
|
colDefinitions.push(colDef)
|
||||||
@@ -212,7 +249,7 @@ function generateTabs()
|
|||||||
var eveCount = 0;
|
var eveCount = 0;
|
||||||
for(i=0;i<pluginUnprocessedEvents.length;i++)
|
for(i=0;i<pluginUnprocessedEvents.length;i++)
|
||||||
{
|
{
|
||||||
if(pluginUnprocessedEvents[i].Plugin == obj.unique_prefix)
|
if(pluginUnprocessedEvents[i].Plugin == pluginObj.unique_prefix)
|
||||||
{
|
{
|
||||||
clm = ""
|
clm = ""
|
||||||
|
|
||||||
@@ -231,7 +268,7 @@ function generateTabs()
|
|||||||
|
|
||||||
for(i=pluginHistory.length-1;i >= 0;i--) // from latest to the oldest
|
for(i=pluginHistory.length-1;i >= 0;i--) // from latest to the oldest
|
||||||
{
|
{
|
||||||
if(pluginHistory[i].Plugin == obj.unique_prefix)
|
if(pluginHistory[i].Plugin == pluginObj.unique_prefix)
|
||||||
{
|
{
|
||||||
if(histCount < 50) // only display 50 entries to optimize performance
|
if(histCount < 50) // only display 50 entries to optimize performance
|
||||||
{
|
{
|
||||||
@@ -253,27 +290,31 @@ function generateTabs()
|
|||||||
var obCount = 0;
|
var obCount = 0;
|
||||||
for(var i=0;i<pluginObjects.length;i++)
|
for(var i=0;i<pluginObjects.length;i++)
|
||||||
{
|
{
|
||||||
if(pluginObjects[i].Plugin == obj.unique_prefix)
|
if(pluginObjects[i].Plugin == pluginObj.unique_prefix)
|
||||||
{
|
{
|
||||||
clm = ""
|
if(shouldBeShown(pluginObjects[i], pluginObj)) // filter TODO
|
||||||
|
{
|
||||||
|
clm = ""
|
||||||
|
|
||||||
for(var j=0;j<colDefinitions.length;j++)
|
for(var j=0;j<colDefinitions.length;j++)
|
||||||
{
|
{
|
||||||
clm += '<td>'+ getFormControl(colDefinitions[j], pluginObjects[i][colDefinitions[j].column], pluginObjects[i]["Index"]) +'</td>'
|
clm += '<td>'+ getFormControl(colDefinitions[j], pluginObjects[i][colDefinitions[j].column], pluginObjects[i]["Index"]) +'</td>'
|
||||||
}
|
}
|
||||||
obRows += `<tr data-my-index="${pluginObjects[i]["Index"]}" >${clm}</tr>`
|
obRows += `<tr data-my-index="${pluginObjects[i]["Index"]}" >${clm}</tr>`
|
||||||
obCount++;
|
obCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate the HTML
|
||||||
|
|
||||||
$('#tabs-content-location').append(
|
$('#tabs-content-location').append(
|
||||||
`
|
`
|
||||||
<div id="${obj.unique_prefix}" class="tab-pane ${activetab}">
|
<div id="${pluginObj.unique_prefix}" class="tab-pane ${activetab}">
|
||||||
<div class="nav-tabs-custom" style="margin-bottom: 0px">
|
<div class="nav-tabs-custom" style="margin-bottom: 0px">
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
<li class="active" >
|
<li class="active" >
|
||||||
<a href="#objectsTarget_${obj.unique_prefix}" data-toggle="tab" >
|
<a href="#objectsTarget_${pluginObj.unique_prefix}" data-toggle="tab" >
|
||||||
|
|
||||||
<i class="fa fa-cube"></i> <?= lang('Plugins_Objects');?> (${obCount})
|
<i class="fa fa-cube"></i> <?= lang('Plugins_Objects');?> (${obCount})
|
||||||
|
|
||||||
@@ -281,7 +322,7 @@ function generateTabs()
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li >
|
<li >
|
||||||
<a href="#eventsTarget_${obj.unique_prefix}" data-toggle="tab" >
|
<a href="#eventsTarget_${pluginObj.unique_prefix}" data-toggle="tab" >
|
||||||
|
|
||||||
<i class="fa fa-bolt"></i> <?= lang('Plugins_Unprocessed_Events');?> (${eveCount})
|
<i class="fa fa-bolt"></i> <?= lang('Plugins_Unprocessed_Events');?> (${eveCount})
|
||||||
|
|
||||||
@@ -289,7 +330,7 @@ function generateTabs()
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li >
|
<li >
|
||||||
<a href="#historyTarget_${obj.unique_prefix}" data-toggle="tab" >
|
<a href="#historyTarget_${pluginObj.unique_prefix}" data-toggle="tab" >
|
||||||
|
|
||||||
<i class="fa fa-clock"></i> <?= lang('Plugins_History');?> (${histCountDisplayed} out of ${histCount} )
|
<i class="fa fa-clock"></i> <?= lang('Plugins_History');?> (${histCountDisplayed} out of ${histCount} )
|
||||||
|
|
||||||
@@ -302,7 +343,7 @@ function generateTabs()
|
|||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
|
||||||
<div id="objectsTarget_${obj.unique_prefix}" class="tab-pane ${activetab}">
|
<div id="objectsTarget_${pluginObj.unique_prefix}" class="tab-pane ${activetab}">
|
||||||
<table class="table table-striped" data-my-dbtable="Plugins_Objects">
|
<table class="table table-striped" data-my-dbtable="Plugins_Objects">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -312,10 +353,10 @@ function generateTabs()
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="plugin-obj-purge">
|
<div class="plugin-obj-purge">
|
||||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Objects' )"><?= lang('Gen_DeleteAll');?></button>
|
<button class="btn btn-primary" onclick="purgeAll('${pluginObj.unique_prefix}', 'Plugins_Objects' )"><?= lang('Gen_DeleteAll');?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="eventsTarget_${obj.unique_prefix}" class="tab-pane">
|
<div id="eventsTarget_${pluginObj.unique_prefix}" class="tab-pane">
|
||||||
<table class="table table-striped" data-my-dbtable="Plugins_Events">
|
<table class="table table-striped" data-my-dbtable="Plugins_Events">
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -326,10 +367,10 @@ function generateTabs()
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="plugin-obj-purge">
|
<div class="plugin-obj-purge">
|
||||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Events' )"><?= lang('Gen_DeleteAll');?></button>
|
<button class="btn btn-primary" onclick="purgeAll('${pluginObj.unique_prefix}', 'Plugins_Events' )"><?= lang('Gen_DeleteAll');?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="historyTarget_${obj.unique_prefix}" class="tab-pane">
|
<div id="historyTarget_${pluginObj.unique_prefix}" class="tab-pane">
|
||||||
<table class="table table-striped" data-my-dbtable="Plugins_History">
|
<table class="table table-striped" data-my-dbtable="Plugins_History">
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -340,7 +381,7 @@ function generateTabs()
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="plugin-obj-purge">
|
<div class="plugin-obj-purge">
|
||||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_History' )"><?= lang('Gen_DeleteAll');?></button>
|
<button class="btn btn-primary" onclick="purgeAll('${pluginObj.unique_prefix}', 'Plugins_History' )"><?= lang('Gen_DeleteAll');?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -349,10 +390,10 @@ function generateTabs()
|
|||||||
|
|
||||||
<div class='plugins-description'>
|
<div class='plugins-description'>
|
||||||
|
|
||||||
${localize(obj, 'description')}
|
${localize(pluginObj, 'description')}
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
<a href="https://github.com/jokob-sk/Pi.Alert/tree/main/pialert/plugins/${obj.code_name}" target="_blank"><?= lang('Gen_ReadDocs');?></a>
|
<a href="https://github.com/jokob-sk/Pi.Alert/tree/main/pialert/plugins/${pluginObj.code_name}" target="_blank"><?= lang('Gen_ReadDocs');?></a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -366,6 +407,7 @@ function generateTabs()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
// Handle active / selected tabs
|
||||||
// handle first tab (objectsTarget_) display
|
// handle first tab (objectsTarget_) display
|
||||||
function initTabs()
|
function initTabs()
|
||||||
{
|
{
|
||||||
@@ -404,6 +446,14 @@ function initTabs()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
// Data cleanup/purge functionality
|
||||||
|
function shouldBeShown(entry, pluginObj)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Data cleanup/purge functionality
|
||||||
plugPrefix = ''
|
plugPrefix = ''
|
||||||
dbTable = ''
|
dbTable = ''
|
||||||
|
|
||||||
@@ -446,7 +496,9 @@ function purgeVisible() {
|
|||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
// Main sequence
|
||||||
|
|
||||||
getData()
|
getData()
|
||||||
|
updater()
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -58,9 +58,6 @@ def importConfigs (db):
|
|||||||
return
|
return
|
||||||
|
|
||||||
conf.lastImportedConfFile = os.path.getmtime(config_file)
|
conf.lastImportedConfFile = os.path.getmtime(config_file)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mylog('debug', ['[Import Config] importing config file'])
|
mylog('debug', ['[Import Config] importing config file'])
|
||||||
conf.mySettings = [] # reset settings
|
conf.mySettings = [] # reset settings
|
||||||
@@ -86,14 +83,6 @@ def importConfigs (db):
|
|||||||
conf.DAYS_TO_KEEP_EVENTS = ccd('DAYS_TO_KEEP_EVENTS', 90 , c_d, 'Delete events days', 'integer', '', 'General')
|
conf.DAYS_TO_KEEP_EVENTS = ccd('DAYS_TO_KEEP_EVENTS', 90 , c_d, 'Delete events days', 'integer', '', 'General')
|
||||||
conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', 'integer', "0", 'General')
|
conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', 'integer', "0", 'General')
|
||||||
|
|
||||||
# # New device defaults
|
|
||||||
# conf.NEWDEV_SCAN = ccd('NEWDEV_SCAN', 1 , c_d, 'Scan Device', 'integer.select', "['0', '1']", 'NewDeviceDefaults')
|
|
||||||
# conf.NEWDEV_ALERT_ALL = ccd('NEWDEV_ALERT_ALL', 0 , c_d, 'Alert All Events', 'integer.select', "['0', '1']", 'NewDeviceDefaults')
|
|
||||||
# conf.NEWDEV_ALERT_DWN = ccd('NEWDEV_ALERT_DWN', 0 , c_d, 'Alert Down', 'integer.select', "['0', '1']", 'NewDeviceDefaults')
|
|
||||||
# conf.NEWDEV_NEWDEV = ccd('NEWDEV_NEWDEV', 1 , c_d, 'New Device', 'integer.select', "['0', '1']", 'NewDeviceDefaults')
|
|
||||||
# conf.NEWDEV_ARCHIVED = ccd('NEWDEV_ARCHIVED', 0 , c_d, 'Archived', 'integer.select', "['0', '1']", 'NewDeviceDefaults')
|
|
||||||
# conf.NEWDEV_SKIPNTF = ccd('NEWDEV_SKIPNTF', 0 , c_d, 'Skip repeated notifications for', 'integer.select', "['0', '1', '8', '24', '168']", 'NewDeviceDefaults')
|
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
conf.REPORT_MAIL = ccd('REPORT_MAIL', False , c_d, 'Enable email', 'boolean', '', 'Email', ['test'])
|
conf.REPORT_MAIL = ccd('REPORT_MAIL', False , c_d, 'Enable email', 'boolean', '', 'Email', ['test'])
|
||||||
conf.SMTP_SERVER = ccd('SMTP_SERVER', '' , c_d,'SMTP server URL', 'text', '', 'Email')
|
conf.SMTP_SERVER = ccd('SMTP_SERVER', '' , c_d,'SMTP server URL', 'text', '', 'Email')
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ A simple template-based plugin for new devices. You can change the default value
|
|||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- This plugin generates editable settings taht are then used in the `device.py` script to initialize new values.
|
- This plugin generates editable settings that are then used in the `device.py` script to initialize new values.
|
||||||
@@ -3,6 +3,13 @@
|
|||||||
"unique_prefix": "NMAPSRV",
|
"unique_prefix": "NMAPSRV",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"data_source": "pialert-db-query",
|
"data_source": "pialert-db-query",
|
||||||
|
"data_filters": [
|
||||||
|
{
|
||||||
|
"column" : "Object_PrimaryID",
|
||||||
|
"function" : "==",
|
||||||
|
"filter_field_id": "txtMacFilter"
|
||||||
|
}
|
||||||
|
],
|
||||||
"localized": ["display_name", "description", "icon"],
|
"localized": ["display_name", "description", "icon"],
|
||||||
"display_name" : [{
|
"display_name" : [{
|
||||||
"language_code":"en_us",
|
"language_code":"en_us",
|
||||||
|
|||||||
Reference in New Issue
Block a user