Dashboards & Visualizations

Need help in applying checkbox in splunk table and saving the on click user details in new column

spkriyaz
Path Finder


I am trying to replicate a interface where the records are stored in a table. A checkbox will be there in every row of the table, so if I am the user and if I want any of the row to be acknowledged I will click the checkbox of that row and press a submit button. This process indicates that I have acknowledged the error in the particular time.

I want to replicate the same in Splunk. Currently I did a editable Splunk table in a dashboard but we have enter the time, username and save the lookup but I wanted to have a checkbox when it is clicked then i want to save the username, time for that particular row in the last column which will be empty for unacknowledged rows.

For example:
Below is the dashboard which I am trying to replicate in Splunk, the first row in it has been acknowledged so it has the value in the last column "Ack By" which shows who has acknowledged and the time. I wanted to do the similar thing in Splunk by having this table in a dashboard. So whoever clicks the row his/her email/name with clicked time should be updated in the new column "Ack By" and the checkbox should be hidden for the acknowledged row. Could anyone please help with this?

sample dashboard.PNG

Labels (3)
0 Karma
1 Solution

kamlesh_vaghela
SplunkTrust
SplunkTrust
 
As per your requirement and with the same approach I suggested in below answer, I have designed sample code for you.
 
 
 
Note:
1) Here I have used sample_data lookup for having sample data in table. You have to change search query as per your need.
 
2) For making running this dashboard you need to create file based lookup.
- Create a sample_data.csv file in lookups folder.
- Add below stanza in transforms.conf
[sample_data]
filename = sample_data.csv
- Do debug refersh
http://splunkhost:8000/en-US/debug/refresh
- Populate lookup using below search
| makeresults count=10 | eval Number=1 | accum Number | eval SomeFields="This is sample data", Ack="" | table Number SomeFields Ack | outputlookup sample_data
 
3) I have create on more search which will update the lookup on click on "Ack Selected rows" button and start search for main search.
 
4) For implementing this solution, You need to change column name in search and in JS as well.
 
5) You can enhance JS and XML as per your requirements.
 
 
 
Sample Code:
 

multiselect_table_submit.xml

 

 

<dashboard script="multiselect_table_submit.js" stylesheet="multiselect_table.css">
  <label>Multi-Select Table Rows Example</label>
  <search id="mysearch">
    <query> |inputlookup sample_data | where Number IN ($mytoken$) | eval Ack=now() | append [|inputlookup sample_data | where not Number IN ($mytoken$) ]  | sort Number | outputlookup sample_data</query>
    <earliest>-15m</earliest>
    <latest>now</latest>
  </search>
  <row>
    <panel>
      <html>
       <div>
         <input type="button" id="mybutton" value="Ack Selected Rows"/>
       </div>
     </html>
    </panel>
  </row>
  <row>
    <panel>
      <table id="myTable">
        <title>Panel A</title>
        <search id="mainSearch">
          <query>|inputlookup sample_data | eval "Select Number"=Number."|".Ack | table "Select Number" Number SomeFields Ack | eval Ack=strftime(Ack,"%c") | sort Number</query>
        </search>
        <option name="count">10</option>
        <option name="drilldown">row</option>
        <drilldown>
          <condition field="*"></condition>
        </drilldown>
      </table>
    </panel>
  </row>
</dashboard>

 

 

multiselect_table_submit.js

require([
		'underscore',
		'jquery',
		'splunkjs/mvc',
		'splunkjs/mvc/tableview',
		'splunkjs/mvc/simplexml/ready!'
	], function (_, $, mvc, TableView) {
	// Access the "default" token model
	var tokens = mvc.Components.get("default");
	var selected_values_array = [];
	var submittedTokens = mvc.Components.get('submitted');
	console.log("This is Multi-select table JS");
	// Custom renderer for applying checkbox.
	var CustomRenderer = TableView.BaseCellRenderer.extend({
			canRender: function (cell) {
				return _(['Select Number']).contains(cell.field);
			},
			render: function ($td, cell) {
				var cell_value = cell.value.split("|")[0];
				var ack_value = cell.value.split("|")[1];
				var ack_flag = ack_value !== "";
				console.log(cell.value,ack_flag,ack_value);
				var div = (ack_flag ? $('<div>') :$('<div>').attr({
						"id": "chk-number" + cell_value,
						"value": cell_value
					}).addClass('checkbox').click(function () {
						if ($(this).attr('class') === "checkbox") {
							selected_values_array.push($(this).attr('value'));
							$(this).removeClass();
							$(this).addClass("checkbox checked");
						} else {
							$(this).removeClass();
							$(this).addClass("checkbox");
							var i = selected_values_array.indexOf($(this).attr('value'));
							if (i != -1) {
								selected_values_array.splice(i, 1);
							}
						}
					}))
				var b = (ack_flag ? $td.addClass('range-cell').addClass('range-acked') : $td.addClass('range-cell').addClass('range-not-acked'));
				div.appendTo($td);
			}
		});

	//List of table ID
	var sh = mvc.Components.get("myTable");
	if (typeof(sh) != "undefined") {
		sh.getVisualization(function (tableView) {
			
			tableView.on('rendered', function() {
				console.log("Kamlesh 1");
                // Apply class of the cells to the parent row in order to color the whole row
                tableView.$el.find('td.range-cell').each(function() {
                	console.log("Kamlesh 2");
                	$(this).parents('tr').addClass(this.className);
                	console.log(this.className);
                });
                console.log("Kamlesh 3");
            });

			// Add custom cell renderer and force re-render
			tableView.table.addCellRenderer(new CustomRenderer());
			tableView.table.render();
		});
	}

	// Disabling button while search is running
	var mysearch = mvc.Components.get('mysearch');
	var mainSearch = mvc.Components.get('mainSearch');
	
	mysearch.on('search:start', function (properties) {
		$("#mybutton").attr('disabled', true);
	});

	mysearch.on('search:done', function (properties) {
		$("#mybutton").attr('disabled', false);
		mainSearch.startSearch();
	});

	$(document).ready(function () {
		
		//setting up tokens with selected value.
		$("#mybutton").on("click", function (e) {
			e.preventDefault();
			tokens.set("mytoken", selected_values_array.join());
			submittedTokens.set(tokens.toJSON());
			$("#mybutton").attr('disabled', true);
		});
	});
});

 

 

 

multiselect_table.css

/* The standalone checkbox square*/
 .checkbox {
   width:20px;
   height:20px;
   border: 1px solid #000;
   display: inline-block;
 }
 
 /* This is what simulates a checkmark icon */
.checkbox.checked:after {
   content: '';
   display: block;
   width: 4px;
   height: 7px;
   
   /* "Center" the checkmark */
   position:relative;
   top:4px;
   left:7px;
   
   border: solid #000;
   border-width: 0 2px 2px 0;
   transform: rotate(45deg);
 }

#myTable tr.range-acked  td  {
    background-color: #08f540 !important; 
}

#myTable tr.range-not-acked  td {
    background-color: #f00d18 !important;
}

 

 

 

Please check and acknowledge 😛


Thanks

View solution in original post

vivekkumarkk
Explorer

@kamlesh_vaghela . This is super cool. Is it possible to create an exclude csv and donot show the acknowledged data in the table again say for x number of days that a user inputs?

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@vivekkumarkk 


Yes, You can create exclude.csv for acknowledged data. For this you have you add acknowledged record in exclude.csv. You need to update exisintg search which is updtaing sample_data and need to add one more search which will add acknowledged record in exclude.csv. 

 

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust
 
As per your requirement and with the same approach I suggested in below answer, I have designed sample code for you.
 
 
 
Note:
1) Here I have used sample_data lookup for having sample data in table. You have to change search query as per your need.
 
2) For making running this dashboard you need to create file based lookup.
- Create a sample_data.csv file in lookups folder.
- Add below stanza in transforms.conf
[sample_data]
filename = sample_data.csv
- Do debug refersh
http://splunkhost:8000/en-US/debug/refresh
- Populate lookup using below search
| makeresults count=10 | eval Number=1 | accum Number | eval SomeFields="This is sample data", Ack="" | table Number SomeFields Ack | outputlookup sample_data
 
3) I have create on more search which will update the lookup on click on "Ack Selected rows" button and start search for main search.
 
4) For implementing this solution, You need to change column name in search and in JS as well.
 
5) You can enhance JS and XML as per your requirements.
 
 
 
Sample Code:
 

multiselect_table_submit.xml

 

 

<dashboard script="multiselect_table_submit.js" stylesheet="multiselect_table.css">
  <label>Multi-Select Table Rows Example</label>
  <search id="mysearch">
    <query> |inputlookup sample_data | where Number IN ($mytoken$) | eval Ack=now() | append [|inputlookup sample_data | where not Number IN ($mytoken$) ]  | sort Number | outputlookup sample_data</query>
    <earliest>-15m</earliest>
    <latest>now</latest>
  </search>
  <row>
    <panel>
      <html>
       <div>
         <input type="button" id="mybutton" value="Ack Selected Rows"/>
       </div>
     </html>
    </panel>
  </row>
  <row>
    <panel>
      <table id="myTable">
        <title>Panel A</title>
        <search id="mainSearch">
          <query>|inputlookup sample_data | eval "Select Number"=Number."|".Ack | table "Select Number" Number SomeFields Ack | eval Ack=strftime(Ack,"%c") | sort Number</query>
        </search>
        <option name="count">10</option>
        <option name="drilldown">row</option>
        <drilldown>
          <condition field="*"></condition>
        </drilldown>
      </table>
    </panel>
  </row>
</dashboard>

 

 

multiselect_table_submit.js

require([
		'underscore',
		'jquery',
		'splunkjs/mvc',
		'splunkjs/mvc/tableview',
		'splunkjs/mvc/simplexml/ready!'
	], function (_, $, mvc, TableView) {
	// Access the "default" token model
	var tokens = mvc.Components.get("default");
	var selected_values_array = [];
	var submittedTokens = mvc.Components.get('submitted');
	console.log("This is Multi-select table JS");
	// Custom renderer for applying checkbox.
	var CustomRenderer = TableView.BaseCellRenderer.extend({
			canRender: function (cell) {
				return _(['Select Number']).contains(cell.field);
			},
			render: function ($td, cell) {
				var cell_value = cell.value.split("|")[0];
				var ack_value = cell.value.split("|")[1];
				var ack_flag = ack_value !== "";
				console.log(cell.value,ack_flag,ack_value);
				var div = (ack_flag ? $('<div>') :$('<div>').attr({
						"id": "chk-number" + cell_value,
						"value": cell_value
					}).addClass('checkbox').click(function () {
						if ($(this).attr('class') === "checkbox") {
							selected_values_array.push($(this).attr('value'));
							$(this).removeClass();
							$(this).addClass("checkbox checked");
						} else {
							$(this).removeClass();
							$(this).addClass("checkbox");
							var i = selected_values_array.indexOf($(this).attr('value'));
							if (i != -1) {
								selected_values_array.splice(i, 1);
							}
						}
					}))
				var b = (ack_flag ? $td.addClass('range-cell').addClass('range-acked') : $td.addClass('range-cell').addClass('range-not-acked'));
				div.appendTo($td);
			}
		});

	//List of table ID
	var sh = mvc.Components.get("myTable");
	if (typeof(sh) != "undefined") {
		sh.getVisualization(function (tableView) {
			
			tableView.on('rendered', function() {
				console.log("Kamlesh 1");
                // Apply class of the cells to the parent row in order to color the whole row
                tableView.$el.find('td.range-cell').each(function() {
                	console.log("Kamlesh 2");
                	$(this).parents('tr').addClass(this.className);
                	console.log(this.className);
                });
                console.log("Kamlesh 3");
            });

			// Add custom cell renderer and force re-render
			tableView.table.addCellRenderer(new CustomRenderer());
			tableView.table.render();
		});
	}

	// Disabling button while search is running
	var mysearch = mvc.Components.get('mysearch');
	var mainSearch = mvc.Components.get('mainSearch');
	
	mysearch.on('search:start', function (properties) {
		$("#mybutton").attr('disabled', true);
	});

	mysearch.on('search:done', function (properties) {
		$("#mybutton").attr('disabled', false);
		mainSearch.startSearch();
	});

	$(document).ready(function () {
		
		//setting up tokens with selected value.
		$("#mybutton").on("click", function (e) {
			e.preventDefault();
			tokens.set("mytoken", selected_values_array.join());
			submittedTokens.set(tokens.toJSON());
			$("#mybutton").attr('disabled', true);
		});
	});
});

 

 

 

multiselect_table.css

/* The standalone checkbox square*/
 .checkbox {
   width:20px;
   height:20px;
   border: 1px solid #000;
   display: inline-block;
 }
 
 /* This is what simulates a checkmark icon */
.checkbox.checked:after {
   content: '';
   display: block;
   width: 4px;
   height: 7px;
   
   /* "Center" the checkmark */
   position:relative;
   top:4px;
   left:7px;
   
   border: solid #000;
   border-width: 0 2px 2px 0;
   transform: rotate(45deg);
 }

#myTable tr.range-acked  td  {
    background-color: #08f540 !important; 
}

#myTable tr.range-not-acked  td {
    background-color: #f00d18 !important;
}

 

 

 

Please check and acknowledge 😛


Thanks

spkriyaz
Path Finder

Thank you for your help @kamlesh_vaghela . It worked like charm!

Could you please help me in highlighting the entire row with green for the row that is acknowledged and rest of the rows in RED.

For example:

I have attached the table and acknowledged the first 5 rows which will have the value in ACK_BY column I want those to be highlighted in green rest in red. I have the table highlighting js but doesn't work as we will have two different table IDs if I use a different js for row highlighting. Can this highlighting concept be included in your JS and CSS itself, please

table.png

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@spkriyaz 

 

I have updated my answer with JS and CSS changes. Please update coloraturas code as per your requirement. Please check and confirm if it's work for you.

spkriyaz
Path Finder

Hi @kamlesh_vaghela I was able to implement the css and js accordingly for my requirement but there is a small problem in it. When I click the checkbox and before clicking the submit button to acknowledge it, I changed my mind not to acknowledge that particular row so I unchecked it but I could see even the unchecked item gets acknowledged.

For example:

I have 10 rows in which first 5 rows I am planning to acknowledge but after clicking the checkbox before clicking the submit button, I realized that one row out of 5 i don't want to acknowledge so i clicked the checkbox to remove the tick from it. After clicking the submit button with 4 tick, i could still see the 5 rows are getting acknowledged.

Looks like need a condition in place to handle the click and unclick logic. I want to acknowledge the rows only if the checkbox has tick mark in it when the submit button is clicked. Could you please have a look. 

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@spkriyaz 

Can you please share your sample JS code? So I can check it. Did you tried my provided sample Code . It is working in this case.

 

if ($(this).attr('class') === "checkbox") {
							selected_values_array.push($(this).attr('value'));
							$(this).removeClass();
							$(this).addClass("checkbox checked");
						} else {
							$(this).removeClass();
							$(this).addClass("checkbox");
							var i = selected_values_array.indexOf($(this).attr('value'));
							if (i != -1) {
								selected_values_array.splice(i, 1);
							}
						}

 

Can you please check this code in your JS?

 

0 Karma

spkriyaz
Path Finder

Yes @kamlesh_vaghela , I have used your code and I could see if i click the checkbox and un-click it immediately still that row is getting acknowledged when i click the submit button.

Also, need your help to capture the user information to be captured in the Ack_By new column along with the time. I did it but i could see whoever clicks it other than me, it shows their name in it which I acknowledged. This dashboard will be used by multiple people so whoever acknowledges it should show their name when I am accessing it. So if I pass the user information in the outputlookup query which you have given will fix the issue or need to be handled via js? 

I have attached the codes

XML:

 

<form script="multiselect_color.js" stylesheet="multiselect_color.css">
  <label>ERROR MONITORING</label>
  <search id="mysearch">
    <query> |inputlookup ram_error.csv | where ID IN ($mytoken$) | eval Ack=now() | append [|inputlookup ram_error.csv | where not ID IN ($mytoken$) ] | sort ID | outputlookup ram_error.csv</query>
    <earliest>$earliest$</earliest>
    <latest>$latest$</latest>
  </search>
  <row>
    <panel>
      <html>
       <div>
         <input type="button" id="mybutton" value="Ack Selected Rows"/>
       </div>
     </html>
    </panel>
  </row>
  <row>
    <panel>
      <title>Omnipay RAM Error Log</title>
      <table id="myTable">
        <search id="mainSearch">
          <query>|inputlookup ram_error.csv  
| eval Tick=ID."|".Ack 
| join [rest /services/authentication/current-context splunk_server=local | fields + email,username] 
| eval Ack_By=email+" "+strftime(Ack,"%c") 
| makemv delim="," "Package_Name, Procedure_Name, File_Name, Host_Name"
| table Tick ID Institution Date_Time Ack_By Program_ID, Program_Run_ID, Program_Name "Package_Name, Procedure_Name, File_Name, Host_Name" Message_Text email 
| sort - ID 
| fields - ID email Ack time</query>
          <earliest>$earliest$</earliest>
          <latest>$latest$</latest>
          <refresh>1m</refresh>
          <refreshType>delay</refreshType>
        </search>
        <option name="count">10</option>
        <option name="drilldown">row</option>
        <option name="refresh.display">none</option>
        <option name="wrap">true</option>
        <drilldown>
          <condition field="*"></condition>
        </drilldown>
      </table>
    </panel>
  </row>
 </form> 

 

 

JS:

 

require([
		'underscore',
		'jquery',
		'splunkjs/mvc',
		'splunkjs/mvc/tableview',
		'splunkjs/mvc/simplexml/ready!'
	], function (_, $, mvc, TableView) {
	// Access the "default" token model
	var tokens = mvc.Components.get("default");
	var selected_values_array = [];
	var submittedTokens = mvc.Components.get('submitted');
	console.log("This is Multi-select table JS");
	// Custom renderer for applying checkbox.
	var CustomRenderer = TableView.BaseCellRenderer.extend({
			canRender: function (cell) {
				return _(['Tick']).contains(cell.field);
			},
			render: function ($td, cell) {
				var cell_value = cell.value.split("|")[0];
				var ack_value = cell.value.split("|")[1];
				var ack_flag = ack_value !== "";
				console.log(cell.value,ack_flag,ack_value);
				var div = (ack_flag ? $('<div>') :$('<div>').attr({
						"id": "chk-number" + cell_value,
						"value": cell_value
					}).addClass('checkbox').click(function () {
						if ($(this).attr('class') === "checkbox") {
							selected_values_array.push($(this).attr('value'));
							$(this).removeClass();
							$(this).addClass("checkbox checked");
						} else {
							$(this).removeClass();
							$(this).addClass("checkbox");
							var i = selected_values_array.indexOf($(this).attr('value'));
							if (i != -1) {
								selected_values_array.splice(i, 1);
							}
						}
					}))
				var b = (ack_flag ? $td.addClass('range-cell').addClass('range-acked') : $td.addClass('range-cell').addClass('range-not-acked'));
				div.appendTo($td);
			}
		});

	//List of table ID
	var sh = mvc.Components.get("myTable");
	if (typeof(sh) != "undefined") {
		sh.getVisualization(function (tableView) {
			
			tableView.on('rendered', function() {
				console.log("Omnipay 1");
                // Apply class of the cells to the parent row in order to color the whole row
                tableView.$el.find('td.range-cell').each(function() {
                	console.log("Omnipay 2");
                	$(this).parents('tr').addClass(this.className);
                	console.log(this.className);
                });
                console.log("Omnipay 3");
            });

			// Add custom cell renderer and force re-render
			tableView.table.addCellRenderer(new CustomRenderer());
			tableView.table.render();
		});
	}

	// Disabling button while search is running
	var mysearch = mvc.Components.get('mysearch');
	var mainSearch = mvc.Components.get('mainSearch');
	
	mysearch.on('search:start', function (properties) {
		$("#mybutton").attr('disabled', true);
	});

	mysearch.on('search:done', function (properties) {
		$("#mybutton").attr('disabled', false);
		mainSearch.startSearch();
	});

	$(document).ready(function () {
		
		//setting up tokens with selected value.
		$("#mybutton").on("click", function (e) {
			e.preventDefault();
			tokens.set("mytoken", selected_values_array.join());
			submittedTokens.set(tokens.toJSON());
			$("#mybutton").attr('disabled', true);
		});
	});
});

 


CSS:

 

/* The standalone checkbox square*/
 .checkbox {
   width:0px;
   height:0px;
   border: 2px solid #000;
   display: inline-block;
 }
 
 /* This is what simulates a checkmark icon */
 .checkbox.checked:after {
   content: '';
   display: block;
   width: 2px;
   height: 7px;
   
   /* "Center" the checkmark */
   position:relative;
   top:1px;
   left:-8px;
   
   border: solid black;
   border-width: 0 2px 2px 0;
   transform: rotate(45deg);
 }
 
 .checkbox, .radio {
    min-height: 12px;
    padding-left: 12px;
}

td.numeric, th.numeric {
    text-align: left;
}

#myTable tr.range-acked  td  {
    background-color: #c1ffc3 !important; 
}

#myTable tr.range-not-acked  td {
    background-color: #ff7366 !important;
}

 

 

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@spkriyaz 

Your code looks good to me.. Can you please replace below code and check the IDs which are same as checked ??

 

var div = (ack_flag ? $('<div>') :$('<div>').attr({
						"id": "chk-number" + cell_value,
						"value": cell_value
					}).addClass('checkbox').click(function () {
						if ($(this).attr('class') === "checkbox") {
							selected_values_array.push($(this).attr('value'));
							$(this).removeClass();
							$(this).addClass("checkbox checked");
							console.log(selected_values_array);
						} else {
							$(this).removeClass();
							$(this).addClass("checkbox");
							var i = selected_values_array.indexOf($(this).attr('value'));
							if (i != -1) {
								selected_values_array.splice(i, 1);
							}
							console.log(selected_values_array);
						}
					}))

 

0 Karma

spkriyaz
Path Finder

@kamlesh_vaghela not sure whether any lag in js was the issue or not. Now the same code works fine, thanks!

0 Karma

niketn
Legend

@spkriyaz Refer to one of older post that while using Table render() function you many need to add a minor setTimeout() for version 6.6 and above.

https://community.splunk.com/t5/All-Apps-and-Add-ons/Splunk-Dashboard-Examples-Table-Row-highlightin...

 

Also, instead of document ready() function you can also code.

$(document).on("click","#mybutton",function(){
   ... 
   Your Click Handler Code goes here
   ...
});

 

____________________________________________
| makeresults | eval message= "Happy Splunking!!!"

spkriyaz
Path Finder

Hi @niketn,

Could you please review the js and let me know where the changes are exactly required. I modified the document ready() function as you said but still I have the lag. The color is applied only if I flip the table page from 2 to 1. I referred your post which you have mentioned but couldn't fix mine accordingly.

JS:

 

 

require([
		'underscore',
		'jquery',
		'splunkjs/mvc',
		'splunkjs/mvc/tableview',
		'splunkjs/mvc/simplexml/ready!'
	], function (_, $, mvc, TableView) {
	// Access the "default" token model
	var tokens = mvc.Components.get("default");
	var selected_values_array = [];
	var submittedTokens = mvc.Components.get('submitted');
	console.log("This is Multi-select table JS");
	// Custom renderer for applying checkbox.
	var CustomRenderer = TableView.BaseCellRenderer.extend({
			canRender: function (cell) {
				return _(['Tick']).contains(cell.field);
			},
			render: function ($td, cell) {
				var cell_value = cell.value.split("|")[0];
				var ack_value = cell.value.split("|")[1];
				var ack_flag = ack_value !== "";
				console.log(cell.value,ack_flag,ack_value);
				var div = (ack_flag ? $('<div>') :$('<div>').attr({
						"id": "chk-number" + cell_value,
						"value": cell_value
					}).addClass('checkbox').click(function () {
						if ($(this).attr('class') === "checkbox") {
							selected_values_array.push($(this).attr('value'));
							$(this).removeClass();
							$(this).addClass("checkbox checked");
							console.log(selected_values_array);
						} else {
							$(this).removeClass();
							$(this).addClass("checkbox");
							var i = selected_values_array.indexOf($(this).attr('value'));
							if (i != -1) {
								selected_values_array.splice(i, 1);
							}
							console.log(selected_values_array);
						}
					}))
				var b = (ack_flag ? $td.addClass('range-cell').addClass('range-acked') : $td.addClass('range-cell').addClass('range-not-acked'));
				div.appendTo($td);
			}
		});

	//List of table ID
	var sh = mvc.Components.get("myTable");
	if (typeof(sh) != "undefined") {
		sh.getVisualization(function (tableView) {
			
			tableView.on('rendered', function() {
				console.log("Output 1");
				setTimeout(function(){
                // Apply class of the cells to the parent row in order to color the whole row
                tableView.$el.find('td.range-cell').each(function() {
                	console.log("OUtput 2");
                	$(this).parents('tr').addClass(this.className);
                	console.log(this.className);
                });
                console.log("Output 3");
			 },100);
            });

			// Add custom cell renderer and force re-render
			tableView.table.addCellRenderer(new CustomRenderer());
			tableView.table.render();
		});
	}};

	// Disabling button while search is running
	var mysearch = mvc.Components.get('mysearch');
	var mainSearch = mvc.Components.get('mainSearch');
	var myrevertsearch = mvc.Components.get('myrevertsearch'); 
	
	mysearch.on('search:start', function (properties) {
		$("#mybutton").attr('disabled', true);
	});

	mysearch.on('search:done', function (properties) {
		$("#mybutton").attr('disabled', false);
		mainSearch.startSearch();
	});
	
	myrevertsearch.on('search:done', function (properties) {
		console.log("Revert Search Done");
		$("#myrevertbutton").attr('disabled', false);
		mainSearch.startSearch();
		console.log("Main Search Done");
	});

	$(document).ready(function () {
		
		//setting up tokens with selected value.
		$("#mybutton").on("click", function (e) {
			e.preventDefault();
			tokens.set("mytoken", selected_values_array.join());
			submittedTokens.set(tokens.toJSON());
			$("#mybutton").attr('disabled', true);
		});
		
		$("#myrevertbutton").on("click", function (e) {
			e.preventDefault();		
			myrevertsearch.startSearch();
			$("#myrevertbutton").attr('disabled', true);
			console.log("Hiee");
		});
		
	});
});

 

CSS:

 

/* The standalone checkbox square*/
 .checkbox {
   width:0px;
   height:0px;
   border: 2px solid #000;
   display: inline-block;
 }
 
 /* This is what simulates a checkmark icon */
 .checkbox.checked:after {
   content: '';
   display: block;
   width: 2px;
   height: 7px;
   
   /* "Center" the checkmark */
   position:relative;
   top:1px;
   left:-8px;
   
   border: solid black;
   border-width: 0 2px 2px 0;
   transform: rotate(45deg);
 }
 
 .checkbox, .radio {
    min-height: 12px;
    padding-left: 12px;
}

td.numeric, th.numeric {
    text-align: left;
}

#myTable tr.range-acked  td  {
    background-color: #c1ffc3 !important; 
}

#myTable tr.range-not-acked  td {
    background-color: #ff7366 !important;
}


#c1ffc3 !important

 

 

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@spkriyaz 

 

For user information, you can use Global tokens like $env:user$, $env:user_realname$, etc . Please refer this link .

 

0 Karma

spkriyaz
Path Finder

Yes @kamlesh_vaghela , I can use these tokens but I want to store the user information in the lookup. Currently according to the js which I am using, the below search id "mysearch" does the lookup update. Here I am unable to save the user information who clicked the checkbox and acknowledged it. In my search id "mainSearch" I capture the user information in "Ack_By" column and I want that details to stored in the lookup.

Currently, it shows the user information of who logging to the system not the one who acknowledged the row. For example, if you have acknowledged the first row in the table and when I login with my ID and if I acknowledge the second row, it shows both in my ID which is wrong. It should show the first row with your name and second with my name in Ack_By column.

Is there any logic i am missing in my XML to correct this? Could you please check and let me know your comments.

<form script="multiselect_color.js" stylesheet="multiselect_color.css">
  <label>ERROR MONITORING</label>
  <search id="mysearch">
    <query> |inputlookup ram_error.csv | where ID IN ($mytoken$) | eval Ack=now() | append [|inputlookup ram_error.csv | where not ID IN ($mytoken$) ] | sort ID | outputlookup ram_error.csv</query>
    <earliest>$earliest$</earliest>
    <latest>$latest$</latest>
  </search>
  <row>
    <panel>
      <html>
       <div>
         <input type="button" id="mybutton" value="Ack Selected Rows"/>
       </div>
     </html>
    </panel>
  </row>
  <row>
    <panel>
      <title>Omnipay RAM Error Log</title>
      <table id="myTable">
        <search id="mainSearch">
          <query>|inputlookup ram_error.csv  
| eval Tick=ID."|".Ack 
| join [rest /services/authentication/current-context splunk_server=local | fields + email,username] 
| eval Ack_By=email+" "+strftime(Ack,"%c") 
| makemv delim="," "Package_Name, Procedure_Name, File_Name, Host_Name"
| table Tick ID Institution Date_Time Ack_By Program_ID, Program_Run_ID, Program_Name "Package_Name, Procedure_Name, File_Name, Host_Name" Message_Text email 
| sort - ID 
| fields - ID email Ack time</query>
          <earliest>$earliest$</earliest>
          <latest>$latest$</latest>
          <refresh>1m</refresh>
          <refreshType>delay</refreshType>
        </search>
        <option name="count">10</option>
        <option name="drilldown">row</option>
        <option name="refresh.display">none</option>
        <option name="wrap">true</option>
        <drilldown>
          <condition field="*"></condition>
        </drilldown>
      </table>
    </panel>
  </row>
 </form> 
0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

yes @spkriyaz .

 

So you can use global token to store user information in lookup. like

 

|inputlookup ram_error.csv | where ID IN ($mytoken$) | eval Ack=now(),User="$env:user$" | append [|inputlookup ram_error.csv | where not ID IN ($mytoken$) ] | sort ID | outputlookup ram_error.csv

spkriyaz
Path Finder

Hi @kamlesh_vaghela,

Using the same XML,JS and CSS is it possible to add one more feature?

I want to unacknowledge the rows which are acknowledged by mistake. For that, I have a text box in which I can enter the ID(unique for each row) which is acknowledged already but that I want to unacknowledge it now as I realized it was acknowledged by mistake. I created a textbox in the existing dashboard as encircled in red(image below) in which I will provide my ID. I want a button next to "Ack Selected Row" button(marked in green in the image below) say "Unacknowledged". So once I enter the ID and hit that "Unacknowledged" button it should unacknowledge the row. Basically the "Ack_By" should become null and need to updated in the lookup, which will make the row to as unacknowledged.

Need your help to add this logic to the existing JS, could you please check? Attached the existing code and screenshot to give better clarity
button.PNG

 

XML:

<form script="multiselect_color.js" stylesheet="multiselect_color.css">
  <label>ERROR MONITORING</label>
  <search id="mysearch">
    <query> |inputlookup error.csv | where not ID IN ($mytoken$) | append  [|inputlookup error.csv | where ID IN ($mytoken$) | eval Ack=now(),Ack_By="$env:user_email$"+","+strftime(Ack,"%c")] | outputlookup error.csv</query>
    <earliest>$earliest$</earliest>
    <latest>$latest$</latest>
  </search>
  <fieldset submitButton="false" autoRun="false"></fieldset>
  <row depends="$hidden$">
    <panel>
      <html>
         <style>
           td {
            line-height: 13px !important;
            font-size: 13px !important;
           }
         </style>
       </html>
    </panel>
  </row>
  <row>
    <panel>
      <input type="time" token="time1" searchWhenChanged="false">
        <label>Time Window</label>
        <default>
          <earliest>-60m@m</earliest>
          <latest>now</latest>
        </default>
      </input>
      <input type="dropdown" token="inst">
        <label>Institution</label>
        <choice value="*">ALL</choice>
        <default>*</default>
        <fieldForLabel>Institution</fieldForLabel>
        <fieldForValue>Institution</fieldForValue>
        <search>
          <query>|inputlookup error.csv  
| dedup Institution 
| sort Institution
| table Institution</query>
          <earliest>-30m</earliest>
          <latest>now</latest>
        </search>
      </input>
      <input type="dropdown" token="platform" searchWhenChanged="true">
        <label>Platform</label>
        <choice value="EMEA">EMEA</choice>
        <choice value="APAC">APAC</choice>
        <choice value="*">ALL</choice>
        <default>*</default>
      </input>
      <input type="dropdown" token="ackby" searchWhenChanged="true">
        <label>Acknowledged By</label>
        <choice value="*">ALL</choice>
        <default>*</default>
        <fieldForLabel>Ack_name</fieldForLabel>
        <fieldForValue>Ack_name</fieldForValue>
        <search>
          <query>|inputlookup error.csv  
| eval _time=strptime(Date_Time,"%d-%m-%Y %H:%M:%S")
| convert mstime(_time) AS ms_time
| addinfo
| where ms_time &gt;= info_min_time AND ms_time &lt;= info_max_time
| eval fields=split(Ack_By,",") | eval Ack_name=mvindex(fields,0)
| dedup Ack_name
|  table Ack_name</query>
          <earliest>-7d@h</earliest>
          <latest>now</latest>
        </search>
      </input>
      <input type="dropdown" token="ackflag" searchWhenChanged="true">
        <label>Acknowledged (Y/N)</label>
        <choice value="*">Both</choice>
        <search>
          <query>| inputlookup error.csv
| eval ack_flag=if(isnotnull(Ack_By),"Yes","No")
| dedup ack_flag
| table ack_flag</query>
          <earliest>-15m</earliest>
          <latest>now</latest>
        </search>
        <fieldForLabel>ack_flag</fieldForLabel>
        <fieldForValue>ack_flag</fieldForValue>
      </input>
      <input type="text" searchWhenChanged="true" token="unack">
        <label>Enter ID to Unacknowledge</label>
      </input>
      <html>
       <div>
         <input type="button" id="mybutton" value="Ack Selected Rows"/>
       </div>
     </html>
    </panel>
  </row>
  <row>
    <panel>
      <title>Error Log</title>
      <table id="myTable">
        <search id="mainSearch">
          <query>|inputlookup error.csv  
| eval fields=split(Ack_By,",") | eval Ack_name=mvindex(fields,0)
| eval Ack_name = if(isnotnull(Ack_name),Ack_name,"NA")
| eval ack_flag=if(isnotnull(Ack_By),"Yes","No")
| search Institution=$inst$ Platform=$platform$ Ack_name=$ackby$ ack_flag=$ackflag$
| eval _time=strptime(Date_Time,"%d-%m-%Y %H:%M:%S")
| convert mstime(_time) AS ms_time
| addinfo
| where ms_time &gt;= info_min_time AND ms_time &lt;= info_max_time
| eval Tick=ID."|".Ack
| eval "Program_ID Program_Name" = Program_ID+","+Program_Name | makemv delim="," "Program_ID Program_Name" 
| makemv delim="," "Package_Name, Procedure_Name, File_Name, Host_Name"
| makemv delim="," Ack_By
| table Tick ID ms_time Institution Date_Time Ack_By "Program_ID Program_Name", Program_Run_ID "Package_Name, Procedure_Name, File_Name, Host_Name" Message_Text email 
|  sort 0 - ms_time
| fields - email Ack ms_time Ack_name</query>
          <earliest>$time1.earliest$</earliest>
          <latest>$time1.latest$</latest>
          <refresh>1m</refresh>
          <refreshType>delay</refreshType>
        </search>
        <option name="count">10</option>
        <option name="drilldown">row</option>
        <option name="refresh.display">progressbar</option>
        <option name="wrap">true</option>
        <drilldown>
          <condition field="*"></condition>
        </drilldown>
      </table>
    </panel>
  </row>
</form>

Existing JS:

require([
		'underscore',
		'jquery',
		'splunkjs/mvc',
		'splunkjs/mvc/tableview',
		'splunkjs/mvc/simplexml/ready!'
	], function (_, $, mvc, TableView) {
	// Access the "default" token model
	var tokens = mvc.Components.get("default");
	var selected_values_array = [];
	var submittedTokens = mvc.Components.get('submitted');
	console.log("This is Multi-select table JS");
	// Custom renderer for applying checkbox.
	var CustomRenderer = TableView.BaseCellRenderer.extend({
			canRender: function (cell) {
				return _(['Tick']).contains(cell.field);
			},
			render: function ($td, cell) {
				var cell_value = cell.value.split("|")[0];
				var ack_value = cell.value.split("|")[1];
				var ack_flag = ack_value !== "";
				console.log(cell.value,ack_flag,ack_value);
				var div = (ack_flag ? $('<div>') :$('<div>').attr({
						"id": "chk-number" + cell_value,
						"value": cell_value
					}).addClass('checkbox').click(function () {
						if ($(this).attr('class') === "checkbox") {
							selected_values_array.push($(this).attr('value'));
							$(this).removeClass();
							$(this).addClass("checkbox checked");
						} else {
							$(this).removeClass();
							$(this).addClass("checkbox");
							var i = selected_values_array.indexOf($(this).attr('value'));
							if (i != -1) {
								selected_values_array.splice(i, 1);
							}
						}
					}))
				var b = (ack_flag ? $td.addClass('range-cell').addClass('range-acked') : $td.addClass('range-cell').addClass('range-not-acked'));
				div.appendTo($td);
			}
		});

	//List of table ID
	var sh = mvc.Components.get("myTable");
	if (typeof(sh) != "undefined") {
		sh.getVisualization(function (tableView) {
			
			tableView.on('rendered', function() {
				console.log("Kamlesh 1");
                // Apply class of the cells to the parent row in order to color the whole row
                tableView.$el.find('td.range-cell').each(function() {
                	console.log("Kamlesh 2");
                	$(this).parents('tr').addClass(this.className);
                	console.log(this.className);
                });
                console.log("Kamlesh 3");
            });

			// Add custom cell renderer and force re-render
			tableView.table.addCellRenderer(new CustomRenderer());
			tableView.table.render();
		});
	}

	// Disabling button while search is running
	var mysearch = mvc.Components.get('mysearch');
	var mainSearch = mvc.Components.get('mainSearch');
	
	mysearch.on('search:start', function (properties) {
		$("#mybutton").attr('disabled', true);
	});

	mysearch.on('search:done', function (properties) {
		$("#mybutton").attr('disabled', false);
		mainSearch.startSearch();
	});

	$(document).ready(function () {
		
		//setting up tokens with selected value.
		$("#mybutton").on("click", function (e) {
			e.preventDefault();
			tokens.set("mytoken", selected_values_array.join());
			submittedTokens.set(tokens.toJSON());
			$("#mybutton").attr('disabled', true);
		});
	});
});

 

CSS:

/* The standalone checkbox square*/
 .checkbox {
   width:0px;
   height:0px;
   border: 2px solid #000;
   display: inline-block;
 }
 
 /* This is what simulates a checkmark icon */
 .checkbox.checked:after {
   content: '';
   display: block;
   width: 2px;
   height: 7px;
   
   /* "Center" the checkmark */
   position:relative;
   top:1px;
   left:-8px;
   
   border: solid black;
   border-width: 0 2px 2px 0;
   transform: rotate(45deg);
 }
 
 .checkbox, .radio {
    min-height: 12px;
    padding-left: 12px;
}

td.numeric, th.numeric {
    text-align: left;
}

#myTable tr.range-acked  td  {
    background-color: #c1ffc3 !important; 
}

#myTable tr.range-not-acked  td {
    background-color: #ff7366 !important;
}


#c1ffc3 !important

 

Thanks in advance 🙂

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@spkriyaz 

For providing UnAck feature,  please add below code in my provided example in this answer.

XML

1) Add a search manager.

  <search id="myrevertsearch">
    <query> | inputlookup sample_data  where Number = "$UnAckID$" | eval Ack="" | append [| inputlookup sample_data  where Number != "$UnAckID$"] | sort Number | outputlookup sample_data</query>
  </search>

 

2) Add button with "Ack Selected Button".

<div>
  <input type="button" id="mybutton" value="Ack Selected Rows"/>
  <input type="button" id="myrevertbutton" value="Un Ack Entered Id"/>
</div>

 

JS:

3) Add SearchManager Object and on:done event with other object,

// Disabling button while search is running
	var mysearch = mvc.Components.get('mysearch');
	var mainSearch = mvc.Components.get('mainSearch');
	var myrevertsearch = mvc.Components.get('myrevertsearch'); 




myrevertsearch.on('search:done', function (properties) {
		$("#myrevertbutton").attr('disabled', false);
		mainSearch.startSearch();
	});

 

4) Add onclick event on UnAck button

$(document).ready(function () {
		
		//setting up tokens with selected value.
		$("#mybutton").on("click", function (e) {
			e.preventDefault();
			tokens.set("mytoken", selected_values_array.join());
			submittedTokens.set(tokens.toJSON());
			$("#mybutton").attr('disabled', true);
		});
		
		$("#myrevertbutton").on("click", function (e) {
			e.preventDefault();
			myrevertsearch.startSearch();
			$("#myrevertbutton").attr('disabled', true);
		});
	});

 

 

Thanks
Kamlesh Vaghela

0 Karma

spkriyaz
Path Finder

Applied the following changes, I create a text box and gave the token "UnAckID" for it. If I enter the ID number in the textbox and click the button "Un Ack Entered Id" the button froze and no action performed. If I refresh the page my lookup file becomes empty. Is anything missed here?

unack.PNG

XML:

<form script="multiselect_color_omupd.js" stylesheet="multiselect_color_om.css">
  <label>ERROR MONITORING</label>
  <search id="mysearch">
    <query> |inputlookup ram_text_error.csv | where ID IN ($mytoken$) | eval Ack=now(),Ack_By="$env:user_email$"+","+strftime(Ack,"%c") | append [|inputlookup ram_text_error.csv | where not ID IN ($mytoken$) ] | sort ID | outputlookup ram_text_error.csv</query>
    <earliest>$earliest$</earliest>
    <latest>$latest$</latest>
  </search>
  <search id="myrevertsearch">
    <query> | inputlookup ram_text_error.csv  where Number = "$UnAckID$" | eval Ack="" | append [| inputlookup ram_text_error.csv  where Number != "$UnAckID$"] | sort Number | outputlookup ram_text_error.csv</query>
  </search>
  <fieldset submitButton="false" autoRun="false">
    <input type="text" searchWhenChanged="false" token="UnAckID">
      <label>Un Ack Button</label>
    </input>
  </fieldset>
  <row>
    <panel>
      <html>
       <div>
        <input type="button" id="mybutton" value="Ack Selected Rows"/>
        <input type="button" id="myrevertbutton" value="Un Ack Entered Id"/>
       </div>
     </html>
    </panel>
  </row>
  <row>
    <panel>
      <title>Omnipay RAM Error Log</title>
      <table id="myTable">
        <search id="mainSearch">
          <query>|inputlookup ram_text_error.csv  
| eval Tick=ID."|".Ack 
| makemv delim="," Package_Name_Procedure_Name_File_Name_Host_Name
| table Tick ID Institution Date_Time Ack_By Program_ID, Program_Run_ID, Program_Name, Package_Name_Procedure_Name_File_Name_Host_Name, Message_Text, email 
| sort - ID
|fields - email Ack</query>
          <earliest>$earliest$</earliest>
          <latest>$latest$</latest>
          <refresh>1m</refresh>
          <refreshType>delay</refreshType>
        </search>
        <option name="count">10</option>
        <option name="drilldown">row</option>
        <option name="refresh.display">none</option>
        <option name="wrap">true</option>
        <drilldown>
          <condition field="*"></condition>
        </drilldown>
      </table>
    </panel>
  </row>
</form>

JS:

require([
		'underscore',
		'jquery',
		'splunkjs/mvc',
		'splunkjs/mvc/tableview',
		'splunkjs/mvc/simplexml/ready!'
	], function (_, $, mvc, TableView) {
	// Access the "default" token model
	var tokens = mvc.Components.get("default");
	var selected_values_array = [];
	var submittedTokens = mvc.Components.get('submitted');
	console.log("This is Multi-select table JS");
	// Custom renderer for applying checkbox.
	var CustomRenderer = TableView.BaseCellRenderer.extend({
			canRender: function (cell) {
				return _(['Tick']).contains(cell.field);
			},
			render: function ($td, cell) {
				var cell_value = cell.value.split("|")[0];
				var ack_value = cell.value.split("|")[1];
				var ack_flag = ack_value !== "";
				console.log(cell.value,ack_flag,ack_value);
				var div = (ack_flag ? $('<div>') :$('<div>').attr({
						"id": "chk-number" + cell_value,
						"value": cell_value
					}).addClass('checkbox').click(function () {
						if ($(this).attr('class') === "checkbox") {
							selected_values_array.push($(this).attr('value'));
							$(this).removeClass();
							$(this).addClass("checkbox checked");
							console.log(selected_values_array);
						} else {
							$(this).removeClass();
							$(this).addClass("checkbox");
							var i = selected_values_array.indexOf($(this).attr('value'));
							if (i != -1) {
								selected_values_array.splice(i, 1);
							}
							console.log(selected_values_array);
						}
					}))
				var b = (ack_flag ? $td.addClass('range-cell').addClass('range-acked') : $td.addClass('range-cell').addClass('range-not-acked'));
				div.appendTo($td);
			}
		});

	//List of table ID
	var sh = mvc.Components.get("myTable");
	if (typeof(sh) != "undefined") {
		sh.getVisualization(function (tableView) {
			
			tableView.on('rendered', function() {
				console.log("Output 1");
                // Apply class of the cells to the parent row in order to color the whole row
                tableView.$el.find('td.range-cell').each(function() {
                	console.log("OUtput 2");
                	$(this).parents('tr').addClass(this.className);
                	console.log(this.className);
                });
                console.log("Output 3");
            });

			// Add custom cell renderer and force re-render
			tableView.table.addCellRenderer(new CustomRenderer());
			tableView.table.render();
		});
	}

	// Disabling button while search is running
	var mysearch = mvc.Components.get('mysearch');
	var mainSearch = mvc.Components.get('mainSearch');
	var myrevertsearch = mvc.Components.get('myrevertsearch'); 
	
	mysearch.on('search:start', function (properties) {
		$("#mybutton").attr('disabled', true);
	});

	mysearch.on('search:done', function (properties) {
		$("#mybutton").attr('disabled', false);
		mainSearch.startSearch();
	});
	
	myrevertsearch.on('search:done', function (properties) {
		$("#myrevertbutton").attr('disabled', false);
		mainSearch.startSearch();
	});

	$(document).ready(function () {
		
		//setting up tokens with selected value.
		$("#mybutton").on("click", function (e) {
			e.preventDefault();
			tokens.set("mytoken", selected_values_array.join());
			submittedTokens.set(tokens.toJSON());
			$("#mybutton").attr('disabled', true);
		});
		
		$("#myrevertbutton").on("click", function (e) {
			e.preventDefault();
			myrevertsearch.startSearch();
			$("#myrevertbutton").attr('disabled', true);
		});
	});
});
0 Karma

jayregu17
Loves-to-Learn Everything

Hi 

 

Did you manage to get this solution work? if yes, could you please share the code?

 

Thanks very much

0 Karma
Get Updates on the Splunk Community!

Extending Observability Content to Splunk Cloud

Register to join us !   In this Extending Observability Content to Splunk Cloud Tech Talk, you'll see how to ...

What's new in Splunk Cloud Platform 9.1.2312?

Hi Splunky people! We are excited to share the newest updates in Splunk Cloud Platform 9.1.2312! Analysts can ...

What’s New in Splunk Security Essentials 3.8.0?

Splunk Security Essentials (SSE) is an app that can amplify the power of your existing Splunk Cloud Platform, ...