i'm using table_row_expansion.js file whose source code is written below: please give me your input here.
require([
'splunkjs/mvc/tableview',
'splunkjs/mvc/chartview',
'splunkjs/mvc/dataview',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/postprocessmanager',
'splunkjs/mvc',
'underscore',
'splunkjs/mvc/simplexml/ready!'],function(
TableView,
ChartView,
DataView,
SearchManager,
PostProcessManager,
mvc,
_
){
var EventSearchBasedRowExpansionRenderer = TableView.BaseRowExpansionRenderer.extend({
initialize: function(args) {
// initialize will run once, so we will set up a search and a chart to be reused.
this._searchManager = new SearchManager({
id: 'details-search-manager',
preview: false
});
this._baseSearchManager = new SearchManager({
id: 'base-search-manager',
search: '| loadjob savedsearch="debie2scmi:duerr_it_vuln_management:saved-plugin_outputv2"',
preview: false
});
this._postproc_pluginOutput = new PostProcessManager({
id: 'postproc-plugin-output',
managerid: 'base-search-manager',
});
this._tableView = new TableView({
managerid: 'details-search-manager',
'charting.legend.placement': 'none'
});
this._plugintableView = new TableView({
managerid: 'postproc-plugin-output',
'charting.legend.placement': 'none'
});
//this._dataView = new DataView({
// managerid: 'details-search-manager',
// template: "<b>Solution:</b> <p><%= results[0].solution %></p>"
//});
},
canRender: function(rowData) {
// Since more than one row expansion renderer can be registered we let each decide if they can handle that
// data
// Here we will always handle it.
return true;
},
render: function($container, rowData) {
// rowData contains information about the row that is expanded. We can see the cells, fields, and values
// We will find the sourcetype cell to use its value
var findingCell = _(rowData.cells).find(function (cell) {
return cell.field === 'finding';
});
// get plugin and ip
var result = findingCell.value.split("#");
//update the searches
this._searchManager.set({ search: '| inputlookup nessus_plugin_solution.csv | search id=' + result[1] + ' | fields solution | append [| makeresults | eval solution="No data available." | fields - _time ] | head 1'});
// the rex is a workaround for splunk not implementing linebreaks in the details tables correctly, only mv fields seem to work
this._postproc_pluginOutput.set({search: '| search pokey="' + result[3] + '" | fields plugin_output | rex mode=sed field=plugin_output "s/(\\n)/\\1 #BREAK#/g" | makemv delim="#BREAK#" plugin_output | append [| makeresults | eval plugin_output="No data available." | fields - _time ] | head 1' });
// $container is the jquery object where we can put out content.
// In this case we will render our chart and add it to the $container
$container.append(this._tableView.render().el);
$container.append(this._plugintableView.render().el);
// $container.append("IP: " + result[0] + " Plugin: " + result[1] );
}
});
var CustomLinkRenderer = TableView.BaseCellRenderer.extend({
canRender: function(cell) {
return cell.field === 'solved';
},
render: function($td, cell) {
var solved = cell.value;
var solved_result = solved.split("-");
//var a = $('<a>').attr("href", "www.test.de").text("testlink");
//var a = $('<input>').attr('type','checkbox');
//var a = $('<div>').attr({"id":"chk-sourcetype"+cell.value,"value":cell.value}).attr('class','icon-minus-circle');
var a = $('<div>').attr({"id":"chk-sourcetype"+cell.value,"value":cell.value});
// check if marked as solved
if(solved_result[1] == 1)
{
a.attr('class','icon-check-circle');
}
else
{
a.attr('class','icon-minus-circle');
}
$td.empty().append(a);
a.click(function(e) {
e.preventDefault();
//window.location = $(e.currentTarget).attr('href');
// or for popup:
// window.open($(e.currentTarget).attr('href'));
if($(e.currentTarget).attr('class') == 'icon-minus-circle')
{
$(e.currentTarget).attr('class','icon-gear');
var updatestring = '| inputlookup lkp-all-findings | eval key=_key | where key="' + solved_result[2] +'" | eval lastchecked=1 | outputlookup append=t lkp-all-findings';
var kvupdate = new SearchManager({
preview: false
});
kvupdate.set({search: updatestring});
kvupdate.on('search:done', function(properties) {
//console.log("DONE!\nSearch job properties:", properties.content);
// Set new value when search is done
$(e.currentTarget).attr('class','icon-check-circle');
});
}
else
{
$(e.currentTarget).attr('class','icon-gear');
var updatestring = '| inputlookup lkp-all-findings | eval key=_key | where key="' + solved_result[2] +'" | eval lastchecked=0 | outputlookup append=t lkp-all-findings';
var kvupdate = new SearchManager({
preview: false
});
kvupdate.set({search: updatestring});
kvupdate.on('search:done', function(properties) {
//console.log("DONE!\nSearch job properties:", properties.content);
// Set new value when search is done
$(e.currentTarget).attr('class','icon-minus-circle');
});
}
});
}
});
var tableElement = mvc.Components.getInstance("reports_table");
tableElement.getVisualization(function(tableView) {
// Add custom cell renderer, the table will re-render automatically.
tableView.addRowExpansionRenderer(new EventSearchBasedRowExpansionRenderer());
tableView.table.addCellRenderer(new CustomLinkRenderer());
// Force the table to re-render
tableView.table.render();
});
});
@splunk @Anonymous @techiesid @kamlesh_vaghela please help.
Yeah. That might be delayed at JS level. Can you please update JS and try?
render: function($container, rowData) {
//Add below line for cleaning previous content
$container.html("");
thanks
Can you please share your sample XML and JS in below format? You can find "Insert/Edit Code Sample" button in toolbar.
This is sample code
@kamlesh_vaghela please find the code.
1) XML code
<form script="table_row_expansion.js" theme="dark">
<label>Scan reports v3</label>
<fieldset submitButton="false" autoRun="true"></fieldset>
<row>
<panel>
<title>Vulnerability report</title>
<input type="checkbox" token="srStatus">
<label>Status</label>
<choice value="1">solved</choice>
<choice value="0">unsolved</choice>
<default>1,0</default>
<prefix>(</prefix>
<suffix>)</suffix>
<initialValue>1,0</initialValue>
<valuePrefix>is_solved=</valuePrefix>
<delimiter> OR </delimiter>
</input>
<input type="checkbox" token="rsSeverity">
<label>Severity</label>
<choice value="critical">critical</choice>
<choice value="high">high</choice>
<choice value="medium">medium</choice>
<choice value="low">low</choice>
<choice value="informational">informational</choice>
<default>critical,high,medium,low,informational</default>
<prefix>(</prefix>
<suffix>)</suffix>
<initialValue>critical,high,medium,low,informational</initialValue>
<valuePrefix>severity=</valuePrefix>
<delimiter> OR </delimiter>
</input>
<input type="dropdown" token="srAsset">
<label>Asset Group</label>
<choice value="*">All</choice>
<default>*</default>
<fieldForLabel>Asset_Gruppe</fieldForLabel>
<fieldForValue>Asset_Gruppe</fieldForValue>
<search>
<query>| inputlookup lkp-asset-list-master | stats count by Asset_Gruppe</query>
</search>
</input>
<input type="dropdown" token="srCcode">
<label>Company Code</label>
<choice value="*">All</choice>
<default>*</default>
<fieldForLabel>Company Code</fieldForLabel>
<fieldForValue>Company Code</fieldForValue>
<search>
<query>| inputlookup lkp-GlobalIPRange | dedup "Company Code"</query>
<earliest>-24h@h</earliest>
<latest>now</latest>
</search>
</input>
<input type="dropdown" token="srCompany">
<label>Homag / Dürr</label>
<choice value="*">All</choice>
<fieldForLabel>Scan-Company</fieldForLabel>
<fieldForValue>Scan-Company</fieldForValue>
<search>
<query>| inputlookup lkp-asset-list-master | eval Scan-Company=substr('Scan-Company',0,1).lower(substr('Scan-Company',2)) | stats count by Scan-Company</query>
</search>
<default>*</default>
</input>
<input type="dropdown" token="srLocation">
<label>Location</label>
<choice value="*">All</choice>
<default>*</default>
<fieldForLabel>Location</fieldForLabel>
<fieldForValue>Location</fieldForValue>
<search>
<query>| inputlookup lkp-asset-list-master | stats count by Location</query>
</search>
</input>
<input type="dropdown" token="srScanner">
<label>Scanner</label>
<choice value="*">All</choice>
<default>*</default>
<fieldForLabel>Scanner</fieldForLabel>
<fieldForValue>Scanner</fieldForValue>
<search>
<query>| inputlookup lkp-asset-list-master | stats count by Scanner</query>
</search>
</input>
<input type="text" token="srHostname">
<label>Hostname</label>
<default>*</default>
</input>
<input type="text" token="srIp">
<label>IP</label>
<default>*</default>
</input>
<input type="text" token="srPluginname">
<label>Plugin name</label>
<default>*</default>
</input>
<input type="text" token="srScanDate">
<label>Scan Date</label>
<default>*</default>
</input>
<table id="reports_table">
<search>
<query>| inputlookup lkp-all-findings
| lookup lkp-findings-blacklist.csv blfinding as finding OUTPUTNEW blfinding
| lookup lkp-asset-list-master "IP Adresse" as ip OUTPUTNEW Asset_Gruppe Scan-Company Scanner Scan-Location Location "DNS Name" as dns_name
| lookup lkp-GlobalIpRange 3-Letter-Code as Location OUTPUTNEW "Company Code"
| eval key=_key,is_solved=if(lastchecked>lastfound OR lastchecked == 1,1,0),solved=finding."-".is_solved."-".key,blacklisted=if(isnull(blfinding),0,1),scandate=strftime(lastfound,"%Y-%m-%d %H:%M:%S"),lastchecked=strftime(lastchecked,"%Y-%m-%d %H:%M:%S")
| fillnull value="N.A." Asset_Gruppe Scan-Company Scanner Scan-Location Location hostname
| search $srStatus$ $rsSeverity$ blacklisted=0 Asset_Gruppe="$srAsset$" Scan-Company="$srCompany$" Location="$srLocation$" Scanner="$srScanner$" dns="$srHostname$" pluginname="$srPluginname$" ip="$srIp$" scandate="*$srScanDate$*" "Company Code"="$srCcode$"
| strcat finding "#" NessusHost sid hostid pluginid finding
| fields dns ip lastchecked severity pluginid pluginname scandate Asset_Gruppe Location Scan-Company "Company Code" Scan-Location Scanner solved finding
| rename dns as Hostname,ip as IP</query>
<earliest>0</earliest>
<latest></latest>
<sampleRatio>1</sampleRatio>
</search>
<option name="count">100</option>
<option name="dataOverlayMode">none</option>
<option name="drilldown">none</option>
<option name="percentagesRow">false</option>
<option name="refresh.display">progressbar</option>
<option name="rowNumbers">false</option>
<option name="totalsRow">false</option>
<option name="wrap">true</option>
<format type="color" field="severity">
<colorPalette type="map">{"critical":#ff0000,"high":#ff631e,"medium":#ffb750,"low":#fffcab,"informational":#8ba6ff}</colorPalette>
<scale type="category"></scale>
</format>
</table>
</panel>
</row>
</form>
2) JS code
require([
'splunkjs/mvc/tableview',
'splunkjs/mvc/chartview',
'splunkjs/mvc/dataview',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/postprocessmanager',
'splunkjs/mvc',
'underscore',
'splunkjs/mvc/simplexml/ready!'],function(
TableView,
ChartView,
DataView,
SearchManager,
PostProcessManager,
mvc,
_
){
var EventSearchBasedRowExpansionRenderer = TableView.BaseRowExpansionRenderer.extend({
initialize: function(args) {
// initialize will run once, so we will set up a search and a chart to be reused.
this._searchManager = new SearchManager({
id: 'details-search-manager',
preview: false
});
this._baseSearchManager = new SearchManager({
id: 'base-search-manager',
search: '| loadjob savedsearch="debie2scmi:duerr_it_vuln_management:saved-plugin_outputv2"',
preview: false
});
this._postproc_pluginOutput = new PostProcessManager({
id: 'postproc-plugin-output',
managerid: 'base-search-manager',
});
this._tableView = new TableView({
managerid: 'details-search-manager',
'charting.legend.placement': 'none'
});
this._plugintableView = new TableView({
managerid: 'postproc-plugin-output',
'charting.legend.placement': 'none'
});
//this._dataView = new DataView({
// managerid: 'details-search-manager',
// template: "<b>Solution:</b> <p><%= results[0].solution %></p>"
//});
},
canRender: function(rowData) {
// Since more than one row expansion renderer can be registered we let each decide if they can handle that
// data
// Here we will always handle it.
return true;
},
render: function($container, rowData) {
// rowData contains information about the row that is expanded. We can see the cells, fields, and values
// We will find the sourcetype cell to use its value
var findingCell = _(rowData.cells).find(function (cell) {
return cell.field === 'finding';
});
// get plugin and ip
var result = findingCell.value.split("#");
//update the searches
this._searchManager.set({ search: '| inputlookup nessus_plugin_solution.csv | search id=' + result[1] + ' | fields solution | append [| makeresults | eval solution="No data available." | fields - _time ] | head 1'});
// the rex is a workaround for splunk not implementing linebreaks in the details tables correctly, only mv fields seem to work
this._postproc_pluginOutput.set({search: '| search pokey="' + result[3] + '" | fields plugin_output | rex mode=sed field=plugin_output "s/(\\n)/\\1 #BREAK#/g" | makemv delim="#BREAK#" plugin_output | append [| makeresults | eval plugin_output="No data available." | fields - _time ] | head 1' });
// $container is the jquery object where we can put out content.
// In this case we will render our chart and add it to the $container
$container.append(this._tableView.render().el);
$container.append(this._plugintableView.render().el);
// $container.append("IP: " + result[0] + " Plugin: " + result[1] );
}
});
var CustomLinkRenderer = TableView.BaseCellRenderer.extend({
canRender: function(cell) {
return cell.field === 'solved';
},
render: function($td, cell) {
var solved = cell.value;
var solved_result = solved.split("-");
//var a = $('<a>').attr("href", "www.test.de").text("testlink");
//var a = $('<input>').attr('type','checkbox');
//var a = $('<div>').attr({"id":"chk-sourcetype"+cell.value,"value":cell.value}).attr('class','icon-minus-circle');
var a = $('<div>').attr({"id":"chk-sourcetype"+cell.value,"value":cell.value});
// check if marked as solved
if(solved_result[1] == 1)
{
a.attr('class','icon-check-circle');
}
else
{
a.attr('class','icon-minus-circle');
}
$td.empty().append(a);
a.click(function(e) {
e.preventDefault();
//window.location = $(e.currentTarget).attr('href');
// or for popup:
// window.open($(e.currentTarget).attr('href'));
if($(e.currentTarget).attr('class') == 'icon-minus-circle')
{
$(e.currentTarget).attr('class','icon-gear');
var updatestring = '| inputlookup lkp-all-findings | eval key=_key | where key="' + solved_result[2] +'" | eval lastchecked=1 | outputlookup append=t lkp-all-findings';
var kvupdate = new SearchManager({
preview: false
});
kvupdate.set({search: updatestring});
kvupdate.on('search:done', function(properties) {
//console.log("DONE!\nSearch job properties:", properties.content);
// Set new value when search is done
$(e.currentTarget).attr('class','icon-check-circle');
});
}
else
{
$(e.currentTarget).attr('class','icon-gear');
var updatestring = '| inputlookup lkp-all-findings | eval key=_key | where key="' + solved_result[2] +'" | eval lastchecked=0 | outputlookup append=t lkp-all-findings';
var kvupdate = new SearchManager({
preview: false
});
kvupdate.set({search: updatestring});
kvupdate.on('search:done', function(properties) {
//console.log("DONE!\nSearch job properties:", properties.content);
// Set new value when search is done
$(e.currentTarget).attr('class','icon-minus-circle');
});
}
});
}
});
var tableElement = mvc.Components.getInstance("reports_table");
tableElement.getVisualization(function(tableView) {
// Add custom cell renderer, the table will re-render automatically.
tableView.addRowExpansionRenderer(new EventSearchBasedRowExpansionRenderer());
tableView.table.addCellRenderer(new CustomLinkRenderer());
// Force the table to re-render
tableView.table.render();
});
});
when I click on > button of any row in my dashboard it shows desired result , now if I click on any other row > button , then the result of first row reflect in the area for some time and automatically reloaded to desired output.
so, I guess there is delay in JS output or may be JS through some cache for time being.
Yeah. That might be delayed at JS level. Can you please update JS and try?
render: function($container, rowData) {
//Add below line for cleaning previous content
$container.html("");
thanks
@kamlesh_vaghela can you please look into this case as well.👇
thank you @kamlesh_vaghela , it works like a charm.😊