99fba63e1b999ab0b486e65b6a0b4deb6bfa6ace
[Evergreen.git] / Open-ILS / web / js / ui / default / cat / authority / list.js
1 dojo.require('dijit.Dialog');
2 dojo.require('dijit.form.Button');
3 dojo.require('dijit.form.DropDownButton');
4 dojo.require('dijit.form.FilteringSelect');
5 dojo.require('dijit.form.Form');
6 dojo.require('dijit.form.NumberSpinner');
7 dojo.require('dijit.form.TextBox');
8 dojo.require("dijit.Menu");
9 dojo.require("dijit.MenuItem");
10 dojo.require('dojox.xml.parser');
11 dojo.require('DojoSRF');
12 dojo.require("fieldmapper.Fieldmapper");
13 dojo.require('openils.CGI');
14 dojo.require('openils.PermaCrud');
15 dojo.require('openils.XUL');
16 dojo.require('openils.widget.OrgUnitFilteringSelect');
17 dojo.require("openils.widget.PCrudAutocompleteBox");
18 dojo.require("MARC.FixedFields");
19 dojo.requireLocalization("openils.authority", "authority");
20 var auth_strings = dojo.i18n.getLocalization("openils.authority", "authority");
21
22 var cgi = new openils.CGI();
23 var pcrud = new openils.PermaCrud();
24
25 var _acs_cache_by_at = {};
26 function fetch_control_set(thesaurus) {
27     if (!_acs_cache_by_at[thesaurus]) {
28         var at = pcrud.retrieve(
29             "at", thesaurus,
30             {"flesh": 1, "flesh_fields": {"at": ["control_set"]}}
31         );
32         var cs;
33         if (at.control_set()) {
34             cs = at.control_set();
35         } else {
36             cs = new fieldmapper.acs();
37             cs.name("None");    // XXX i18n
38
39         }
40         _acs_cache_by_at[thesaurus] = cs;
41     }
42     return _acs_cache_by_at[thesaurus];
43 }
44
45 /*
46 // OrgUnits do not currently affect the retrieval of authority records,
47 // but this is how to display them if they become OrgUnit-aware
48 function authOUListInit() {
49     new openils.User().buildPermOrgSelector(
50         "STAFF_LOGIN", // anywhere you can log in
51         dijit.byId("authOU"),
52         null, // pre-selected org
53         null
54     );
55 }
56 dojo.addOnLoad(authOUListInit);
57 */
58 function displayAuthorities(data) { 
59
60     var idArr = [];
61     // Grab each record from the returned authority records
62     dojo.query("record", data).forEach(function(node) {
63         var auth = {};
64         auth.text = '';
65         auth.thesaurus = '|';
66         auth.id = 0;
67
68         // Grab each authority record field from the authority record
69         dojo.query("datafield[tag^='1']", node).forEach(function(dfNode) {
70             auth.text += dojox.xml.parser.textContent(dfNode); 
71             auth.name = dojo.attr(dfNode, 'tag');
72             auth.ind1 = dojo.attr(dfNode, 'ind1');
73             auth.ind2 = dojo.attr(dfNode, 'ind2');
74         });
75
76         
77         // Grab the ID of the authority record
78         dojo.query("datafield[tag='901'] subfield[code='c']", node).forEach(function(dfNode) {
79             auth.id = dojox.xml.parser.textContent(dfNode); 
80         });
81
82         /* I wrap this in try/catch only because:
83          *  a) this interface hasn't hitherto relied on MARC.Record, and
84          *  b) the functionality we need it for is optional
85          */
86         try {
87             var marc = new MARC.Record({"rtype": "AUT", "xml": node});
88             auth.thesaurus = marc.extractFixedField("Subj", "|");
89         } catch (E) {
90             console.warn(
91                 "MARC.Record didn't work for authority record " +
92                 auth.id + ": " + E
93             );
94         }
95
96         idArr.push(parseInt(auth.id));
97
98         // Create the authority record listing entry. XXX i18n
99         dojo.place(
100             '<div class="authEntry" id="auth' + auth.id + '">' +
101             '<div class="text" id="authLabel' + auth.id + '">' + auth.text + '</div>' +
102             '<div class="authority-control-set">Control Set: <span class="acs-name">' +
103             fetch_control_set(auth.thesaurus).name() +
104             '</span> <span class="acs-id">(#' +
105             fetch_control_set(auth.thesaurus).id() + ')</span></div></div>',
106             "authlist-div", "last"
107         );
108
109         // Add the menu of new/edit/delete/mark-for-merge options
110         var auth_menu = new dijit.Menu({});
111
112         // "Edit" menu item
113         new dijit.MenuItem({"id": "edit_" + auth.id, "onClick": function(){
114             var auth_rec = pcrud.retrieve("are", auth.id);
115             if (auth_rec) {
116                 loadMarcEditor(pcrud, auth_rec);
117             }
118         }, "label":auth_strings.MENU_EDIT}).placeAt(auth_menu, "first");
119
120         // "Merge" menu item
121         new dijit.MenuItem({"id": "merge_" + auth.id, "onClick":function(){
122             auth.text = '';
123             dojo.query('#auth' + auth.id + ' span.text').forEach(function(node) {
124                 auth.text += dojox.xml.parser.textContent(node); 
125             });
126
127             // If there is a toMerge item already, this is a target record
128             var mergeRole = '<td style="border: 1px solid black; padding-left: 0.5em; padding-right: 1em;">';
129             var isTarget = dojo.query('.toMerge').length;
130             if (isTarget) {
131                 mergeRole += auth_strings.TARGET_RECORD + '</td>';
132             } else {
133                 mergeRole += auth_strings.MASTER_RECORD + '</td>';
134             }
135
136             dojo.place('<tr class="toMerge" id="toMerge_' + auth.id + '"><td>' + mergeRole + '</td><td  style="border: 1px solid black;" id="mergeMeta_' + auth.id + '"></td><td style="border: 1px solid black; padding-left: 1em; padding-right: 1em;" >' + auth.text + '</td></tr>', 'mergebox-tbody', 'last');
137             dojo.place('<span class="authmeta" style="font-family: monospace;">' + auth.name + ' ' + auth.ind1 + auth.ind2 + '</span>', 'mergeMeta_' + auth.id, 'last');
138             dojo.removeClass('mergebox-div', 'hidden');
139         }, "label":auth_strings.MENU_MERGE}).placeAt(auth_menu, "last");
140
141         // "Delete" menu item
142         new dijit.MenuItem({
143             "id": "delete_" + auth.id,
144             "onClick":function(){
145                 auth.text = '';
146
147                 var auth_rec = pcrud.retrieve("are", auth.id);
148
149                 // Bit of a hack to get the linked bib count until an explicit ID
150                 var linkedBibs = dojox.xml.parser.textContent(
151                     dojo.query("#authLabel" + auth.id)[0].previousSibling
152                 );
153
154                 var delDlg = dijit.byId("delDialog_" + auth.id);
155
156                 dojo.query('#auth' + auth.id + ' span.text').forEach(function(node) {
157                     auth.text += dojo.trim(dojox.xml.parser.textContent(node)); 
158                 });
159
160                 if (!delDlg) {
161                     var content = '<div>' + dojo.string.substitute(auth_strings.CONFIRM_DELETE_TITLE, [auth.text]) + '</div>';
162                     if (parseInt(linkedBibs) > 0) {
163                         content = "<div id='delAuthSum_" + auth.id + "'>"
164                             + dojo.string.substitute(auth_strings.LINKED_BIBS, [linkedBibs])
165                             + "</div>";
166                     }
167                     content += "<div id='authMARC" + auth.id + "' style='width: 100%; display:none;'>";
168                     content += "<hr style='width: 100%;' />";
169                     content += marcToHTML(auth_rec.marc());
170                     content += "</div><hr style='width: 100%;' /><div>";
171                     content += "<input type='button' dojoType='dijit.form.Button' label='" + auth_strings.CANCEL + "' onClick='cancelDelete(" + auth.id + ")'/>";
172                     content += "<input type='button' dojoType='dijit.form.Button' label='" + auth_strings.DELETE + "' onClick='confirmDelete(" + auth.id + ")'/>";
173                     content += "<input id='viewMARC" + auth.id + "' type='button' "
174                         + "style='float:right;' dojoType='dijit.form.Button' "
175                         + "label='" + auth_strings.VIEW_MARC + "' onClick='viewMARC(" + auth.id + ")'/>";
176                     content += "<input id='hideMARC" + auth.id + "' type='button' "
177                         + "style='display: none; float:right;' dojoType='dijit.form.Button' "
178                         + "label='" + auth_strings.HIDE_MARC + "' onClick='hideMARC(" + auth.id + ")'/>";
179                     content += "</div>";
180                     delDlg = new dijit.Dialog({
181                         "id":"delDialog_" + auth.id,
182                         "title": dojo.string.substitute(auth_strings.CONFIRM_DELETE_PROMPT, [auth.id]),
183                         "content": content
184                     });
185                 }
186                 delDlg.show();
187
188         }, "label":auth_strings.DELETE}).placeAt(auth_menu, "last");
189
190         auth_mb = new dijit.form.DropDownButton({dropDown: auth_menu, label: auth_strings.ACTIONS, id:"menu" + auth.id});
191         auth_mb.placeAt(dojo.create("div", null, "auth" + auth.id, "first"), "first");
192         auth_menu.startup();
193     });
194
195     showBibCount(idArr);
196 }
197
198 function viewMARC(recId) {
199     dojo.style(dojo.byId("authMARC" + recId), 'display', 'block');
200     dojo.style(dijit.byId("viewMARC" + recId).domNode, 'display', 'none');
201     dojo.style(dijit.byId("hideMARC" + recId).domNode, 'display', 'block');
202 }
203
204 function hideMARC(recId) {
205     dojo.style(dojo.byId("authMARC" + recId), 'display', 'none');
206     dojo.style(dijit.byId("hideMARC" + recId).domNode, 'display', 'none');
207     dojo.style(dijit.byId("viewMARC" + recId).domNode, 'display', 'block');
208 }
209
210 function marcToHTML(marc) {
211     var html = '<table><tbody>';
212     marc = dojox.xml.parser.parse(marc);
213     dojo.query('leader', marc).forEach(function(node) {
214         html += '<tr><td>LDR</td><td>&nbsp;</td><td>&nbsp;</td><td>' + dojox.xml.parser.textContent(node) + '</td></tr>';
215     });
216     dojo.query('controlfield', marc).forEach(function(node) {
217         html += '<tr><td>' + dojo.attr(node, "tag") + '</td><td>&nbsp;</td><td>&nbsp;</td><td>' + dojox.xml.parser.textContent(node) + '</td></tr>';
218     });
219     dojo.query('datafield', marc).forEach(function(node) {
220         var cnt = 0;
221         html += '<tr><td>' + dojo.attr(node, "tag") + '</td><td>' + dojo.attr(node, "ind1") + '</td><td>' + dojo.attr(node, "ind2") + '</td>';
222         dojo.query('subfield', node).forEach(function(sf) {
223             if (cnt == 0) {
224                 html += '<td>$' + dojo.attr(sf, "code") + ' ' + dojox.xml.parser.textContent(sf) + '</td></tr>';
225                 cnt = 1;
226             } else {
227                 html += '<tr><td colspan="3"></td><td>$' + dojo.attr(sf, "code") + ' ' + dojox.xml.parser.textContent(sf) + '</td></tr>';
228             }
229         });
230     });
231     html += '</tbody></table>';
232     return html;
233 }
234
235 function cancelDelete(recId) {
236     dijit.byId("delDialog_" + recId).hide();
237 }
238
239 function confirmDelete(recId) {
240     var auth_rec = pcrud.retrieve("are", recId);
241     if (auth_rec) {
242         pcrud.eliminate(auth_rec);
243         dijit.byId("delDialog_" + recId).attr("content", dojo.string.substitute(auth_strings.CONFIRM_DELETE_RESULT, [recId]));
244         setTimeout(function() {
245             dijit.byId("delDialog_" + recId).hide();
246         }, 3000);
247     }
248 }
249
250 function showBibCount(authIds) {
251     /* Decorate the list with # of bibs linked to each authority record */
252     var ses = new OpenSRF.ClientSession('open-ils.cat');
253     var req = ses.request('open-ils.cat.authority.records.count_linked_bibs', authIds);
254     var linkedIds = [];
255     req.oncomplete = function(r) {
256         var msg = r.recv().content();
257         dojo.forEach(msg, function(auth) {
258                 linkedIds.push(auth.authority);
259                 dojo.place('<span class="bibcount">' + auth.bibs + '</span> ', 'authLabel' + auth.authority, 'first');
260             }
261         );
262
263         /* Assign counts of 0 for every non-linked authority */
264         dojo.forEach(authIds, function (id) {
265             var found = false;
266             dojo.forEach(linkedIds, function (lid) {
267                 if (id == lid) {
268                     found = true;
269                 }
270             });
271             if (!found) {
272                 dojo.place('<span class="bibcount">0</span> ', 'authLabel' + id, 'first');
273             }
274         });
275     }
276     req.send();
277 }
278
279 function loadMarcEditor(pcrud, rec) {
280     /*
281        To run in Firefox directly, must set signed.applets.codebase_principal_support
282        to true in about:config
283      */
284     win = window.open('/xul/server/cat/marcedit.xul'); // XXX version?
285
286     win.xulG = {
287         "record": {"marc": rec.marc(), "rtype": "are"},
288         "save": {
289             "label": auth_strings.SAVE,
290             "func": function(xmlString) {
291                 rec.marc(xmlString);
292                 rec.edit_date('now');
293                 rec.ischanged(true);
294                 pcrud.update(rec);
295                 alert(auth_strings.SAVE_RESULT_SUCCESS);
296                 win.close();
297             }
298         },
299         'lock_tab' : typeof xulG != 'undefined' ? (typeof xulG['lock_tab'] != 'undefined' ? xulG.lock_tab : undefined) : undefined,
300         'unlock_tab' : typeof xulG != 'undefined' ? (typeof xulG['unlock_tab'] != 'undefined' ? xulG.unlock_tab : undefined) : undefined
301     };
302 }
303
304 function authListInit() {
305     var term = cgi.param('authTerm') || '';
306     var page = cgi.param('authPage') || 0;
307     var axis = cgi.param('authAxis') || 'authority.author';
308     if (axis) {
309         dijit.byId('authAxis').attr('value', axis);
310     }
311     if (page) {
312         dijit.byId('authPage').attr('value', page);
313     }
314     if (term) {
315         dijit.byId('authTerm').attr('value', term);
316         displayRecords();
317     }
318
319     dojo.connect(dijit.byId('authAxis'), 'onKeyPress', function(evt) {
320         if (evt.keyCode == dojo.keys.ENTER) {
321             dijit.byId('authPage').attr('value', 0);
322             displayRecords();
323         }
324     }); 
325
326     dojo.connect(dijit.byId('authPage'), 'onKeyPress', function(evt) {
327         if (evt.keyCode == dojo.keys.ENTER) {
328             dijit.byId('authPage').attr('value', 0);
329             displayRecords();
330         }
331     });
332
333     dojo.connect(dijit.byId('authTerm'), 'onKeyPress', function(evt) {
334         if (evt.keyCode == dojo.keys.ENTER) {
335             dijit.byId('authPage').attr('value', 0);
336             displayRecords();
337         }
338     });
339
340     dijit.byId('authTerm').focus();
341
342 }
343 dojo.addOnLoad(authListInit);
344
345 function displayRecords(parms) {
346
347     if (parms && parms.page) {
348         if (parms.page == 'next') {
349             page = dijit.byId('authPage').attr('value');
350             dijit.byId('authPage').attr('value', page + 1);
351         } else if (parms.page == 'prev') {
352             page = dijit.byId('authPage').attr('value');
353             dijit.byId('authPage').attr('value', page - 1);
354         } else {
355             dijit.byId('authPage').attr('value', parms.page);
356         }
357     }
358
359     /* Protect against null input */
360     if (!dijit.byId('authTerm').attr('value')) {
361         return;
362     }
363
364     /* Clear out the current contents of the page */
365     var widgets = dijit.findWidgets(dojo.byId('authlist-div'));
366     dojo.forEach(widgets, function(w) { w.destroyRecursive(true); });
367
368     dojo.query("#authlist-div div").orphan();
369
370     var url = '/opac/extras/browse/marcxml/authority.'
371         + dijit.byId('authAxis').attr('value')
372         // + '/' + dijit.byId('authOU').attr('value')
373         + '/1' // replace with preceding line if OUs gain some meaning
374         + '/' + dijit.byId('authTerm').attr('value')
375         + '/' + dijit.byId('authPage').attr('value')
376         + '/' + '20' // 20 results per page
377     ;
378     dojo.xhrGet({"url":url, "handleAs":"xml", "content":{"format":"marcxml"}, "preventCache": true, "load":displayAuthorities });
379 }
380
381 function clearMergeRecords() {
382     var records = dojo.query('.toMerge').orphan();
383     dojo.addClass('mergebox-div', 'hidden');
384 }
385
386 function mergeRecords() {
387     var records = dojo.query('.toMerge').attr('id');
388     dojo.forEach(records, function(item, idx) {
389         records[idx] = parseInt(item.slice(item.lastIndexOf('_') + 1));
390     });
391
392     /* Take the first record in the list and use that as the master */
393     fieldmapper.standardRequest(
394         ['open-ils.cat', 'open-ils.cat.authority.records.merge'],
395         {   async: false,
396             params: [openils.User.authtoken, records.shift(), records],
397             oncomplete : function(r) {
398                 alert(auth_strings.MERGE_RESULT_SUCCESS);
399                 clearMergeRecords();
400                 displayRecords();
401             }
402         }
403     );
404 }