Trigger Event Log
[working/Evergreen.git] / Open-ILS / src / templates / actor / user / event_log.tt2
1 [% WRAPPER base.tt2 %]
2 [% ctx.page_title = "Triggered Event Log" %]
3 <script type="text/javascript">
4     dojo.require("dijit.form.Button");
5     dojo.require("dojo.data.ItemFileReadStore");
6     dojo.require("openils.CGI");
7     dojo.require("openils.widget.FlattenerGrid");
8     dojo.require("openils.widget.OrgUnitFilteringSelect");
9     dojo.require("openils.widget.ProgressDialog");
10
11     /* If you want to expand this list of supported core_types, you must also
12        change openils.widget._FSBuilder.CoreType further down, so that it
13        includes labels for these in its data store for filter widget
14        purposes. */
15     var initial_query = {"core_type": ["circ", "ahr"]};
16
17     /* Pre-filter by patron ID or copy ID if those are passed in as CGI
18        parameters.  It's an either-or proposition for now. */
19     var cgi = new openils.CGI();
20     var patron_id = cgi.param("patron_id");
21     var copy_id = cgi.param("copy_id");
22
23     /* These features depends on target_{circ,hold}_{patron,copy}_id fields
24        being present in map_extras later. */
25     if (patron_id) {
26         initial_query["-or"] = {
27             "target_circ_patron_id": patron_id,
28             "target_hold_patron_id": patron_id
29         };
30         openils.Util.addOnLoad(
31             function() { openils.Util.show("patron_specific", "inline"); }
32         );
33     } else if (copy_id) {
34         initial_query["-or"] = {
35             "target_circ_copy_id": copy_id,
36             "target_hold_copy_id": copy_id
37         };
38         openils.Util.addOnLoad(
39             function() { openils.Util.show("copy_specific", "inline"); }
40         );
41     }
42
43     /* This semaphore business exists to prevent a race condition. Both
44        the OrgUnitFilteringSelect and the FlattenerFilterPane try to refresh
45        the grid when they're done loading, but that doesn't need to happen
46        until they're /both/ done loading. */
47     var _filter_semaphore_ctr = 2;
48     function filter_semaphore() { return --_filter_semaphore_ctr <= 0; }
49     function filter_semaphore_callback() {
50         grid.fetchLock = false;
51         grid.filterUi.doApply();
52     }
53
54     /* This and its subclasses exist because *FilterPane expect things that
55        act like AutoFieldWidget, which is a widget /builder/.  */
56     dojo.declare(
57         "openils.widget._FSBuilder", null, {
58             "dijitArgs": null,
59             "parentNode": null,
60             "useCorrectly": true,
61
62             "constructor": function(args) {
63                 dojo.mixin(this, args);
64             },
65
66             "build": function() {
67                 var dijitArgs = dojo.mixin(
68                     {
69                         "store": this.store,
70                         "query": {},
71                         "labelAttr": "label",
72                         "searchAttr": "label",
73                     }, this.dijitArgs
74                 );
75
76                 this.widget =
77                     new dijit.form.FilteringSelect(dijitArgs, this.parentNode);
78             }
79         }
80     );
81
82     dojo.declare(
83         "openils.widget._FSBuilder.CoreType",
84         [openils.widget._FSBuilder], {
85             "store": new dojo.data.ItemFileReadStore({
86                 "data": {
87                     "identifier": "name",
88                     "items": [  /* XXX i18n */
89                         {"name": "circ", "label": "Circulation"},
90                         {"name": "ahr", "label": "Hold"}
91                     ]
92                 }
93             })
94         }
95     );
96
97     dojo.declare(
98         "openils.widget._FSBuilder.EventState",
99         [openils.widget._FSBuilder], {
100             "store": new dojo.data.ItemFileReadStore({
101                 "data": {
102                     "identifier": "name",
103                     "items": [  /* XXX i18n */
104                         {"name": "found", "label": "Found"},
105                         {"name": "collected", "label": "Collected"},
106                         {"name": "invalid", "label": "Invalid"},
107                         {"name": "pending", "label": "Pending"},
108                         {"name": "reacting", "label": "Reacting"},
109                         {"name": "complete", "label": "Complete"},
110                         {"name": "error", "label": "Error"}
111                     ]
112                 }
113             })
114         }
115     );
116
117     /* Various things with which to initialize FlattenerGrid. */
118     var map_extras = {
119         "core_type": {"path": "hook.core_type", "filter": true},
120         "perm_lib": {"path": "perm_lib", "filter": true},
121         "target_circ_patron_id": {"path": "target_circ.usr", "filter": true},
122         "target_hold_patron_id": {"path": "target_hold.usr", "filter": true},
123         "target_circ_copy_id":{"path":"target_circ.target_copy","filter":true},
124         "target_hold_copy_id":{"path":"target_hold.current_copy","filter":true}
125     };
126
127     var filter_initializers = [
128         {"field": "state", "operator": "=", "value": "complete"},
129         {"field": "core_type", "operator": "=", "value": "circ"}
130     ];
131
132     var filter_widget_builders = {
133         "ath:core_type": openils.widget._FSBuilder.CoreType,
134         "atul:state": openils.widget._FSBuilder.EventState,
135     };
136
137     function print_all() {
138         var n;
139         if ((n = parseInt(prompt('[% l("Up to how many rows?") %]','100'))) > 0)
140             grid.print(n);
141     }
142
143     function act_on_events(id_list, action) {
144         console.log(id_list);
145         var method = "open-ils.actor.user.event." + action + ".batch";
146         if (id_list.length < 1) {
147             alert("[% l('You selected nothing.') %]");
148             return;
149         }
150
151         var count = 0;
152         progress_dialog.show(true);
153         fieldmapper.standardRequest(
154             ["open-ils.actor", method], {
155                 "async": true,
156                 "params": [openils.User.authtoken, id_list],
157                 "onresponse": function(r) {
158                     if (r = openils.Util.readResponse(r)) {
159                         progress_dialog.update(
160                             {"maximum": id_list.length, "progress": ++count}
161                         );
162                     }
163                 },
164                 "oncomplete": function(r) {
165                     progress_dialog.hide();
166                     r = openils.Util.readResponse(r);
167                     grid.refresh();
168                 }
169             }
170         );
171     }
172
173     /* The callback fired when the OrgUnitFilteringSelect is changed */
174     function set_grid_query_from_org_selector() {
175         /* Naughty: shouldn't use _baseQuery like this, but need to rethink
176            multiple competing filtering mechanisms. */
177         grid._baseQuery.perm_lib = aou.descendantNodeList(
178             org_selector.attr("value"), /* as id */ true
179         );
180
181         /* But for the persistent filter UI, this would be grid.refresh() */
182         if (filter_semaphore())   /* avoid race between ou selector and other
183                                      filter thing */
184             filter_semaphore_callback();
185     }
186
187     /* Builds a OrgUnitFilteringSelect limited to org units where you have
188        a given permission. */
189     function prepare_org_selector(perm) {
190         new openils.User().buildPermOrgSelector(
191             perm, org_selector, null,
192             function() {
193                 dojo.connect(
194                     org_selector, "onChange", set_grid_query_from_org_selector
195                 );
196                 set_grid_query_from_org_selector();
197             }
198         );
199     }
200
201     openils.Util.addOnLoad(
202         function() {
203             prepare_org_selector("VIEW_TRIGGER_EVENT");
204         }
205     );
206 </script>
207 <div dojoType="dijit.layout.ContentPane" layoutAlign="client">
208     <div dojoType="dijit.layout.ContentPane"
209          layoutAlign="top" class="oils-header-panel">
210         <div>
211             [% ctx.page_title %]
212             <span id="copy_specific" class="hidden">([% l('copy specific') %])</span>
213             <span id="patron_specific" class="hidden">([% l('patron specific') %])</span>
214         </div>
215         <div>
216             <button dojoType="dijit.form.Button"
217                 onClick="act_on_events(grid.getSelectedIDs(), 'reset');">[% l('Reset Selected Events') %]</button>
218             <button dojoType="dijit.form.Button"
219                 onClick="act_on_events(grid.getSelectedIDs(), 'cancel');">[% l('Cancel Selected Events') %]</button>
220             <button dojoType="dijit.form.Button"
221                 onClick="grid.printSelected();">[% l('Print Selected Events') %]</button>
222             <button dojoType="dijit.form.Button"
223                 onClick="print_all();">[% l('Print All Events') %]</button>
224         </div>
225     </div>
226     <div class="oils-acq-basic-roomy">
227         <label for="org_selector">[% l('Show events at and below') %]:</label>
228         <select
229             id="org_selector" jsId="org_selector"
230             dojoType="openils.widget.OrgUnitFilteringSelect"
231             searchAttr="name" labelAttr="name">
232         </select>
233     </div>
234     <div>
235         <div style="float: left; width: 66%;">
236             <table id="gridNode"
237                 jsid="grid"
238                 dojoType="openils.widget.FlattenerGrid"
239                 columnPersistKey='"ui.grid_columns.actor.user.event_log"'
240                 autoHeight="10"
241                 selectable="true"
242                 editOnEnter="false"
243                 showLoadFilter="true"
244                 filterAlwaysInDiv="'filter_holder'"
245                 filterInitializers="filter_initializers"
246                 filterWidgetBuilders="filter_widget_builders"
247                 filterSemaphore="filter_semaphore"
248                 filterSemaphoreCallback="filter_semaphore_callback"
249                 fmClass="'atul'"
250                 autoFieldFields="['target_hold','target_circ']"
251                 defaultSort="['run_time']"
252                 mapExtras="map_extras"
253                 fetchLock="true"
254                 query="initial_query">
255                 <thead>
256                     <tr>
257                         <th field="name" fpath="name" ffilter="true">Event Name</th>
258                         <th field="reactor" fpath="reactor" ffilter="true"></th>
259                         <th field="run_time" fpath="run_time" ffilter="true"></th><!-- XXX TODO formatters for *_time -->
260                         <th field="add_time" fpath="add_time" ffilter="true" _visible="false"></th>
261                         <th field="start_time" fpath="start_time" ffilter="true" _visible="false"></th>
262                         <th field="update_time" fpath="update_time" ffilter="true" _visible="false"></th>
263                         <th field="complete_time" fpath="complete_time" ffilter="true" _visible="false"></th>
264                         <th field="id" fpath="id" ffilter="true" _visible="false"></th>
265                         <th field="state" fpath="state" ffilter="true"></th>
266                         <th field="target_circ_copy_barcode" fpath="target_circ.target_copy.barcode" ffilter="true">Target Circulation - Copy Barcode</th>
267                         <th field="target_circ_copy_title" fpath="target_circ.target_copy.call_number.record.simple_record.title">Target Circulation - Title</th>
268                         <th field="target_circ_copy_author" fpath="target_circ.target_copy.call_number.record.simple_record.author">Target Circulation - Author</th>
269                         <th field="target_circ_patron_barcode" fpath="target_circ.usr.card.barcode" ffilter="true">Target Circulation - Patron Barcode</th>
270                         <th field="target_hold_patron_barcode" fpath="target_hold.usr.card.barcode" ffilter="true">Target Hold - Patron Barcode</th>
271                     </tr>
272                 </thead>
273             </table>
274         </div>
275         <div style="float: right; width: 33%;">
276             <div id="filter_holder"></div>
277         </div>
278         <div style="clear: both;"></div>
279     </div>
280 </div>
281 <div dojoType="openils.widget.ProgressDialog" jsId="progress_dialog"></div>
282 [% END %]