I'm implementing a dashboard that enables the ability to acknowledge events using kvstores. Basically my users need the ability to mark an event as acknowledged and also remove that acknowledgement in the event they mark it by mistake, need to re-evaluate the event, etc. Using the Button module, the ability to mark events as reviewed works correctly, however, my users would like a checkbox instead.
I've implemented the Checkbox module as in the below XML, but there are two issues - 1) Although I have autoRun="True"
defined, when click the checkbox the table does not refresh to reflect the acknowledgement 2) The checkbox value, i.e. checked or unchecked, is not preserved during a refresh of the page or table. For example, if I check the checkbox to acknowledge an event and I hard refresh the page, the new state will reflect acknowledged. If I check to acknowledge a second event, when the dashboard refreshes only the second event I tagged will reflect as acknowledged since the first event will read from the offValue
parameter for the field because the checkbox does not maintain its checked status.
Hopefully that makes sense, and any help is appreciated since I'm pretty new to Sideview.
<view autoCancelInterval="90" isSticky="False" onunloadCancelJobs="true" template="dashboard.html">
<label>Testing Sideview</label>
<module name="AccountBar" layoutPanel="appHeader" />
<module name="AppBar" layoutPanel="appHeader" />
<module name="SideviewUtils" layoutPanel="appHeader" />
<module name="Message" layoutPanel="messaging">
<param name=”filter”>*</param>
<param name=”maxSize”>2</param>
<param name=”clearOnJobDispatch”>False</param>
</module>
<module name=TextField” layoutPanel=”panel_row1_col1” autoRun=”True”>
<param name=”name”>wild</param>
<param name=”float”>left</param>
<param name=”label”>Search</param>
<param name=”default”>*</param>
<module name=”Search”>
<param name=”search”>`Test_Macro(wild=”$wild$”)` | eval Acknowledge="" | lookup myLookup myIdField OUTPUT state</param>
<!-- This search is loaded by default to populate the table with events -->
<param name=”earliest”>-1d</param>
<param name=”latest”>now</param>
<module name=”Pager”>
<module name=”Table”>
<module name=”Checkbox” group=”row.fields.Acknowledge” autoRun="True">
<param name=”name”>state</param>
<param name=”onValue”>state="ack"</param>
<param name=”offValue”>state=""</param>
<module name=”Search”>
<param name=”search”>| inputlookup myLookup | append [stats count | fields - count | eval myIdField="$row.fields.myIdField$" | eval $state$] | stats last(state) as state by myIdField | outputlookup myLookup</param>
<!-- This search is executed based on checkbox behavior to update the collection and state of events -->
<module name=”CustomBehavior”>
<param name=”requiresDispatch”>True</param>
</module>
</module>
</module>
</module>
</module>
</module>
</module>
</view>
This is really two questions, and the answers are pretty complicated, so let me answer them one at a time.
First -- "how can we make the Table with it's table-embedded Checkbox modules load so that the last known state is actually reflected in the checked/unchecked state of the checkboxes".
You were optimistic that putting autoRun="true" on the Checkbox would do something, but that will do nothing good. The cardinal rule of autoRun="true" is that you should never have one downstream from another. Never nest an autoRun="true" inside another. It's very bad.
So anyway, we want the individual Checkbox modules to select themselves if the row they represent has state="ack"
. We can rely on some things. First, if a Sideview form module gets data from upstream, and it sees a key in that data whose name matches its own "name" param, it will try and set itself to the value. So if we can contrive to have a key called "state" whose value is either "ack" or "", then it'll just work. Here is XML where we use ValueSetter to do exactly this:
<view autoCancelInterval="90" isSticky="False" onunloadCancelJobs="true" template="dashboard.html">
<label>Testing Sideview</label>
<module name="AccountBar" layoutPanel="appHeader" />
<module name="AppBar" layoutPanel="appHeader" />
<module name="SideviewUtils" layoutPanel="appHeader" />
<module name="Message" layoutPanel="messaging">
<param name="filter">*</param>
<param name="maxSize">2</param>
<param name="clearOnJobDispatch">False</param>
</module>
<module name="TextField" layoutPanel="panel_row1_col1" autoRun="True">
<param name="name">wild</param>
<param name="float">left</param>
<param name="label">Search</param>
<param name="default">*</param>
<module name="Search">
<param name=”search”>`Test_Macro(wild=”$wild$”)` | eval Acknowledge="" | lookup myLookup myIdField OUTPUT state</param>
<param name="earliest">-1d</param>
<param name="latest">now</param>
<module name="Pager">
<module name="Table">
<module name="ValueSetter" group="row.fields.Acknowledge">
<param name="name">state</param>
<param name="value">$row.fields.state$</param>
<module name="Checkbox">
<param name="name">state</param>
<param name="onValue">ack</param>
<param name="offValue"></param>
<module name="Search">
<param name="search">| inputlookup myLookup | append [stats count | fields - count | eval myIdField="$row.fields.myIdField$" | eval state="$state$"] | stats last(state) as state by myIdField | outputlookup myLookup</param>
<!-- This search is executed based on checkbox behavior to update the collection and state of events -->
<module name="CustomBehavior">
<param name="requiresDispatch">True</param>
</module>
</module>
</module>
</module>
</module>
</module>
</module>
</module>
</view>
When the page is loaded, now all the checkbox states match the underlying states. And as you had already, when a checkbox is clicked, the embedded search is run, thus changing the underlying state of the lookup to match the now-changed state of the checkbox. Things go two ways and everything is marvelous.
2) Except the table doesn't refresh, so the "state" column still says the wrong thing.
a) Really easy solution! Do not display the state as a column! You can give the Checkbox module a "label" param of "Acknowledged" and I submit that the usability is pretty good here. The users will trust the Checkboxes even if the whole table isn't updating.
b) I've been down this road on several other interfaces exactly like this. actually after making teh Table refresh you'll have another problem you haven't noticed yet - making the Table reload back on whatever page number the user had paged out to. o_O.
Ignoring the paging, the answer is to define two customBehaviors in application.js. One customBehavior is on a CustomBehavior module just upstream of the Table module, the other one is on that CustomBehavior we already have downstream from the embedded Search module(s). Then to get all the paging right, there has to be a third longer customBehavior on the Table. Like many seemingly easy UI customizations, it gets complicated. a) is recommended! just remove the "state" column from the Table. 😃
Thanks for the answer!
I'm applying this to tables that will be dynamically updating and displaying intrusion detection data, and the plan is to have the tables auto-refresh every minute or two, which is the reasoning behind my concern about the checkboxes not maintaining their value. Analysts need the ability to determine whether or not an event has been "acknowledged" to prevent duplicate effort, an I'm also looking at a way to display the user that acknowledged the event in case someone has a question they know who to reach out to.
I'll check out ValueSetter, as long as the Checkbox remains checked so that a) analysts can determine an event has been reviewed already and b) as other events are acknowledged, the table is refreshed, and other analysts view the table all events have their state preserved (either acknowledged or not) and displayed properly then I'm happy!